On Wed, Jul 02 2014, Andrzej Pietrasiewicz <andrze...@samsung.com> 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 <andrze...@samsung.com>
> ---
> Rebased onto Felipe's testing/next with gadget directory cleanup patches
> applied:
>
> http://www.spinics.net/lists/linux-usb/msg109928.html
>
> This is meant for 3.17.
>
> @Michal: I kindly ask for your review. Your comments will be very
> valuable.

For now I've only skimmed over binding code.

>
>  drivers/usb/gadget/function/f_fs.c  | 345 
> +++++++++++++++++++++++++++++++++++-
>  drivers/usb/gadget/function/u_fs.h  |   7 +
>  include/uapi/linux/usb/functionfs.h |  87 ++++++++-
>  3 files changed, 432 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/usb/gadget/function/f_fs.c 
> b/drivers/usb/gadget/function/f_fs.c
> index 88d6fa2..55c0db7 100644
> --- a/drivers/usb/gadget/function/f_fs.c
> +++ b/drivers/usb/gadget/function/f_fs.c
> @@ -34,6 +34,7 @@
>
>  #include "u_fs.h"
>  #include "u_f.h"
> +#include "u_os_desc.h"
>  #include "configfs.h"
>
>  #define FUNCTIONFS_MAGIC     0xa647361 /* Chosen by a honest dice roll ;) */
> @@ -1644,11 +1645,18 @@ enum ffs_entity_type {
>       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,
>                                  u8 *valuep,
>                                  struct usb_descriptor_header *desc,
>                                  void *priv);
>
> +typedef int (*ffs_os_desc_callback)(enum ffs_os_desc_type entity,
> +                                 u8 *valuep, void *data, void *priv);
> +
>  static int __must_check ffs_do_desc(char *data, unsigned len,
>                                   ffs_entity_callback entity, void *priv)
>  {
> @@ -1855,11 +1863,190 @@ static int __ffs_data_do_entity(enum ffs_entity_type 
> type,
>       return 0;
>  }
>
> +/*
> + * Process all extended compatibility/extended property descriptors
> + * of a feature descriptor
> + */
> +static int __must_check ffs_do_os_desc(char *data, unsigned len, u8 *valuep,
> +                                    u16 feature_count,
> +                                    ffs_os_desc_callback entity, void *priv,
> +                                    struct usb_os_desc_header *desc)

The name of this function has to be changed.  Perhaps
ffs_do_os_single_desc?  It took me embarrassing amount of time to figure
out that ffs_do_os_descs and ffs_do_os_desc are different functions.

> +{
> +     int ret;
> +     enum ffs_os_desc_type *type = (enum ffs_os_desc_type *)valuep;

Read the value already:

        enum ffs_os_desc_type type = *(enum ffs_os_desc_type *)valuep;

> +     const unsigned _len = len;
> +
> +     ENTER();
> +
> +     /* loop over all ext compat/ext prop descriptors */
> +     while (feature_count--) {
> +             ret = entity(*type, (u8 *)desc, (void *)data, priv);

You don't have to cast to void *.  It's implicit.

> +             if (unlikely(ret < 0)) {
> +                     pr_debug("bad OS descriptor, type: %d\n", *valuep);
> +                     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 (;;) {
> +             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;

With the union addition that I've mentioned near the end of the email,
this would also handle checking if bCount/wCount is there.

> +
> +             if (num == count)
> +                     return _len - len;

        for (num = 0; num < count; ++num) {
                …;
        }
        return _len - len;

> +
> +             /* 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
> +              */
> +             ret = entity(FFS_OS_DESC, (u8 *)&type, desc, priv);

This casting looks ugly.  Just change type of the second argument of
entity callback to void*.  You are casting it back and forth anyway.

> +             if (unlikely(ret < 0)) {
> +                     pr_debug("entity OS_DESCRIPTOR(%02lx); ret = %d\n",
> +                              num, ret);
> +                     return ret;
> +             }
> +             if (type == FFS_OS_DESC_EXT_COMPAT) {
> +                     struct usb_ext_compat_desc_header *h =
> +                             (struct usb_ext_compat_desc_header *)data;

Add checking if h->Reserved is zero, reject if not.

> +                     feature_count = h->bCount;
> +             } else if (type == FFS_OS_DESC_EXT_PROP) {
> +                     struct usb_ext_prop_desc_header *h = (void *)data;
> +
> +                     feature_count = le16_to_cpu(h->wCount);
> +             } else {
> +                     return -EINVAL;
> +             }

Perhaps you could take advantage of the fact that in little endian
a 16-bit “?? 00” is the same as 8-bit “??”.  Something like:

        feature_count = le16_to_cpu(d->wCount);
        if (type == FFS_OS_DESC_EXT_COMPAT && feature_count > 255)
                return -EINVAL;

> +             if (type == FFS_OS_DESC_EXT_COMPAT) {
> +                     struct usb_ext_compat_desc_header *h =
> +                             (struct usb_ext_compat_desc_header *)data;

Add checking if h->Reserved is zero, reject if not.

> +                     feature_count = h->bCount;
> +             } else if (type == FFS_OS_DESC_EXT_PROP) {
> +                     struct usb_ext_prop_desc_header *h = (void *)data;
> +
> +                     feature_count = le16_to_cpu(h->wCount);
> +             } else {
> +                     return -EINVAL;
> +             }
> +             len -= (ret + 2);
> +             data += (ret + 2);
> +
> +             /*
> +              * Process all function/property descriptors
> +              * of this Feature Descriptor
> +              */
> +             ret = ffs_do_os_desc(data, len, (u8 *)&type, feature_count,
> +                                  entity, priv, desc);
> +             if (unlikely(ret < 0)) {
> +                     pr_debug("%s returns %d\n", __func__, ret);
> +                     return ret;
> +             }
> +
> +             len -= ret;
> +             data += ret;
> +             ++num;
> +     }
> +}
> +
> +/**
> + * Validate contents of the buffer from userspace related to OS descriptors.
> + */
> +static int __ffs_data_do_os_desc(enum ffs_os_desc_type type,
> +                              u8 *valuep, void *data, void *priv)

As commented above, make it “void *valuep”.

> +{
> +     struct ffs_data *ffs = priv;
> +     __u8 length;
> +
> +     ENTER();
> +
> +     switch (type) {
> +     case FFS_OS_DESC: {
> +             struct usb_os_desc_header *desc = data;
> +             enum ffs_os_desc_type *next_type =
> +                     (enum ffs_os_desc_type *)valuep;
> +             __u16 bcd_version = le16_to_cpu(desc->bcdVersion);
> +             __u16 w_index = le16_to_cpu(desc->wIndex);
> +
> +             if (bcd_version != 0x1) {

Is it just me, or does 0x1 look kinda weird?

> +                     pr_vdebug("unsupported os descriptors version: %d",
> +                               bcd_version);
> +                     return -EINVAL;
> +             }
> +             if (w_index != 0x4 && w_index != 0x5) {
> +                     pr_vdebug("unsupported os descriptor type: %d",
> +                               w_index);
> +                     return -EINVAL;

Stash this as a “default” in the switch below.

> +             }
> +             switch (w_index) {
> +             case 0x4:
> +                     *next_type = FFS_OS_DESC_EXT_COMPAT;
> +                     break;
> +             case 0x5:
> +                     *next_type = FFS_OS_DESC_EXT_PROP;
> +                     break;
> +             }
> +             length = sizeof(*desc);
> +     }
> +             break;
> +     case FFS_OS_DESC_EXT_COMPAT: {
> +             struct usb_ext_compat_desc *d = data;

        if (len < sizeof *d)
                return -EINVAL;

> +
> +             if (d->bFirstInterfaceNumber >= ffs->interfaces_count)
> +                     return -EINVAL;
> +

Also add checking whether d->Reserved1 and d->Reserved2[] is zero,
reject if not.

> +             length = sizeof(struct usb_ext_compat_desc);
> +     }
> +             break;
> +     case FFS_OS_DESC_EXT_PROP: {
> +             struct usb_os_desc_header *desc =
> +                     (struct usb_os_desc_header *)valuep;
> +             struct usb_ext_prop_desc *d = data;
> +             __le32 type, pdl;
> +             __le16 pnl;

Why __le32 and __le16?  You want u32 and u16 here.

        if (len < sizeof *d)
                return -EINVAL;

> +
> +             if (desc->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 *)(data + 10 + pnl));
> +             if (length != 14 + pnl + pdl) {
> +#define FMT "invalid os descriptor length: %d pnl:%d pdl:%d (descriptor 
> %d)\n"
> +                     pr_vdebug(FMT, length, pnl, pdl, type);
> +#undef FMT

Uh?  Why the #define?

> +                     return -EINVAL;
> +             }
> +             ++ffs->ms_os_descs_ext_prop_count;
> +             ffs->ms_os_descs_ext_prop_name_len += (pnl << 1);

Why is pnl multiplied by two?

> +             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();
> @@ -1877,7 +2064,8 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
>               flags = get_unaligned_le32(data + 8);
>               if (flags & ~(FUNCTIONFS_HAS_FS_DESC |
>                             FUNCTIONFS_HAS_HS_DESC |
> -                           FUNCTIONFS_HAS_SS_DESC)) {
> +                           FUNCTIONFS_HAS_SS_DESC |
> +                           FUNCTIONFS_HAS_MS_OS_DESC)) {
>                       ret = -ENOSYS;
>                       goto error;
>               }
> @@ -1900,6 +2088,11 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
>                       len  -= 4;
>               }
>       }
> +     if (flags & (1 << i)) {
> +             os_descs_count = get_unaligned_le32(data);
> +             data += 4;
> +             len -= 4;
> +     };
>
>       /* Read descriptors */
>       raw_descs = data;
> @@ -1913,6 +2106,14 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
>               data += 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) {
>               ret = -EINVAL;
> @@ -1925,6 +2126,7 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
>       ffs->fs_descs_count     = counts[0];
>       ffs->hs_descs_count     = counts[1];
>       ffs->ss_descs_count     = counts[2];
> +     ffs->ms_os_descs_count  = os_descs_count;
>
>       return 0;
>
> @@ -2091,6 +2293,7 @@ static void __ffs_event_add(struct ffs_data *ffs,
>               rem_type2 = FUNCTIONFS_SUSPEND;
>               /* FALL THROUGH */
>       case FUNCTIONFS_SUSPEND:
> +

Unrelated and unnecessary.

>       case FUNCTIONFS_SETUP:
>               rem_type1 = type;
>               /* Discard all similar events */

> diff --git a/include/uapi/linux/usb/functionfs.h 
> b/include/uapi/linux/usb/functionfs.h
> index 2a4b4a7..4ec3798 100644
> --- a/include/uapi/linux/usb/functionfs.h
> +++ b/include/uapi/linux/usb/functionfs.h
> @@ -33,6 +32,42 @@ struct usb_endpoint_descriptor_no_audio {
>       __u8  bInterval;
>  } __attribute__((packed));
>
> +/* MS OS Descriptor header */
> +struct usb_os_desc_header {
> +     __u8    interface;
> +     __u32   dwLength;
> +     __u16   bcdVersion;
> +     __u16   wIndex;

Is that __u32 or __le32?  And same with 16?  You are accessing the data
with le*_to_cpu so I assume this should read __le32 and __le16.

Instead of having separate structures, how about adding this:

        union {
                struct {
                        __u8 bCount;
                        __u8 Reserved;
                } __attribute__((packed));
                __le16 wCount;
        };

Feel free to name the union and struct if you are so inclined.

> +} __attribute__((packed));
> +
> +/* MS OS Extended Compatibility Descriptor header */
> +struct usb_ext_compat_desc_header {
> +     struct  usb_os_desc_header header;
> +     __u8    bCount;
> +     __u8    Reserved;
> +} __attribute__((packed));
> +
> +struct usb_ext_compat_desc {
> +     __u8    bFirstInterfaceNumber;
> +     __u8    Reserved1;
> +     __u8    CompatibleID[8];
> +     __u8    SubCompatibleID[8];
> +     __u8    Reserved2[6];
> +};
> +
> +/* MS OS Extended Properties Descriptor header */
> +struct usb_ext_prop_desc_header {
> +     struct  usb_os_desc_header header;
> +     __u16   wCount;
> +} __attribute__((packed));
> +
> +struct usb_ext_prop_desc {
> +     __u32   dwSize;
> +     __u32   dwPropertyDataType;
> +     __u16   wPropertyNameLength;
> +} __attribute__((packed));
> +
> +#ifndef __KERNEL__
>
>  /*
>   * Descriptors format:

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

Reply via email to