usb: gadget: f_fs: OS descriptors support
authorAndrzej Pietrasiewicz <andrzej.p@samsung.com>
Wed, 9 Jul 2014 10:20:08 +0000 (12:20 +0200)
committerFelipe Balbi <balbi@ti.com>
Thu, 10 Jul 2014 13:36:52 +0000 (08:36 -0500)
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>
drivers/usb/gadget/f_fs.c
drivers/usb/gadget/u_fs.h
include/uapi/linux/usb/functionfs.h

index e1b2ddd7964a4d96e3455f92dc6d745ce99f8e76..fe45060e0a7a3e19df5b0383d88daefeae8e19a6 100644 (file)
@@ -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,19 @@ 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,
+                                   struct usb_os_desc_header *h, void *data,
+                                   unsigned len, void *priv);
+
 static int __must_check ffs_do_single_desc(char *data, unsigned len,
                                           ffs_entity_callback entity,
                                           void *priv)
@@ -1856,11 +1865,191 @@ 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
+                */
+               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,
                                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();
@@ -1878,7 +2067,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;
                }
@@ -1901,6 +2091,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;
@@ -1914,6 +2109,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;
@@ -1926,6 +2129,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;
 
@@ -2267,6 +2471,85 @@ 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];
+               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)
 {
@@ -2328,7 +2611,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);
@@ -2340,6 +2623,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;
 
@@ -2350,12 +2645,16 @@ static int _ffs_func_bind(struct usb_configuration *c,
                return -ENOTSUPP;
 
        /* 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))
                return -ENOMEM;
 
-       /* Zero */
-       memset(vla_ptr(vlabuf, d, eps), 0, d_eps__sz);
+       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);
+
        /* Copy descriptors  */
        memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs,
               ffs->raw_descs_length);
@@ -2409,12 +2708,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;
        }
 
        /*
@@ -2430,6 +2733,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);
+       if (unlikely(ret < 0))
+               goto error;
+       func->function.os_desc_n =
+               c->cdev->use_os_string ? ffs->interfaces_count : 0;
+
        /* And we're done */
        ffs_event_add(ffs, FUNCTIONFS_BIND);
        return 0;
index bf0ba375d459ac59710711815a934b7f97c8f6ab..63d6e71569c18d7ddcf26e92586798d30a97cbd2 100644 (file)
@@ -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;
index 2a4b4a72a4f915ee0c0e16db893713bc622359db..b66fae77c08cf5059e31c1fdfea20ecff5051608 100644 (file)
@@ -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,36 @@ struct usb_endpoint_descriptor_no_audio {
        __u8  bInterval;
 } __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:
@@ -45,9 +74,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 +105,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 {