Re: [PATCHv3 3/3] usb: gadget: f_fs: OS descriptors support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Jul 08 2014, Andrzej Pietrasiewicz <andrzej.p@xxxxxxxxxxx> wrote:
> 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@xxxxxxxxxxx>

Acked-by: Michal Nazarewicz <mina86@xxxxxxxxxx>

Just a few minor comments.

> @@ -1856,11 +1865,189 @@ static int __ffs_data_do_entity(enum ffs_entity_type type,
>  	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
> +		 */

So dwLength is never accessed at all.  It probably should?  At least:

		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 (d->bFirstInterfaceNumber >= ffs->interfaces_count ||
> +		    d->Reserved1)
> +			return -EINVAL;
> +		for (i = 0; i < ARRAY_SIZE(d->Reserved2); ++i)
> +			if (d->Reserved2[i])
> +				return -EINVAL;
> +
> +		if (len < sizeof(*d))
> +			return -EINVAL;

This check should be at the beginning of the case.

> +		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,
>  				char *const _data, size_t len)
>  {
>  	char *data = _data, *raw_descs;
> -	unsigned counts[3], flags;
> +	unsigned os_descs_count = 0, counts[3], flags;
>  	int ret = -EINVAL, i;
>  
>  	ENTER();


> @@ -2267,6 +2469,86 @@ static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep,
>  	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];
> +		/* Copy CompatibleID and SubCompatibleID in one go, hence 16. */

So now there's no “16” in the code, so the comment is confusing. ;)

> +		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,
>  						struct usb_configuration *c)
>  {

-- 
Best regards,                                         _     _
.o. | Liege of Serenely Enlightened Majesty of      o' \,=./ `o
..o | Computer Science,  Michał “mina86” Nazarewicz    (o o)
ooo +--<mpn@xxxxxxxxxx>--<xmpp:mina86@xxxxxxxxxx>--ooO--(_)--Ooo--
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux