mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	usb: gadget: f_fs: OS descriptors support
Add support for OS descriptors. The new format of descriptors is used, because the "flags" field is required for extensions. os_count gives the number of OSDesc[] elements. The format of descriptors is given in include/uapi/linux/usb/functionfs.h. For extended properties descriptor the usb_ext_prop_desc structure covers only a part of a descriptor, because the wPropertyNameLength is unknown up front. Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@samsung.com> Acked-by: Michal Nazarewicz <mina86@mina86.com> Signed-off-by: Felipe Balbi <balbi@ti.com>
This commit is contained in:
		
							parent
							
								
									7ea4f088c8
								
							
						
					
					
						commit
						f0175ab519
					
				
					 3 changed files with 419 additions and 10 deletions
				
			
		| 
						 | 
					@ -34,6 +34,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "u_fs.h"
 | 
					#include "u_fs.h"
 | 
				
			||||||
#include "u_f.h"
 | 
					#include "u_f.h"
 | 
				
			||||||
 | 
					#include "u_os_desc.h"
 | 
				
			||||||
#include "configfs.h"
 | 
					#include "configfs.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define FUNCTIONFS_MAGIC	0xa647361 /* Chosen by a honest dice roll ;) */
 | 
					#define FUNCTIONFS_MAGIC	0xa647361 /* Chosen by a honest dice roll ;) */
 | 
				
			||||||
| 
						 | 
					@ -1644,11 +1645,19 @@ enum ffs_entity_type {
 | 
				
			||||||
	FFS_DESCRIPTOR, FFS_INTERFACE, FFS_STRING, FFS_ENDPOINT
 | 
						FFS_DESCRIPTOR, FFS_INTERFACE, FFS_STRING, FFS_ENDPOINT
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum ffs_os_desc_type {
 | 
				
			||||||
 | 
						FFS_OS_DESC, FFS_OS_DESC_EXT_COMPAT, FFS_OS_DESC_EXT_PROP
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef int (*ffs_entity_callback)(enum ffs_entity_type entity,
 | 
					typedef int (*ffs_entity_callback)(enum ffs_entity_type entity,
 | 
				
			||||||
				   u8 *valuep,
 | 
									   u8 *valuep,
 | 
				
			||||||
				   struct usb_descriptor_header *desc,
 | 
									   struct usb_descriptor_header *desc,
 | 
				
			||||||
				   void *priv);
 | 
									   void *priv);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef int (*ffs_os_desc_callback)(enum ffs_os_desc_type entity,
 | 
				
			||||||
 | 
									    struct usb_os_desc_header *h, void *data,
 | 
				
			||||||
 | 
									    unsigned len, void *priv);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int __must_check ffs_do_single_desc(char *data, unsigned len,
 | 
					static int __must_check ffs_do_single_desc(char *data, unsigned len,
 | 
				
			||||||
					   ffs_entity_callback entity,
 | 
										   ffs_entity_callback entity,
 | 
				
			||||||
					   void *priv)
 | 
										   void *priv)
 | 
				
			||||||
| 
						 | 
					@ -1856,11 +1865,191 @@ static int __ffs_data_do_entity(enum ffs_entity_type type,
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int __ffs_do_os_desc_header(enum ffs_os_desc_type *next_type,
 | 
				
			||||||
 | 
									   struct usb_os_desc_header *desc)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u16 bcd_version = le16_to_cpu(desc->bcdVersion);
 | 
				
			||||||
 | 
						u16 w_index = le16_to_cpu(desc->wIndex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (bcd_version != 1) {
 | 
				
			||||||
 | 
							pr_vdebug("unsupported os descriptors version: %d",
 | 
				
			||||||
 | 
								  bcd_version);
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						switch (w_index) {
 | 
				
			||||||
 | 
						case 0x4:
 | 
				
			||||||
 | 
							*next_type = FFS_OS_DESC_EXT_COMPAT;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case 0x5:
 | 
				
			||||||
 | 
							*next_type = FFS_OS_DESC_EXT_PROP;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							pr_vdebug("unsupported os descriptor type: %d", w_index);
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return sizeof(*desc);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Process all extended compatibility/extended property descriptors
 | 
				
			||||||
 | 
					 * of a feature descriptor
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int __must_check ffs_do_single_os_desc(char *data, unsigned len,
 | 
				
			||||||
 | 
										      enum ffs_os_desc_type type,
 | 
				
			||||||
 | 
										      u16 feature_count,
 | 
				
			||||||
 | 
										      ffs_os_desc_callback entity,
 | 
				
			||||||
 | 
										      void *priv,
 | 
				
			||||||
 | 
										      struct usb_os_desc_header *h)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
						const unsigned _len = len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ENTER();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* loop over all ext compat/ext prop descriptors */
 | 
				
			||||||
 | 
						while (feature_count--) {
 | 
				
			||||||
 | 
							ret = entity(type, h, data, len, priv);
 | 
				
			||||||
 | 
							if (unlikely(ret < 0)) {
 | 
				
			||||||
 | 
								pr_debug("bad OS descriptor, type: %d\n", type);
 | 
				
			||||||
 | 
								return ret;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							data += ret;
 | 
				
			||||||
 | 
							len -= ret;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return _len - len;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Process a number of complete Feature Descriptors (Ext Compat or Ext Prop) */
 | 
				
			||||||
 | 
					static int __must_check ffs_do_os_descs(unsigned count,
 | 
				
			||||||
 | 
										char *data, unsigned len,
 | 
				
			||||||
 | 
										ffs_os_desc_callback entity, void *priv)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const unsigned _len = len;
 | 
				
			||||||
 | 
						unsigned long num = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ENTER();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (num = 0; num < count; ++num) {
 | 
				
			||||||
 | 
							int ret;
 | 
				
			||||||
 | 
							enum ffs_os_desc_type type;
 | 
				
			||||||
 | 
							u16 feature_count;
 | 
				
			||||||
 | 
							struct usb_os_desc_header *desc = (void *)data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (len < sizeof(*desc))
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * Record "descriptor" entity.
 | 
				
			||||||
 | 
							 * Process dwLength, bcdVersion, wIndex, get b/wCount.
 | 
				
			||||||
 | 
							 * Move the data pointer to the beginning of extended
 | 
				
			||||||
 | 
							 * compatibilities proper or extended properties proper
 | 
				
			||||||
 | 
							 * portions of the data
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							if (le32_to_cpu(desc->dwLength) > len)
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ret = __ffs_do_os_desc_header(&type, desc);
 | 
				
			||||||
 | 
							if (unlikely(ret < 0)) {
 | 
				
			||||||
 | 
								pr_debug("entity OS_DESCRIPTOR(%02lx); ret = %d\n",
 | 
				
			||||||
 | 
									 num, ret);
 | 
				
			||||||
 | 
								return ret;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * 16-bit hex "?? 00" Little Endian looks like 8-bit hex "??"
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							feature_count = le16_to_cpu(desc->wCount);
 | 
				
			||||||
 | 
							if (type == FFS_OS_DESC_EXT_COMPAT &&
 | 
				
			||||||
 | 
							    (feature_count > 255 || desc->Reserved))
 | 
				
			||||||
 | 
									return -EINVAL;
 | 
				
			||||||
 | 
							len -= ret;
 | 
				
			||||||
 | 
							data += ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * Process all function/property descriptors
 | 
				
			||||||
 | 
							 * of this Feature Descriptor
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							ret = ffs_do_single_os_desc(data, len, type,
 | 
				
			||||||
 | 
										    feature_count, entity, priv, desc);
 | 
				
			||||||
 | 
							if (unlikely(ret < 0)) {
 | 
				
			||||||
 | 
								pr_debug("%s returns %d\n", __func__, ret);
 | 
				
			||||||
 | 
								return ret;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							len -= ret;
 | 
				
			||||||
 | 
							data += ret;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return _len - len;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Validate contents of the buffer from userspace related to OS descriptors.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int __ffs_data_do_os_desc(enum ffs_os_desc_type type,
 | 
				
			||||||
 | 
									 struct usb_os_desc_header *h, void *data,
 | 
				
			||||||
 | 
									 unsigned len, void *priv)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ffs_data *ffs = priv;
 | 
				
			||||||
 | 
						u8 length;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ENTER();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (type) {
 | 
				
			||||||
 | 
						case FFS_OS_DESC_EXT_COMPAT: {
 | 
				
			||||||
 | 
							struct usb_ext_compat_desc *d = data;
 | 
				
			||||||
 | 
							int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (len < sizeof(*d) ||
 | 
				
			||||||
 | 
							    d->bFirstInterfaceNumber >= ffs->interfaces_count ||
 | 
				
			||||||
 | 
							    d->Reserved1)
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
							for (i = 0; i < ARRAY_SIZE(d->Reserved2); ++i)
 | 
				
			||||||
 | 
								if (d->Reserved2[i])
 | 
				
			||||||
 | 
									return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							length = sizeof(struct usb_ext_compat_desc);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case FFS_OS_DESC_EXT_PROP: {
 | 
				
			||||||
 | 
							struct usb_ext_prop_desc *d = data;
 | 
				
			||||||
 | 
							u32 type, pdl;
 | 
				
			||||||
 | 
							u16 pnl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (len < sizeof(*d) || h->interface >= ffs->interfaces_count)
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
							length = le32_to_cpu(d->dwSize);
 | 
				
			||||||
 | 
							type = le32_to_cpu(d->dwPropertyDataType);
 | 
				
			||||||
 | 
							if (type < USB_EXT_PROP_UNICODE ||
 | 
				
			||||||
 | 
							    type > USB_EXT_PROP_UNICODE_MULTI) {
 | 
				
			||||||
 | 
								pr_vdebug("unsupported os descriptor property type: %d",
 | 
				
			||||||
 | 
									  type);
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							pnl = le16_to_cpu(d->wPropertyNameLength);
 | 
				
			||||||
 | 
							pdl = le32_to_cpu(*(u32 *)((u8 *)data + 10 + pnl));
 | 
				
			||||||
 | 
							if (length != 14 + pnl + pdl) {
 | 
				
			||||||
 | 
								pr_vdebug("invalid os descriptor length: %d pnl:%d pdl:%d (descriptor %d)\n",
 | 
				
			||||||
 | 
									  length, pnl, pdl, type);
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							++ffs->ms_os_descs_ext_prop_count;
 | 
				
			||||||
 | 
							/* property name reported to the host as "WCHAR"s */
 | 
				
			||||||
 | 
							ffs->ms_os_descs_ext_prop_name_len += pnl * 2;
 | 
				
			||||||
 | 
							ffs->ms_os_descs_ext_prop_data_len += pdl;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							pr_vdebug("unknown descriptor: %d\n", type);
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return length;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int __ffs_data_got_descs(struct ffs_data *ffs,
 | 
					static int __ffs_data_got_descs(struct ffs_data *ffs,
 | 
				
			||||||
				char *const _data, size_t len)
 | 
									char *const _data, size_t len)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	char *data = _data, *raw_descs;
 | 
						char *data = _data, *raw_descs;
 | 
				
			||||||
	unsigned counts[3], flags;
 | 
						unsigned os_descs_count = 0, counts[3], flags;
 | 
				
			||||||
	int ret = -EINVAL, i;
 | 
						int ret = -EINVAL, i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ENTER();
 | 
						ENTER();
 | 
				
			||||||
| 
						 | 
					@ -1878,7 +2067,8 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
 | 
				
			||||||
		flags = get_unaligned_le32(data + 8);
 | 
							flags = get_unaligned_le32(data + 8);
 | 
				
			||||||
		if (flags & ~(FUNCTIONFS_HAS_FS_DESC |
 | 
							if (flags & ~(FUNCTIONFS_HAS_FS_DESC |
 | 
				
			||||||
			      FUNCTIONFS_HAS_HS_DESC |
 | 
								      FUNCTIONFS_HAS_HS_DESC |
 | 
				
			||||||
			      FUNCTIONFS_HAS_SS_DESC)) {
 | 
								      FUNCTIONFS_HAS_SS_DESC |
 | 
				
			||||||
 | 
								      FUNCTIONFS_HAS_MS_OS_DESC)) {
 | 
				
			||||||
			ret = -ENOSYS;
 | 
								ret = -ENOSYS;
 | 
				
			||||||
			goto error;
 | 
								goto error;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -1901,6 +2091,11 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
 | 
				
			||||||
			len  -= 4;
 | 
								len  -= 4;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if (flags & (1 << i)) {
 | 
				
			||||||
 | 
							os_descs_count = get_unaligned_le32(data);
 | 
				
			||||||
 | 
							data += 4;
 | 
				
			||||||
 | 
							len -= 4;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Read descriptors */
 | 
						/* Read descriptors */
 | 
				
			||||||
	raw_descs = data;
 | 
						raw_descs = data;
 | 
				
			||||||
| 
						 | 
					@ -1914,6 +2109,14 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
 | 
				
			||||||
		data += ret;
 | 
							data += ret;
 | 
				
			||||||
		len  -= ret;
 | 
							len  -= ret;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if (os_descs_count) {
 | 
				
			||||||
 | 
							ret = ffs_do_os_descs(os_descs_count, data, len,
 | 
				
			||||||
 | 
									      __ffs_data_do_os_desc, ffs);
 | 
				
			||||||
 | 
							if (ret < 0)
 | 
				
			||||||
 | 
								goto error;
 | 
				
			||||||
 | 
							data += ret;
 | 
				
			||||||
 | 
							len -= ret;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (raw_descs == data || len) {
 | 
						if (raw_descs == data || len) {
 | 
				
			||||||
		ret = -EINVAL;
 | 
							ret = -EINVAL;
 | 
				
			||||||
| 
						 | 
					@ -1926,6 +2129,7 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
 | 
				
			||||||
	ffs->fs_descs_count	= counts[0];
 | 
						ffs->fs_descs_count	= counts[0];
 | 
				
			||||||
	ffs->hs_descs_count	= counts[1];
 | 
						ffs->hs_descs_count	= counts[1];
 | 
				
			||||||
	ffs->ss_descs_count	= counts[2];
 | 
						ffs->ss_descs_count	= counts[2];
 | 
				
			||||||
 | 
						ffs->ms_os_descs_count	= os_descs_count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2267,6 +2471,85 @@ static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep,
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int __ffs_func_bind_do_os_desc(enum ffs_os_desc_type type,
 | 
				
			||||||
 | 
									      struct usb_os_desc_header *h, void *data,
 | 
				
			||||||
 | 
									      unsigned len, void *priv)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ffs_function *func = priv;
 | 
				
			||||||
 | 
						u8 length = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (type) {
 | 
				
			||||||
 | 
						case FFS_OS_DESC_EXT_COMPAT: {
 | 
				
			||||||
 | 
							struct usb_ext_compat_desc *desc = data;
 | 
				
			||||||
 | 
							struct usb_os_desc_table *t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							t = &func->function.os_desc_table[desc->bFirstInterfaceNumber];
 | 
				
			||||||
 | 
							t->if_id = func->interfaces_nums[desc->bFirstInterfaceNumber];
 | 
				
			||||||
 | 
							memcpy(t->os_desc->ext_compat_id, &desc->CompatibleID,
 | 
				
			||||||
 | 
							       ARRAY_SIZE(desc->CompatibleID) +
 | 
				
			||||||
 | 
							       ARRAY_SIZE(desc->SubCompatibleID));
 | 
				
			||||||
 | 
							length = sizeof(*desc);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case FFS_OS_DESC_EXT_PROP: {
 | 
				
			||||||
 | 
							struct usb_ext_prop_desc *desc = data;
 | 
				
			||||||
 | 
							struct usb_os_desc_table *t;
 | 
				
			||||||
 | 
							struct usb_os_desc_ext_prop *ext_prop;
 | 
				
			||||||
 | 
							char *ext_prop_name;
 | 
				
			||||||
 | 
							char *ext_prop_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							t = &func->function.os_desc_table[h->interface];
 | 
				
			||||||
 | 
							t->if_id = func->interfaces_nums[h->interface];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ext_prop = func->ffs->ms_os_descs_ext_prop_avail;
 | 
				
			||||||
 | 
							func->ffs->ms_os_descs_ext_prop_avail += sizeof(*ext_prop);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ext_prop->type = le32_to_cpu(desc->dwPropertyDataType);
 | 
				
			||||||
 | 
							ext_prop->name_len = le16_to_cpu(desc->wPropertyNameLength);
 | 
				
			||||||
 | 
							ext_prop->data_len = le32_to_cpu(*(u32 *)
 | 
				
			||||||
 | 
								usb_ext_prop_data_len_ptr(data, ext_prop->name_len));
 | 
				
			||||||
 | 
							length = ext_prop->name_len + ext_prop->data_len + 14;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ext_prop_name = func->ffs->ms_os_descs_ext_prop_name_avail;
 | 
				
			||||||
 | 
							func->ffs->ms_os_descs_ext_prop_name_avail +=
 | 
				
			||||||
 | 
								ext_prop->name_len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ext_prop_data = func->ffs->ms_os_descs_ext_prop_data_avail;
 | 
				
			||||||
 | 
							func->ffs->ms_os_descs_ext_prop_data_avail +=
 | 
				
			||||||
 | 
								ext_prop->data_len;
 | 
				
			||||||
 | 
							memcpy(ext_prop_data,
 | 
				
			||||||
 | 
							       usb_ext_prop_data_ptr(data, ext_prop->name_len),
 | 
				
			||||||
 | 
							       ext_prop->data_len);
 | 
				
			||||||
 | 
							/* unicode data reported to the host as "WCHAR"s */
 | 
				
			||||||
 | 
							switch (ext_prop->type) {
 | 
				
			||||||
 | 
							case USB_EXT_PROP_UNICODE:
 | 
				
			||||||
 | 
							case USB_EXT_PROP_UNICODE_ENV:
 | 
				
			||||||
 | 
							case USB_EXT_PROP_UNICODE_LINK:
 | 
				
			||||||
 | 
							case USB_EXT_PROP_UNICODE_MULTI:
 | 
				
			||||||
 | 
								ext_prop->data_len *= 2;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ext_prop->data = ext_prop_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							memcpy(ext_prop_name, usb_ext_prop_name_ptr(data),
 | 
				
			||||||
 | 
							       ext_prop->name_len);
 | 
				
			||||||
 | 
							/* property name reported to the host as "WCHAR"s */
 | 
				
			||||||
 | 
							ext_prop->name_len *= 2;
 | 
				
			||||||
 | 
							ext_prop->name = ext_prop_name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							t->os_desc->ext_prop_len +=
 | 
				
			||||||
 | 
								ext_prop->name_len + ext_prop->data_len + 14;
 | 
				
			||||||
 | 
							++t->os_desc->ext_prop_count;
 | 
				
			||||||
 | 
							list_add_tail(&ext_prop->entry, &t->os_desc->ext_prop);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							pr_vdebug("unknown descriptor: %d\n", type);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return length;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f,
 | 
					static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f,
 | 
				
			||||||
						struct usb_configuration *c)
 | 
											struct usb_configuration *c)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -2328,7 +2611,7 @@ static int _ffs_func_bind(struct usb_configuration *c,
 | 
				
			||||||
	const int super = gadget_is_superspeed(func->gadget) &&
 | 
						const int super = gadget_is_superspeed(func->gadget) &&
 | 
				
			||||||
		func->ffs->ss_descs_count;
 | 
							func->ffs->ss_descs_count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int fs_len, hs_len, ret;
 | 
						int fs_len, hs_len, ss_len, ret, i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Make it a single chunk, less management later on */
 | 
						/* Make it a single chunk, less management later on */
 | 
				
			||||||
	vla_group(d);
 | 
						vla_group(d);
 | 
				
			||||||
| 
						 | 
					@ -2340,6 +2623,18 @@ static int _ffs_func_bind(struct usb_configuration *c,
 | 
				
			||||||
	vla_item_with_sz(d, struct usb_descriptor_header *, ss_descs,
 | 
						vla_item_with_sz(d, struct usb_descriptor_header *, ss_descs,
 | 
				
			||||||
		super ? ffs->ss_descs_count + 1 : 0);
 | 
							super ? ffs->ss_descs_count + 1 : 0);
 | 
				
			||||||
	vla_item_with_sz(d, short, inums, ffs->interfaces_count);
 | 
						vla_item_with_sz(d, short, inums, ffs->interfaces_count);
 | 
				
			||||||
 | 
						vla_item_with_sz(d, struct usb_os_desc_table, os_desc_table,
 | 
				
			||||||
 | 
								 c->cdev->use_os_string ? ffs->interfaces_count : 0);
 | 
				
			||||||
 | 
						vla_item_with_sz(d, char[16], ext_compat,
 | 
				
			||||||
 | 
								 c->cdev->use_os_string ? ffs->interfaces_count : 0);
 | 
				
			||||||
 | 
						vla_item_with_sz(d, struct usb_os_desc, os_desc,
 | 
				
			||||||
 | 
								 c->cdev->use_os_string ? ffs->interfaces_count : 0);
 | 
				
			||||||
 | 
						vla_item_with_sz(d, struct usb_os_desc_ext_prop, ext_prop,
 | 
				
			||||||
 | 
								 ffs->ms_os_descs_ext_prop_count);
 | 
				
			||||||
 | 
						vla_item_with_sz(d, char, ext_prop_name,
 | 
				
			||||||
 | 
								 ffs->ms_os_descs_ext_prop_name_len);
 | 
				
			||||||
 | 
						vla_item_with_sz(d, char, ext_prop_data,
 | 
				
			||||||
 | 
								 ffs->ms_os_descs_ext_prop_data_len);
 | 
				
			||||||
	vla_item_with_sz(d, char, raw_descs, ffs->raw_descs_length);
 | 
						vla_item_with_sz(d, char, raw_descs, ffs->raw_descs_length);
 | 
				
			||||||
	char *vlabuf;
 | 
						char *vlabuf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2350,12 +2645,16 @@ static int _ffs_func_bind(struct usb_configuration *c,
 | 
				
			||||||
		return -ENOTSUPP;
 | 
							return -ENOTSUPP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Allocate a single chunk, less management later on */
 | 
						/* Allocate a single chunk, less management later on */
 | 
				
			||||||
	vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL);
 | 
						vlabuf = kzalloc(vla_group_size(d), GFP_KERNEL);
 | 
				
			||||||
	if (unlikely(!vlabuf))
 | 
						if (unlikely(!vlabuf))
 | 
				
			||||||
		return -ENOMEM;
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Zero */
 | 
						ffs->ms_os_descs_ext_prop_avail = vla_ptr(vlabuf, d, ext_prop);
 | 
				
			||||||
	memset(vla_ptr(vlabuf, d, eps), 0, d_eps__sz);
 | 
						ffs->ms_os_descs_ext_prop_name_avail =
 | 
				
			||||||
 | 
							vla_ptr(vlabuf, d, ext_prop_name);
 | 
				
			||||||
 | 
						ffs->ms_os_descs_ext_prop_data_avail =
 | 
				
			||||||
 | 
							vla_ptr(vlabuf, d, ext_prop_data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Copy descriptors  */
 | 
						/* Copy descriptors  */
 | 
				
			||||||
	memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs,
 | 
						memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs,
 | 
				
			||||||
	       ffs->raw_descs_length);
 | 
						       ffs->raw_descs_length);
 | 
				
			||||||
| 
						 | 
					@ -2409,12 +2708,16 @@ static int _ffs_func_bind(struct usb_configuration *c,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (likely(super)) {
 | 
						if (likely(super)) {
 | 
				
			||||||
		func->function.ss_descriptors = vla_ptr(vlabuf, d, ss_descs);
 | 
							func->function.ss_descriptors = vla_ptr(vlabuf, d, ss_descs);
 | 
				
			||||||
		ret = ffs_do_descs(ffs->ss_descs_count,
 | 
							ss_len = ffs_do_descs(ffs->ss_descs_count,
 | 
				
			||||||
				vla_ptr(vlabuf, d, raw_descs) + fs_len + hs_len,
 | 
									vla_ptr(vlabuf, d, raw_descs) + fs_len + hs_len,
 | 
				
			||||||
				d_raw_descs__sz - fs_len - hs_len,
 | 
									d_raw_descs__sz - fs_len - hs_len,
 | 
				
			||||||
				__ffs_func_bind_do_descs, func);
 | 
									__ffs_func_bind_do_descs, func);
 | 
				
			||||||
		if (unlikely(ret < 0))
 | 
							if (unlikely(ss_len < 0)) {
 | 
				
			||||||
 | 
								ret = ss_len;
 | 
				
			||||||
			goto error;
 | 
								goto error;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							ss_len = 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
| 
						 | 
					@ -2430,6 +2733,28 @@ static int _ffs_func_bind(struct usb_configuration *c,
 | 
				
			||||||
	if (unlikely(ret < 0))
 | 
						if (unlikely(ret < 0))
 | 
				
			||||||
		goto error;
 | 
							goto error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						func->function.os_desc_table = vla_ptr(vlabuf, d, os_desc_table);
 | 
				
			||||||
 | 
						if (c->cdev->use_os_string)
 | 
				
			||||||
 | 
							for (i = 0; i < ffs->interfaces_count; ++i) {
 | 
				
			||||||
 | 
								struct usb_os_desc *desc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								desc = func->function.os_desc_table[i].os_desc =
 | 
				
			||||||
 | 
									vla_ptr(vlabuf, d, os_desc) +
 | 
				
			||||||
 | 
									i * sizeof(struct usb_os_desc);
 | 
				
			||||||
 | 
								desc->ext_compat_id =
 | 
				
			||||||
 | 
									vla_ptr(vlabuf, d, ext_compat) + i * 16;
 | 
				
			||||||
 | 
								INIT_LIST_HEAD(&desc->ext_prop);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						ret = ffs_do_os_descs(ffs->ms_os_descs_count,
 | 
				
			||||||
 | 
								      vla_ptr(vlabuf, d, raw_descs) +
 | 
				
			||||||
 | 
								      fs_len + hs_len + ss_len,
 | 
				
			||||||
 | 
								      d_raw_descs__sz - fs_len - hs_len - ss_len,
 | 
				
			||||||
 | 
								      __ffs_func_bind_do_os_desc, func);
 | 
				
			||||||
 | 
						if (unlikely(ret < 0))
 | 
				
			||||||
 | 
							goto error;
 | 
				
			||||||
 | 
						func->function.os_desc_n =
 | 
				
			||||||
 | 
							c->cdev->use_os_string ? ffs->interfaces_count : 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* And we're done */
 | 
						/* And we're done */
 | 
				
			||||||
	ffs_event_add(ffs, FUNCTIONFS_BIND);
 | 
						ffs_event_add(ffs, FUNCTIONFS_BIND);
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -216,6 +216,13 @@ struct ffs_data {
 | 
				
			||||||
	unsigned			fs_descs_count;
 | 
						unsigned			fs_descs_count;
 | 
				
			||||||
	unsigned			hs_descs_count;
 | 
						unsigned			hs_descs_count;
 | 
				
			||||||
	unsigned			ss_descs_count;
 | 
						unsigned			ss_descs_count;
 | 
				
			||||||
 | 
						unsigned			ms_os_descs_count;
 | 
				
			||||||
 | 
						unsigned			ms_os_descs_ext_prop_count;
 | 
				
			||||||
 | 
						unsigned			ms_os_descs_ext_prop_name_len;
 | 
				
			||||||
 | 
						unsigned			ms_os_descs_ext_prop_data_len;
 | 
				
			||||||
 | 
						void				*ms_os_descs_ext_prop_avail;
 | 
				
			||||||
 | 
						void				*ms_os_descs_ext_prop_name_avail;
 | 
				
			||||||
 | 
						void				*ms_os_descs_ext_prop_data_avail;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	unsigned short			strings_count;
 | 
						unsigned short			strings_count;
 | 
				
			||||||
	unsigned short			interfaces_count;
 | 
						unsigned short			interfaces_count;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,10 +18,9 @@ enum functionfs_flags {
 | 
				
			||||||
	FUNCTIONFS_HAS_FS_DESC = 1,
 | 
						FUNCTIONFS_HAS_FS_DESC = 1,
 | 
				
			||||||
	FUNCTIONFS_HAS_HS_DESC = 2,
 | 
						FUNCTIONFS_HAS_HS_DESC = 2,
 | 
				
			||||||
	FUNCTIONFS_HAS_SS_DESC = 4,
 | 
						FUNCTIONFS_HAS_SS_DESC = 4,
 | 
				
			||||||
 | 
						FUNCTIONFS_HAS_MS_OS_DESC = 8,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef __KERNEL__
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Descriptor of an non-audio endpoint */
 | 
					/* Descriptor of an non-audio endpoint */
 | 
				
			||||||
struct usb_endpoint_descriptor_no_audio {
 | 
					struct usb_endpoint_descriptor_no_audio {
 | 
				
			||||||
	__u8  bLength;
 | 
						__u8  bLength;
 | 
				
			||||||
| 
						 | 
					@ -33,6 +32,36 @@ struct usb_endpoint_descriptor_no_audio {
 | 
				
			||||||
	__u8  bInterval;
 | 
						__u8  bInterval;
 | 
				
			||||||
} __attribute__((packed));
 | 
					} __attribute__((packed));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* MS OS Descriptor header */
 | 
				
			||||||
 | 
					struct usb_os_desc_header {
 | 
				
			||||||
 | 
						__u8	interface;
 | 
				
			||||||
 | 
						__le32	dwLength;
 | 
				
			||||||
 | 
						__le16	bcdVersion;
 | 
				
			||||||
 | 
						__le16	wIndex;
 | 
				
			||||||
 | 
						union {
 | 
				
			||||||
 | 
							struct {
 | 
				
			||||||
 | 
								__u8	bCount;
 | 
				
			||||||
 | 
								__u8	Reserved;
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
							__le16	wCount;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					} __attribute__((packed));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct usb_ext_compat_desc {
 | 
				
			||||||
 | 
						__u8	bFirstInterfaceNumber;
 | 
				
			||||||
 | 
						__u8	Reserved1;
 | 
				
			||||||
 | 
						__u8	CompatibleID[8];
 | 
				
			||||||
 | 
						__u8	SubCompatibleID[8];
 | 
				
			||||||
 | 
						__u8	Reserved2[6];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct usb_ext_prop_desc {
 | 
				
			||||||
 | 
						__le32	dwSize;
 | 
				
			||||||
 | 
						__le32	dwPropertyDataType;
 | 
				
			||||||
 | 
						__le16	wPropertyNameLength;
 | 
				
			||||||
 | 
					} __attribute__((packed));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef __KERNEL__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Descriptors format:
 | 
					 * Descriptors format:
 | 
				
			||||||
| 
						 | 
					@ -45,9 +74,11 @@ struct usb_endpoint_descriptor_no_audio {
 | 
				
			||||||
 * |     | fs_count  | LE32         | number of full-speed descriptors     |
 | 
					 * |     | fs_count  | LE32         | number of full-speed descriptors     |
 | 
				
			||||||
 * |     | hs_count  | LE32         | number of high-speed descriptors     |
 | 
					 * |     | hs_count  | LE32         | number of high-speed descriptors     |
 | 
				
			||||||
 * |     | ss_count  | LE32         | number of super-speed descriptors    |
 | 
					 * |     | ss_count  | LE32         | number of super-speed descriptors    |
 | 
				
			||||||
 | 
					 * |     | os_count  | LE32         | number of MS OS descriptors          |
 | 
				
			||||||
 * |     | fs_descrs | Descriptor[] | list of full-speed descriptors       |
 | 
					 * |     | fs_descrs | Descriptor[] | list of full-speed descriptors       |
 | 
				
			||||||
 * |     | hs_descrs | Descriptor[] | list of high-speed descriptors       |
 | 
					 * |     | hs_descrs | Descriptor[] | list of high-speed descriptors       |
 | 
				
			||||||
 * |     | ss_descrs | Descriptor[] | list of super-speed descriptors      |
 | 
					 * |     | ss_descrs | Descriptor[] | list of super-speed descriptors      |
 | 
				
			||||||
 | 
					 * |     | os_descrs | OSDesc[]     | list of MS OS descriptors            |
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Depending on which flags are set, various fields may be missing in the
 | 
					 * Depending on which flags are set, various fields may be missing in the
 | 
				
			||||||
 * structure.  Any flags that are not recognised cause the whole block to be
 | 
					 * structure.  Any flags that are not recognised cause the whole block to be
 | 
				
			||||||
| 
						 | 
					@ -74,6 +105,52 @@ struct usb_endpoint_descriptor_no_audio {
 | 
				
			||||||
 * |   0 | bLength         | U8   | length of the descriptor |
 | 
					 * |   0 | bLength         | U8   | length of the descriptor |
 | 
				
			||||||
 * |   1 | bDescriptorType | U8   | descriptor type          |
 | 
					 * |   1 | bDescriptorType | U8   | descriptor type          |
 | 
				
			||||||
 * |   2 | payload         |      | descriptor's payload     |
 | 
					 * |   2 | payload         |      | descriptor's payload     |
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * OSDesc[] is an array of valid MS OS Feature Descriptors which have one of
 | 
				
			||||||
 | 
					 * the following formats:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * | off | name            | type | description              |
 | 
				
			||||||
 | 
					 * |-----+-----------------+------+--------------------------|
 | 
				
			||||||
 | 
					 * |   0 | inteface        | U8   | related interface number |
 | 
				
			||||||
 | 
					 * |   1 | dwLength        | U32  | length of the descriptor |
 | 
				
			||||||
 | 
					 * |   5 | bcdVersion      | U16  | currently supported: 1   |
 | 
				
			||||||
 | 
					 * |   7 | wIndex          | U16  | currently supported: 4   |
 | 
				
			||||||
 | 
					 * |   9 | bCount          | U8   | number of ext. compat.   |
 | 
				
			||||||
 | 
					 * |  10 | Reserved        | U8   | 0                        |
 | 
				
			||||||
 | 
					 * |  11 | ExtCompat[]     |      | list of ext. compat. d.  |
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * | off | name            | type | description              |
 | 
				
			||||||
 | 
					 * |-----+-----------------+------+--------------------------|
 | 
				
			||||||
 | 
					 * |   0 | inteface        | U8   | related interface number |
 | 
				
			||||||
 | 
					 * |   1 | dwLength        | U32  | length of the descriptor |
 | 
				
			||||||
 | 
					 * |   5 | bcdVersion      | U16  | currently supported: 1   |
 | 
				
			||||||
 | 
					 * |   7 | wIndex          | U16  | currently supported: 5   |
 | 
				
			||||||
 | 
					 * |   9 | wCount          | U16  | number of ext. compat.   |
 | 
				
			||||||
 | 
					 * |  11 | ExtProp[]       |      | list of ext. prop. d.    |
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * ExtCompat[] is an array of valid Extended Compatiblity descriptors
 | 
				
			||||||
 | 
					 * which have the following format:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * | off | name                  | type | description                         |
 | 
				
			||||||
 | 
					 * |-----+-----------------------+------+-------------------------------------|
 | 
				
			||||||
 | 
					 * |   0 | bFirstInterfaceNumber | U8   | index of the interface or of the 1st|
 | 
				
			||||||
 | 
					 * |     |                       |      | interface in an IAD group           |
 | 
				
			||||||
 | 
					 * |   1 | Reserved              | U8   | 0                                   |
 | 
				
			||||||
 | 
					 * |   2 | CompatibleID          | U8[8]| compatible ID string                |
 | 
				
			||||||
 | 
					 * |  10 | SubCompatibleID       | U8[8]| subcompatible ID string             |
 | 
				
			||||||
 | 
					 * |  18 | Reserved              | U8[6]| 0                                   |
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * ExtProp[] is an array of valid Extended Properties descriptors
 | 
				
			||||||
 | 
					 * which have the following format:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * | off | name                  | type | description                         |
 | 
				
			||||||
 | 
					 * |-----+-----------------------+------+-------------------------------------|
 | 
				
			||||||
 | 
					 * |   0 | dwSize                | U32  | length of the descriptor            |
 | 
				
			||||||
 | 
					 * |   4 | dwPropertyDataType    | U32  | 1..7                                |
 | 
				
			||||||
 | 
					 * |   8 | wPropertyNameLength   | U16  | bPropertyName length (NL)           |
 | 
				
			||||||
 | 
					 * |  10 | bPropertyName         |U8[NL]| name of this property               |
 | 
				
			||||||
 | 
					 * |10+NL| dwPropertyDataLength  | U32  | bPropertyData length (DL)           |
 | 
				
			||||||
 | 
					 * |14+NL| bProperty             |U8[DL]| payload of this property            |
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct usb_functionfs_strings_head {
 | 
					struct usb_functionfs_strings_head {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue