[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-id: <1446555242-3733-8-git-send-email-r.baldyga@samsung.com>
Date: Tue, 03 Nov 2015 13:53:46 +0100
From: Robert Baldyga <r.baldyga@...sung.com>
To: balbi@...com
Cc: gregkh@...uxfoundation.org, andrzej.p@...sung.com,
m.szyprowski@...sung.com, b.zolnierkie@...sung.com,
linux-usb@...r.kernel.org, linux-kernel@...r.kernel.org,
Robert Baldyga <r.baldyga@...sung.com>
Subject: [PATCH 07/23] usb: gadget: composite: add functions for descriptors
handling
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.baldyga@...sung.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-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists