Introduce functions and macros allowing to create and assign descriptors
to function easily. Macros build structure hierarchy using pointers to
USB descriptors, while functions assigning them to gadget make a deep
copy. It allows for easy conversion of USB functions to make them using
new descriptors format.

Signed-off-by: Robert Baldyga <r.bald...@samsung.com>
---
 drivers/usb/gadget/composite.c | 344 +++++++++++++++++++++++++++++++++++++++++
 include/linux/usb/composite.h  |  52 +++++++
 2 files changed, 396 insertions(+)

diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 8b14c2a..3ecfaca 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -327,6 +327,315 @@ int usb_function_activate(struct usb_function *function)
 EXPORT_SYMBOL_GPL(usb_function_activate);
 
 /**
+ * usb_function_set_descs - assing descriptors to USB function
+ * @f: USB function
+ * @descs: USB descriptors to be assigned to function
+ *
+ * This function is to be called from prep_desc() callback to provide
+ * descriptors needed during bind process. It does a deep copy of
+ * descriptors hierarchy.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_function_set_descs(struct usb_function *f,
+               struct usb_composite_descs *descs)
+{
+       struct usb_composite_descs *descs_c;
+       struct usb_composite_intf *intf, *intf_c;
+       struct usb_composite_altset *altset, *altset_c;
+       struct usb_composite_ep *ep, *ep_c;
+       int i, a, e;
+       size_t size;
+       void *mem;
+
+       size = sizeof(*descs);
+
+       if (!descs->intfs_num)
+               return -EINVAL;
+
+       if (!f->config)
+               return -ENODEV;
+
+       size += descs->intfs_num *
+               (sizeof(*descs->intfs) + sizeof(**descs->intfs));
+       for (i = 0; i < descs->intfs_num; ++i) {
+               intf = descs->intfs[i];
+               if (!intf->altsets_num)
+                       return -EINVAL;
+               size += intf->altsets_num *
+                       (sizeof(*intf->altsets) + sizeof(**intf->altsets));
+               for (a = 0; a < intf->altsets_num; ++a) {
+                       altset = intf->altsets[a];
+                       size += sizeof(*altset->alt.desc);
+                       size += altset->eps_num *
+                               (sizeof(*altset->eps) + sizeof(**altset->eps));
+                       for (e = 0; e < altset->eps_num; ++e) {
+                               ep = altset->eps[e];
+                               if (ep->fs.desc) {
+                                       size += sizeof(*ep->fs.desc);
+                                       f->config->fullspeed = true;
+                               }
+                               if (ep->hs.desc) {
+                                       size += sizeof(*ep->hs.desc);
+                                       f->config->highspeed = true;
+                               }
+                               if (ep->ss.desc) {
+                                       size += sizeof(*ep->ss.desc);
+                                       f->config->superspeed = true;
+                               }
+                               if (ep->ss_comp.desc)
+                                       size += sizeof(*ep->ss_comp.desc);
+                       }
+               }
+       }
+
+       mem = kzalloc(size, GFP_KERNEL);
+       if (!mem)
+               return -ENOMEM;
+
+       f->descs = descs_c = mem;
+       mem += sizeof(*descs_c);
+       INIT_LIST_HEAD(&descs_c->vendor_descs);
+       descs_c->intfs_num = descs->intfs_num;
+       descs_c->intfs = mem;
+       mem += descs_c->intfs_num * sizeof(*descs_c->intfs);
+
+       for (i = 0; i < f->descs->intfs_num; ++i) {
+               intf = descs->intfs[i];
+               descs_c->intfs[i] = intf_c = mem;
+               mem += sizeof(*intf_c);
+               intf_c->altsets_num = intf->altsets_num;
+               intf_c->altsets = mem;
+               mem += intf_c->altsets_num * sizeof(*intf_c->altsets);
+
+               for (a = 0; a < intf->altsets_num; ++a) {
+                       altset = intf->altsets[a];
+                       intf_c->altsets[a] = altset_c = mem;
+                       mem += sizeof(*altset_c);
+                       INIT_LIST_HEAD(&altset_c->vendor_descs);
+                       altset_c->alt.desc = mem;
+                       mem += sizeof(*altset->alt.desc);
+                       memcpy(altset_c->alt.desc, altset->alt.desc,
+                               sizeof(*altset->alt.desc));
+                       altset_c->eps_num = altset->eps_num;
+                       altset_c->eps = mem;
+                       mem += altset_c->eps_num * sizeof(*altset_c->eps);
+
+                       for (e = 0; e < altset->eps_num; ++e) {
+                               ep = altset->eps[e];
+                               altset_c->eps[e] = ep_c = mem;
+                               mem += sizeof(*ep_c);
+                               INIT_LIST_HEAD(&ep_c->vendor_descs);
+                               if (ep->fs.desc) {
+                                       ep_c->fs.desc = mem;
+                                       mem += sizeof(*ep_c->fs.desc);
+                                       memcpy(ep_c->fs.desc, ep->fs.desc,
+                                               sizeof(*ep_c->fs.desc));
+                               }
+                               if (ep->hs.desc) {
+                                       ep_c->hs.desc = mem;
+                                       mem += sizeof(*ep_c->hs.desc);
+                                       memcpy(ep_c->hs.desc, ep->hs.desc,
+                                               sizeof(*ep_c->hs.desc));
+                               }
+                               if (ep->ss.desc) {
+                                       ep_c->ss.desc = mem;
+                                       mem += sizeof(*ep_c->ss.desc);
+                                       memcpy(ep_c->ss.desc, ep->ss.desc,
+                                               sizeof(*ep_c->ss.desc));
+                               }
+                               if (ep->ss_comp.desc) {
+                                       ep_c->ss_comp.desc = mem;
+                                       mem += sizeof(*ep_c->ss_comp.desc);
+                                       memcpy(ep_c->ss_comp.desc,
+                                               ep->ss_comp.desc,
+                                               sizeof(*ep_c->ss_comp.desc));
+                               }
+                       }
+               }
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(usb_function_set_descs);
+
+/**
+ * usb_function_free_descs - frees descriptors assinged to function
+ * @f: USB function
+ */
+static inline void usb_function_free_descs(struct usb_function *f)
+{
+       kfree(f->descs);
+       f->descs = NULL;
+}
+
+/**
+ * usb_function_add_vendor_desc - add vendor specific descriptor to USB
+ *     function
+ * @f: USB function
+ * @desc: descriptor to be attached
+ *
+ * Descriptor is copied and attached at the end of linked list.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_function_add_vendor_desc(struct usb_function *f,
+               struct usb_descriptor_header *desc)
+{
+       struct usb_composite_vendor_desc *vd;
+       void *mem;
+
+       if (!f->descs)
+               return -ENODEV;
+
+       mem = kmalloc(sizeof(*vd) + desc->bLength, GFP_KERNEL);
+       if (!mem)
+               return -ENOMEM;
+
+       vd = mem;
+       vd->desc = mem + sizeof(*vd);
+
+       memcpy(vd->desc, desc, desc->bLength);
+
+       list_add_tail(&vd->list, &f->descs->vendor_descs);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(usb_function_add_vendor_desc);
+
+/**
+ * usb_ep_add_vendor_desc - add vendor specific descriptor to altsetting
+ * @f: USB function
+ * @i: index of interface in function
+ * @a: index of altsetting in interface
+ * @desc: descriptor to be attached
+ *
+ * Descriptor is copied and attached at the end of linked list.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_altset_add_vendor_desc(struct usb_function *f, int i, int a,
+               struct usb_descriptor_header *desc)
+{
+       struct usb_composite_vendor_desc *vd;
+       struct usb_composite_altset *alt;
+       void *mem;
+
+       if (!f->descs)
+               return -ENODEV;
+       if (f->descs->intfs_num <= i)
+               return -ENODEV;
+       if (f->descs->intfs[i]->altsets_num <= a)
+               return -ENODEV;
+
+       mem = kmalloc(sizeof(*vd) + desc->bLength, GFP_KERNEL);
+       if (!mem)
+               return -ENOMEM;
+
+       vd = mem;
+       vd->desc = mem + sizeof(*vd);
+
+       memcpy(vd->desc, desc, desc->bLength);
+
+       alt = f->descs->intfs[i]->altsets[a];
+       list_add_tail(&vd->list, &alt->vendor_descs);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(usb_altset_add_vendor_desc);
+
+/**
+ * usb_ep_add_vendor_desc - add vendor specific descriptor to endpoint
+ * @f: USB function
+ * @i: index of interface in function
+ * @a: index of altsetting in interface
+ * @e: index of endpoint in altsetting
+ * @desc: descriptor to be attached
+ *
+ * Descriptor is copied and attached at the end of linked list.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_ep_add_vendor_desc(struct usb_function *f, int i, int a, int e,
+               struct usb_descriptor_header *desc)
+{
+       struct usb_composite_vendor_desc *vd;
+       struct usb_composite_ep *ep;
+       void *mem;
+
+       if (!f->descs)
+               return -ENODEV;
+       if (f->descs->intfs_num <= i)
+               return -ENODEV;
+       if (f->descs->intfs[i]->altsets_num <= a)
+               return -ENODEV;
+       if (f->descs->intfs[i]->altsets[e]->eps_num <= e)
+               return -ENODEV;
+
+       mem = kmalloc(sizeof(*vd) + desc->bLength, GFP_KERNEL);
+       if (!mem)
+               return -ENOMEM;
+
+       vd = mem;
+       vd->desc = mem + sizeof(*vd);
+
+       memcpy(vd->desc, desc, desc->bLength);
+
+       ep = f->descs->intfs[i]->altsets[a]->eps[e];
+       list_add_tail(&vd->list, &ep->vendor_descs);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(usb_ep_add_vendor_desc);
+
+/**
+ * free_vendor_descs - removes and frees all vendor descriptors from
+ *     given list
+ * @vendor_descs: handle to the list of descriptors
+ */
+static inline void free_vendor_descs(struct list_head *vendor_descs)
+{
+       while (!list_empty(vendor_descs)) {
+               struct usb_composite_vendor_desc *d;
+
+               d = list_first_entry(vendor_descs,
+                               struct usb_composite_vendor_desc, list);
+               list_del(&d->list);
+               kfree(d);
+       }
+}
+
+/**
+ * usb_function_free_vendor_descs - frees vendor specific descriptors
+ *     assinged to function
+ * @f: USB function
+ */
+static void usb_function_free_vendor_descs(struct usb_function *f)
+{
+       struct usb_composite_intf *intf;
+       struct usb_composite_altset *alt;
+       int i, a, e;
+
+       if (!f->descs)
+               return;
+
+       free_vendor_descs(&f->descs->vendor_descs);
+       f->descs->vendor_descs_num = 0;
+       for (i = 0; i < f->descs->intfs_num; ++i) {
+               intf = f->descs->intfs[i];
+               for (a = 0; a < intf->altsets_num; ++a) {
+                       alt = intf->altsets[a];
+                       free_vendor_descs(&alt->vendor_descs);
+                       alt->vendor_descs_num = 0;
+                       for (e = 0; e < alt->eps_num; ++e) {
+                               free_vendor_descs(&alt->eps[e]->vendor_descs);
+                               alt->eps[e]->vendor_descs_num = 0;
+                       }
+               }
+       }
+}
+
+/**
  * usb_interface_id() - allocate an unused interface ID
  * @config: configuration associated with the interface
  * @function: function handling the interface
@@ -1893,6 +2202,38 @@ static ssize_t suspended_show(struct device *dev, struct 
device_attribute *attr,
 }
 static DEVICE_ATTR_RO(suspended);
 
+/**
+ * composite_free_descs - free entity descriptors for all functions in all
+ *     configurations, allocated with usb_function_set_descs()
+ * @cdev: composite device
+ */
+void composite_free_descs(struct usb_composite_dev *cdev)
+{
+       struct usb_configuration *c;
+       struct usb_function *f;
+
+       list_for_each_entry(c, &cdev->configs, list)
+               list_for_each_entry(f, &c->functions, list)
+                       usb_function_free_descs(f);
+}
+EXPORT_SYMBOL_GPL(composite_free_descs);
+
+/**
+ * composite_free_descs - free vendor and class specific descriptors for all
+ *     functions in all configurations.
+ * @cdev: composite device
+ */
+void composite_free_vendor_descs(struct usb_composite_dev *cdev)
+{
+       struct usb_configuration *c;
+       struct usb_function *f;
+
+       list_for_each_entry(c, &cdev->configs, list)
+               list_for_each_entry(f, &c->functions, list)
+                       usb_function_free_vendor_descs(f);
+}
+EXPORT_SYMBOL_GPL(composite_free_vendor_descs);
+
 static void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver)
 {
        struct usb_composite_dev        *cdev = get_gadget_data(gadget);
@@ -1904,6 +2245,9 @@ static void __composite_unbind(struct usb_gadget *gadget, 
bool unbind_driver)
         */
        WARN_ON(cdev->config);
 
+       composite_free_vendor_descs(cdev);
+       composite_free_descs(cdev);
+
        while (!list_empty(&cdev->configs)) {
                struct usb_configuration        *c;
                c = list_first_entry(&cdev->configs,
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
index 686c5f7..b778d4d 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -171,6 +171,43 @@ struct usb_composite_descs {
        int vendor_descs_num;
 };
 
+/*
+ * Macros to be used to create USB descriptors hierarchy.
+ */
+
+#define USB_COMPOSITE_ENDPOINT(_name, _fs_desc, _hs_desc, _ss_desc, _ss_comp) \
+       static struct usb_composite_ep _name = { \
+               .fs = { .desc = _fs_desc, }, \
+               .hs = { .desc = _hs_desc, }, \
+               .ss = { .desc = _ss_desc, }, \
+               .ss_comp = { .desc = _ss_comp, }, \
+               .vendor_descs = LIST_HEAD_INIT(_name.vendor_descs), \
+       }
+
+#define __EP_ARRAY(...) ((struct usb_composite_ep*[]){ __VA_ARGS__ })
+#define USB_COMPOSITE_ALTSETTING(_name, _desc, ...) \
+       static struct usb_composite_altset _name = { \
+               .alt = { .desc = _desc, }, \
+               .eps = __EP_ARRAY(__VA_ARGS__), \
+               .eps_num = ARRAY_SIZE(__EP_ARRAY(__VA_ARGS__)), \
+               .vendor_descs = LIST_HEAD_INIT(_name.vendor_descs), \
+       }
+
+#define __ALTSET_ARRAY(...) ((struct usb_composite_altset*[]){ __VA_ARGS__ })
+#define USB_COMPOSITE_INTERFACE(_name, ...) \
+       static struct usb_composite_intf _name = { \
+               .altsets = __ALTSET_ARRAY(__VA_ARGS__), \
+               .altsets_num = ARRAY_SIZE(__ALTSET_ARRAY(__VA_ARGS__)), \
+       }
+
+#define __INTF_ARRAY(...) ((struct usb_composite_intf*[]){ __VA_ARGS__ })
+#define USB_COMPOSITE_DESCRIPTORS(_name, ...) \
+       static struct usb_composite_descs _name = { \
+               .intfs = __INTF_ARRAY(__VA_ARGS__), \
+               .intfs_num = ARRAY_SIZE(__INTF_ARRAY(__VA_ARGS__)), \
+               .vendor_descs = LIST_HEAD_INIT(_name.vendor_descs), \
+       }
+
 /**
  * struct usb_os_desc_ext_prop - describes one "Extended Property"
  * @entry: used to keep a list of extended properties
@@ -356,6 +393,18 @@ int usb_add_function(struct usb_configuration *, struct 
usb_function *);
 int usb_function_deactivate(struct usb_function *);
 int usb_function_activate(struct usb_function *);
 
+int usb_function_set_descs(struct usb_function *f,
+               struct usb_composite_descs *descs);
+
+int usb_function_add_vendor_desc(struct usb_function *f,
+               struct usb_descriptor_header *desc);
+
+int usb_altset_add_vendor_desc(struct usb_function *f, int i, int a,
+               struct usb_descriptor_header *desc);
+
+int usb_ep_add_vendor_desc(struct usb_function *f, int i, int a, int e,
+               struct usb_descriptor_header *desc);
+
 int usb_interface_id(struct usb_configuration *, struct usb_function *);
 
 int config_ep_by_speed(struct usb_gadget *g, struct usb_function *f,
@@ -532,6 +581,9 @@ extern int composite_os_desc_req_prepare(struct 
usb_composite_dev *cdev,
                                         struct usb_ep *ep0);
 void composite_dev_cleanup(struct usb_composite_dev *cdev);
 
+void composite_free_descs(struct usb_composite_dev *cdev);
+void composite_free_vendor_descs(struct usb_composite_dev *cdev);
+
 static inline struct usb_composite_driver *to_cdriver(
                struct usb_gadget_driver *gdrv)
 {
-- 
1.9.1

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