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.

 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)
+{
+       int ret;
+       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);
+               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 (num == count)
+                       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);
+               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;
+                       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)
+{
+       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) {
+                       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;
+               }
+               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 (d->bFirstInterfaceNumber >= ffs->interfaces_count)
+                       return -EINVAL;
+
+               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;
+
+               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
+                       return -EINVAL;
+               }
+               ++ffs->ms_os_descs_ext_prop_count;
+               ffs->ms_os_descs_ext_prop_name_len += (pnl << 1);
+               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:
+
        case FUNCTIONFS_SETUP:
                rem_type1 = type;
                /* Discard all similar events */
@@ -2266,6 +2469,88 @@ 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,
+                                     u8 *valuep, void *data, void *priv)
+{
+       struct ffs_function *func = priv;
+       __u8 length = 0;
+
+       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 w_index = le16_to_cpu(desc->wIndex);
+
+               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 *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, (void *)desc + 2, 16);
+               length = sizeof(*desc);
+       }
+               break;
+       case FFS_OS_DESC_EXT_PROP: {
+               struct usb_os_desc_header *h =
+                       (struct usb_os_desc_header *)valuep;
+               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 *)(data + 10 + 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, data + 14 + ext_prop->name_len,
+                      ext_prop->data_len);
+               ext_prop->data_len <<= 1;
+               ext_prop->data = ext_prop_data;
+
+               memcpy(ext_prop_name, data + 10, ext_prop->name_len);
+               ext_prop->name_len <<= 1;
+               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;
+       }
+
+       return length;
+}
+
 static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f,
                                                struct usb_configuration *c)
 {
@@ -2327,7 +2612,7 @@ static int _ffs_func_bind(struct usb_configuration *c,
        const int super = gadget_is_superspeed(func->gadget) &&
                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 */
        vla_group(d);
@@ -2339,6 +2624,18 @@ static int _ffs_func_bind(struct usb_configuration *c,
        vla_item_with_sz(d, struct usb_descriptor_header *, ss_descs,
                super ? ffs->ss_descs_count + 1 : 0);
        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);
        char *vlabuf;
 
@@ -2353,8 +2650,20 @@ static int _ffs_func_bind(struct usb_configuration *c,
        if (unlikely(!vlabuf))
                return -ENOMEM;
 
+       ffs->ms_os_descs_ext_prop_avail = vla_ptr(vlabuf, d, ext_prop);
+       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);
+
        /* Zero */
        memset(vla_ptr(vlabuf, d, eps), 0, d_eps__sz);
+       memset(vla_ptr(vlabuf, d, os_desc_table), 0, d_os_desc_table__sz);
+       memset(vla_ptr(vlabuf, d, ext_compat), 0, d_ext_compat__sz);
+       memset(vla_ptr(vlabuf, d, os_desc), 0, d_os_desc__sz);
+       memset(vla_ptr(vlabuf, d, ext_prop), 0, d_ext_prop__sz);
+       memset(vla_ptr(vlabuf, d, ext_prop_name), 0, d_ext_prop_name__sz);
+       memset(vla_ptr(vlabuf, d, ext_prop_data), 0, d_ext_prop_data__sz);
        /* Copy descriptors  */
        memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs,
               ffs->raw_descs_length);
@@ -2408,12 +2717,16 @@ static int _ffs_func_bind(struct usb_configuration *c,
 
        if (likely(super)) {
                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,
                                d_raw_descs__sz - fs_len - hs_len,
                                __ffs_func_bind_do_descs, func);
-               if (unlikely(ret < 0))
+               if (unlikely(ss_len < 0)) {
+                       ret = ss_len;
                        goto error;
+               }
+       } else {
+               ss_len = 0;
        }
 
        /*
@@ -2429,6 +2742,28 @@ static int _ffs_func_bind(struct usb_configuration *c,
        if (unlikely(ret < 0))
                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);
+       func->function.os_desc_n =
+               c->cdev->use_os_string ? ffs->interfaces_count : 0;
+       if (unlikely(ret < 0))
+               goto error;
+
        /* And we're done */
        ffs_event_add(ffs, FUNCTIONFS_BIND);
        return 0;
diff --git a/drivers/usb/gadget/function/u_fs.h 
b/drivers/usb/gadget/function/u_fs.h
index bf0ba37..63d6e71 100644
--- a/drivers/usb/gadget/function/u_fs.h
+++ b/drivers/usb/gadget/function/u_fs.h
@@ -216,6 +216,13 @@ struct ffs_data {
        unsigned                        fs_descs_count;
        unsigned                        hs_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                  interfaces_count;
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
@@ -18,10 +18,9 @@ enum functionfs_flags {
        FUNCTIONFS_HAS_FS_DESC = 1,
        FUNCTIONFS_HAS_HS_DESC = 2,
        FUNCTIONFS_HAS_SS_DESC = 4,
+       FUNCTIONFS_HAS_MS_OS_DESC = 8,
 };
 
-#ifndef __KERNEL__
-
 /* Descriptor of an non-audio endpoint */
 struct usb_endpoint_descriptor_no_audio {
        __u8  bLength;
@@ -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;
+} __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:
@@ -45,9 +80,11 @@ struct usb_endpoint_descriptor_no_audio {
  * |     | fs_count  | LE32         | number of full-speed descriptors     |
  * |     | hs_count  | LE32         | number of high-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       |
  * |     | hs_descrs | Descriptor[] | list of high-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
  * structure.  Any flags that are not recognised cause the whole block to be
@@ -74,6 +111,52 @@ struct usb_endpoint_descriptor_no_audio {
  * |   0 | bLength         | U8   | length of the descriptor |
  * |   1 | bDescriptorType | U8   | descriptor type          |
  * |   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 {
-- 
1.8.3.2

--
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