Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1325342 > unrolled thread

[PATCH v4 00/43] usb: gadget: composite: introduce new function API

Started byRobert Baldyga <r.baldyga@samsung.com>
First post2016-02-03 13:50 +0100
Last post2016-02-03 14:00 +0100
Articles 4 on this page of 44 — 2 participants

Back to article view | Back to linux.kernel


Contents

  [PATCH v4 00/43] usb: gadget: composite: introduce new function API Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 10/43] usb: gadget: composite: handle vendor descs Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 16/43] usb: gadget: composite: add usb_function_get_ep()  function Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 24/43] usb: gadget: f_rndis: conversion to new API Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 18/43] usb: gadget: composite: usb_get_endpoint_address()  function Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 06/43] usb: gadget: composite: introduce new descriptors  format Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
      Re: [PATCH v4 06/43] usb: gadget: composite: introduce new  descriptors format kbuild test robot <lkp@intel.com> - 2016-02-04 06:20 +0100
    [PATCH v4 15/43] usb: gadget: composite: handle get_alt() automatically Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 04/43] usb: gadget: configfs: fix error path Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 37/43] usb: gadget: f_mass_storage: conversion to new API Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 23/43] usb: gadget: f_ecm: conversion to new API Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 02/43] usb: gadget: f_sourcesink: free requests in  sourcesink_disable() Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 13/43] usb: gadget: composite: enable eps before calling  set_alt() callback Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 32/43] usb: gadget: f_obex: conversion to new API Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 20/43] usb: gadget: configfs: add new composite API support Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 27/43] usb: gadget: f_acm: conversion to new API Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 26/43] usb: gadget: f_hid: conversion to new API Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 30/43] usb: gadget: f_printer: conversion to new API Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 08/43] usb: gadget: composite: introduce new USB function ops Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 35/43] usb: gadget: f_uac1: conversion to new API Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 31/43] usb: gadget: f_serial: conversion to new API Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 42/43] Documentation: update uvc configfs interface  description Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 43/43] usb: gadget: f_uvc: conversion to new API Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 25/43] usb: gadget: f_hid: handle requests lifetime properly Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 03/43] usb: gadget: f_loopback: free requests in  loopback_disable() Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 38/43] usb: gadget: u_serial: remove  usb_ep_enable()/usb_ep_disable() Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 34/43] usb: gadget: f_subset: conversion to new API Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 29/43] usb: gadget: f_ncm: conversion to new API Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 22/43] usb: gadget: f_sourcesink: convert to new API Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 39/43] usb: gadget: u_ether: remove  usb_ep_enable()/usb_ep_disable() Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 40/43] usb: gadget: uvc: fix typo in UVCG_OPTS_ATTR() macro Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 36/43] usb: gadget: f_uac2: conversion to new API Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 41/43] usb: gadget: uvc: simplify descriptors generation Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 13:50 +0100
    [PATCH v4 09/43] usb: gadget: composite: handle function bind Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 14:00 +0100
      Re: [PATCH v4 09/43] usb: gadget: composite: handle function bind kbuild test robot <lkp@intel.com> - 2016-02-04 06:50 +0100
    [PATCH v4 01/43] usb: gadget: f_sourcesink: make ISO altset  user-selectable Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 14:00 +0100
    [PATCH v4 12/43] usb: gadget: composite: disable eps before calling  disable() callback Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 14:00 +0100
    [PATCH v4 17/43] usb: gadget: composite: add usb_get_interface_id()  function Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 14:00 +0100
    [PATCH v4 19/43] usb: gadget: composite: enable adding USB functions  using new API Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 14:00 +0100
    [PATCH v4 05/43] usb: gadget: composite: fix recursive spinlock locking Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 14:00 +0100
    [PATCH v4 21/43] usb: gadget: f_loopback: convert to new API Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 14:00 +0100
    [PATCH v4 14/43] usb: gadget: composite: introduce clear_alt()  operation Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 14:00 +0100
    [PATCH v4 11/43] usb: gadget: composite: generate old descs for  compatibility Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 14:00 +0100
    [PATCH v4 07/43] usb: gadget: composite: add functions for descriptors  handling Robert Baldyga <r.baldyga@samsung.com> - 2016-02-03 14:00 +0100

Page 3 of 3 — ← Prev page 1 2 [3]


#1325389 — [PATCH v4 21/43] usb: gadget: f_loopback: convert to new API

FromRobert Baldyga <r.baldyga@samsung.com>
Date2016-02-03 14:00 +0100
Subject[PATCH v4 21/43] usb: gadget: f_loopback: convert to new API
Message-ID<qY5co-24p-39@gated-at.bofh.it>
In reply to#1325342
Generate descriptors in new format and attach them to USB function in
prep_descs(). Change set_alt() implementation and implement clear_alt()
operation. Remove unnecessary boilerplate code.

Call usb_config_do_bind() in legacy gadget zero, because it uses
usb_add_config_only() instead of usb_add_config() and prepares
configuration manually.

Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
---
 drivers/usb/gadget/function/f_loopback.c | 162 ++++++-------------------------
 drivers/usb/gadget/legacy/zero.c         |   3 +
 2 files changed, 33 insertions(+), 132 deletions(-)

diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c
index f985107..f2aa056 100644
--- a/drivers/usb/gadget/function/f_loopback.c
+++ b/drivers/usb/gadget/function/f_loopback.c
@@ -76,13 +76,6 @@ static struct usb_endpoint_descriptor fs_loop_sink_desc = {
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 };
 
-static struct usb_descriptor_header *fs_loopback_descs[] = {
-	(struct usb_descriptor_header *) &loopback_intf,
-	(struct usb_descriptor_header *) &fs_loop_sink_desc,
-	(struct usb_descriptor_header *) &fs_loop_source_desc,
-	NULL,
-};
-
 /* high speed support: */
 
 static struct usb_endpoint_descriptor hs_loop_source_desc = {
@@ -101,13 +94,6 @@ static struct usb_endpoint_descriptor hs_loop_sink_desc = {
 	.wMaxPacketSize =	cpu_to_le16(512),
 };
 
-static struct usb_descriptor_header *hs_loopback_descs[] = {
-	(struct usb_descriptor_header *) &loopback_intf,
-	(struct usb_descriptor_header *) &hs_loop_source_desc,
-	(struct usb_descriptor_header *) &hs_loop_sink_desc,
-	NULL,
-};
-
 /* super speed support: */
 
 static struct usb_endpoint_descriptor ss_loop_source_desc = {
@@ -142,14 +128,17 @@ static struct usb_ss_ep_comp_descriptor ss_loop_sink_comp_desc = {
 	.wBytesPerInterval =	0,
 };
 
-static struct usb_descriptor_header *ss_loopback_descs[] = {
-	(struct usb_descriptor_header *) &loopback_intf,
-	(struct usb_descriptor_header *) &ss_loop_source_desc,
-	(struct usb_descriptor_header *) &ss_loop_source_comp_desc,
-	(struct usb_descriptor_header *) &ss_loop_sink_desc,
-	(struct usb_descriptor_header *) &ss_loop_sink_comp_desc,
-	NULL,
-};
+USB_COMPOSITE_ENDPOINT(ep_source, &fs_loop_source_desc, &hs_loop_source_desc,
+		&ss_loop_source_desc, &ss_loop_source_comp_desc);
+USB_COMPOSITE_ENDPOINT(ep_sink, &fs_loop_sink_desc, &hs_loop_sink_desc,
+		&ss_loop_sink_desc, &ss_loop_sink_comp_desc);
+
+USB_COMPOSITE_ALTSETTING(altset0, &loopback_intf, &ep_source, &ep_sink);
+
+USB_COMPOSITE_INTERFACE(intf0, &altset0);
+
+USB_COMPOSITE_DESCRIPTORS(loopback_descs, &intf0);
+
 
 /* function-specific strings: */
 
@@ -170,18 +159,10 @@ static struct usb_gadget_strings *loopback_strings[] = {
 
 /*-------------------------------------------------------------------------*/
 
-static int loopback_bind(struct usb_configuration *c, struct usb_function *f)
+static int loopback_prep_descs(struct usb_function *f)
 {
-	struct usb_composite_dev *cdev = c->cdev;
-	struct f_loopback	*loop = func_to_loop(f);
-	int			id;
-	int ret;
-
-	/* allocate interface ID(s) */
-	id = usb_interface_id(c, f);
-	if (id < 0)
-		return id;
-	loopback_intf.bInterfaceNumber = id;
+	struct usb_composite_dev *cdev = f->config->cdev;
+	int id;
 
 	id = usb_string_id(cdev);
 	if (id < 0)
@@ -189,40 +170,7 @@ static int loopback_bind(struct usb_configuration *c, struct usb_function *f)
 	strings_loopback[0].id = id;
 	loopback_intf.iInterface = id;
 
-	/* allocate endpoints */
-
-	loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_source_desc);
-	if (!loop->in_ep) {
-autoconf_fail:
-		ERROR(cdev, "%s: can't autoconfigure on %s\n",
-			f->name, cdev->gadget->name);
-		return -ENODEV;
-	}
-
-	loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_sink_desc);
-	if (!loop->out_ep)
-		goto autoconf_fail;
-
-	/* support high speed hardware */
-	hs_loop_source_desc.bEndpointAddress =
-		fs_loop_source_desc.bEndpointAddress;
-	hs_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress;
-
-	/* support super speed hardware */
-	ss_loop_source_desc.bEndpointAddress =
-		fs_loop_source_desc.bEndpointAddress;
-	ss_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress;
-
-	ret = usb_assign_descriptors(f, fs_loopback_descs, hs_loopback_descs,
-			ss_loopback_descs);
-	if (ret)
-		return ret;
-
-	DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
-	    (gadget_is_superspeed(c->cdev->gadget) ? "super" :
-	     (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")),
-			f->name, loop->in_ep->name, loop->out_ep->name);
-	return 0;
+	return usb_function_set_descs(f, &loopback_descs);
 }
 
 static void lb_free_func(struct usb_function *f)
@@ -235,7 +183,6 @@ static void lb_free_func(struct usb_function *f)
 	opts->refcnt--;
 	mutex_unlock(&opts->lock);
 
-	usb_free_all_descriptors(f);
 	kfree(func_to_loop(f));
 }
 
@@ -285,15 +232,6 @@ static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
 	}
 }
 
-static void disable_loopback(struct f_loopback *loop)
-{
-	struct usb_composite_dev	*cdev;
-
-	cdev = loop->function.config->cdev;
-	disable_endpoints(cdev, loop->in_ep, loop->out_ep, NULL, NULL);
-	VDBG(cdev, "%s disabled\n", loop->function.name);
-}
-
 static inline struct usb_request *lb_alloc_ep_req(struct usb_ep *ep, int len)
 {
 	struct f_loopback	*loop = ep->driver_data;
@@ -348,70 +286,30 @@ fail:
 	return result;
 }
 
-static int enable_endpoint(struct usb_composite_dev *cdev,
-			   struct f_loopback *loop, struct usb_ep *ep)
-{
-	int					result;
-
-	result = config_ep_by_speed(cdev->gadget, &(loop->function), ep);
-	if (result)
-		goto out;
-
-	result = usb_ep_enable(ep);
-	if (result < 0)
-		goto out;
-	ep->driver_data = loop;
-	result = 0;
-
-out:
-	return result;
-}
-
-static int
-enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop)
-{
-	int					result = 0;
-
-	result = enable_endpoint(cdev, loop, loop->in_ep);
-	if (result)
-		goto out;
-
-	result = enable_endpoint(cdev, loop, loop->out_ep);
-	if (result)
-		goto disable_in;
-
-	result = alloc_requests(cdev, loop);
-	if (result)
-		goto disable_out;
-
-	DBG(cdev, "%s enabled\n", loop->function.name);
-	return 0;
-
-disable_out:
-	usb_ep_disable(loop->out_ep);
-disable_in:
-	usb_ep_disable(loop->in_ep);
-out:
-	return result;
-}
-
 static int loopback_set_alt(struct usb_function *f,
 		unsigned intf, unsigned alt)
 {
 	struct f_loopback	*loop = func_to_loop(f);
 	struct usb_composite_dev *cdev = f->config->cdev;
 
-	/* we know alt is zero */
-	disable_loopback(loop);
-	return enable_loopback(cdev, loop);
+	loop->in_ep = usb_function_get_ep(f, intf, 0);
+	if (!loop->in_ep)
+		return -ENODEV;
+	loop->in_ep->driver_data = loop;
+
+	loop->out_ep = usb_function_get_ep(f, intf, 1);
+	if (!loop->out_ep)
+		return -ENODEV;
+	loop->out_ep->driver_data = loop;
+
+	return alloc_requests(cdev, loop);
 }
 
-static void loopback_disable(struct usb_function *f)
+static void loopback_clear_alt(struct usb_function *f,
+		unsigned intf, unsigned alt)
 {
 	struct f_loopback	*loop = func_to_loop(f);
 
-	disable_loopback(loop);
-
 	free_ep_req(loop->out_ep, loop->out_req);
 	usb_ep_free_request(loop->in_ep, loop->in_req);
 }
@@ -437,9 +335,9 @@ static struct usb_function *loopback_alloc(struct usb_function_instance *fi)
 		loop->qlen = 32;
 
 	loop->function.name = "loopback";
-	loop->function.bind = loopback_bind;
+	loop->function.prep_descs = loopback_prep_descs;
 	loop->function.set_alt = loopback_set_alt;
-	loop->function.disable = loopback_disable;
+	loop->function.clear_alt = loopback_clear_alt;
 	loop->function.strings = loopback_strings;
 
 	loop->function.free_func = lb_free_func;
diff --git a/drivers/usb/gadget/legacy/zero.c b/drivers/usb/gadget/legacy/zero.c
index 6adfc88..781ca94 100644
--- a/drivers/usb/gadget/legacy/zero.c
+++ b/drivers/usb/gadget/legacy/zero.c
@@ -378,6 +378,9 @@ static int zero_bind(struct usb_composite_dev *cdev)
 	status = usb_add_function(&loopback_driver, func_lb);
 	if (status)
 		goto err_free_otg_desc;
+	status = usb_config_do_bind(&loopback_driver);
+	if (status)
+		goto err_free_otg_desc;
 
 	usb_ep_autoconfig_reset(cdev->gadget);
 	usb_composite_overwrite_options(cdev, &coverwrite);
-- 
1.9.1

[toc] | [prev] | [next] | [standalone]


#1325390 — [PATCH v4 14/43] usb: gadget: composite: introduce clear_alt() operation

FromRobert Baldyga <r.baldyga@samsung.com>
Date2016-02-03 14:00 +0100
Subject[PATCH v4 14/43] usb: gadget: composite: introduce clear_alt() operation
Message-ID<qY5co-24p-43@gated-at.bofh.it>
In reply to#1325342
Introduce clear_alt() callback, which is called when prevoiusly set
altsetting is cleared. This can take place in two situations:
- when another altsetting is selected,
- during function disable.

Thanks to symetry to set_alt(), clear_alt() simplifies managing of
resources allocated in set_alt(). It also takes over the function of
disable() opetarion, so it can be removed after converting all USB
functions to new API.

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

diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 0c962d4..1c26443 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -981,6 +981,9 @@ static void disable_interface(struct usb_function *f, unsigned i)
 	for (e = 0; e < alt->eps_num; ++e)
 		usb_ep_disable(alt->eps[e]->ep);
 
+	if (f->clear_alt)
+		f->clear_alt(f, i, intf->cur_altset);
+
 	intf->cur_altset = -1;
 }
 
@@ -992,12 +995,13 @@ static void disable_function(struct usb_function *f)
 {
 	int i;
 
-	if (usb_function_is_new_api(f))
+	if (usb_function_is_new_api(f)) {
 		for (i = 0; i < f->descs->intfs_num; ++i)
 			disable_interface(f, i);
-
-	if (f->disable)
-		f->disable(f);
+	} else {
+		if (f->disable)
+			f->disable(f);
+	}
 
 	bitmap_zero(f->endpoints, 32);
 }
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
index e985f3a..aa55377 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -308,6 +308,8 @@ struct usb_os_desc_table {
  *	initialize usb_ep.driver data at this time (when it is used).
  *	Note that setting an interface to its current altsetting resets
  *	interface state, and that all interfaces have a disabled state.
+ * @clear_alt: (REQUIRED) Clears altsetting, frees all ep requiests and other
+ *	resources allocated by set_alt.
  * @get_alt: Returns the active altsetting.  If this is not provided,
  *	then only altsetting zero is supported.
  * @disable: (REQUIRED) Indicates the function should be disabled.  Reasons
@@ -380,6 +382,8 @@ struct usb_function {
 	/* runtime state management */
 	int			(*set_alt)(struct usb_function *,
 					unsigned interface, unsigned alt);
+	void			(*clear_alt)(struct usb_function *,
+					unsigned interface, unsigned alt);
 	int			(*get_alt)(struct usb_function *,
 					unsigned interface);
 	void			(*disable)(struct usb_function *);
-- 
1.9.1

[toc] | [prev] | [next] | [standalone]


#1325391 — [PATCH v4 11/43] usb: gadget: composite: generate old descs for compatibility

FromRobert Baldyga <r.baldyga@samsung.com>
Date2016-02-03 14:00 +0100
Subject[PATCH v4 11/43] usb: gadget: composite: generate old descs for compatibility
Message-ID<qY5co-24p-41@gated-at.bofh.it>
In reply to#1325342
For now we generate descriptor arrays for each speed as it is done by old
API functions, to allow use mixed new and old API based functions in single
configurations.

This will be removed after complete switch to new API.

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

diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 4f5556b..c34725b 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -2254,6 +2254,30 @@ void composite_free_vendor_descs(struct usb_composite_dev *cdev)
 }
 EXPORT_SYMBOL_GPL(composite_free_vendor_descs);
 
+/**
+ * composite_free_old_descs - for all functions implementing new API free old
+ *	descriptors arrays.
+ * @cdev: composite device
+ */
+void composite_free_old_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) {
+			if (!usb_function_is_new_api(f))
+				continue;
+			kfree(f->fs_descriptors);
+			kfree(f->hs_descriptors);
+			kfree(f->ss_descriptors);
+			f->fs_descriptors = NULL;
+			f->hs_descriptors = NULL;
+			f->ss_descriptors = NULL;
+		}
+}
+EXPORT_SYMBOL_GPL(composite_free_old_descs);
+
 static void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver)
 {
 	struct usb_composite_dev	*cdev = get_gadget_data(gadget);
@@ -2265,6 +2289,7 @@ static void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver)
 	 */
 	WARN_ON(cdev->config);
 
+	composite_free_old_descs(cdev);
 	composite_free_vendor_descs(cdev);
 	composite_free_descs(cdev);
 
@@ -2592,6 +2617,152 @@ int composite_prep_vendor_descs(struct usb_composite_dev *cdev)
 }
 EXPORT_SYMBOL_GPL(composite_prep_vendor_descs);
 
+/*
+ * function_add_desc() - Add given descriptor to descriptor arrays for
+ *	each supported speed, in proper version for each speed.
+ *
+ * @f - USB function
+ * @idx - pointer to index of descriptor in fs and hs array
+ * @idx_ss - pointer to index of descriptor in ss array
+ * @fs - descriptor for FS
+ * @hs - descriptor for HS
+ * @ss - descriptor for SS
+ *
+ * Indexes are automatically incremented.
+ *
+ * This function will be removed after converting all USB functions
+ * in kernel to new API.
+ */
+static inline void function_add_desc(struct usb_function *f,
+		int *idx, int *idx_ss,
+		struct usb_descriptor_header *fs,
+		struct usb_descriptor_header *hs,
+		struct usb_descriptor_header *ss) {
+	if (f->config->fullspeed)
+		f->fs_descriptors[*idx] = fs;
+	if (f->config->highspeed)
+		f->hs_descriptors[*idx] = hs;
+	if (f->config->superspeed)
+		f->ss_descriptors[*idx_ss] = ss;
+	++(*idx);
+	++(*idx_ss);
+}
+
+/*
+ * function_generate_old_descs() - generate descriptors array for each speed
+ *
+ * Allocate arrays of needed size and assign to them USB descriptors.
+ *
+ * This is temporary solution allowing coexistence to both old and new function
+ * API. It will be removed after converting all functions in kernel to new API.
+ */
+static int function_generate_old_descs(struct usb_function *f)
+{
+	struct usb_composite_intf *intf;
+	struct usb_composite_altset *alt;
+	struct usb_composite_ep *ep;
+	struct usb_composite_vendor_desc *vd;
+	int cnt, eps, i, a, e, idx, idx_ss;
+
+	cnt = f->descs->vendor_descs_num;
+	eps = 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];
+			cnt += alt->vendor_descs_num + 1;
+			eps += alt->eps_num;
+			for (e = 0; e < alt->eps_num; ++e)
+				cnt += alt->eps[e]->vendor_descs_num + 1;
+		}
+	}
+
+	if (f->config->fullspeed) {
+		f->fs_descriptors = kzalloc((cnt + 1) *
+				sizeof(*f->fs_descriptors), GFP_KERNEL);
+		if (!f->fs_descriptors)
+			return -ENOMEM;
+	}
+	if (f->config->highspeed) {
+		f->hs_descriptors = kzalloc((cnt + 1) *
+				sizeof(*f->hs_descriptors), GFP_KERNEL);
+		if (!f->hs_descriptors)
+			goto err;
+	}
+	if (f->config->superspeed) {
+		f->ss_descriptors = kzalloc((cnt + eps + 1) *
+				sizeof(*f->ss_descriptors), GFP_KERNEL);
+		if (!f->ss_descriptors)
+			goto err;
+	}
+
+	idx = 0;
+	idx_ss = 0;
+	list_for_each_entry(vd, &f->descs->vendor_descs, list)
+		function_add_desc(f, &idx, &idx_ss,
+				vd->desc, vd->desc, vd->desc);
+	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];
+			function_add_desc(f, &idx, &idx_ss, alt->alt.header,
+					alt->alt.header, alt->alt.header);
+			list_for_each_entry(vd, &alt->vendor_descs, list)
+				function_add_desc(f, &idx, &idx_ss,
+						vd->desc, vd->desc, vd->desc);
+			for (e = 0; e < alt->eps_num; ++e) {
+				ep = alt->eps[e];
+				function_add_desc(f, &idx, &idx_ss,
+						ep->fs.header, ep->hs.header,
+						ep->ss.header);
+				if (f->config->superspeed)
+					f->ss_descriptors[idx_ss++] =
+							ep->ss_comp.header;
+				list_for_each_entry(vd,
+						&ep->vendor_descs, list)
+					function_add_desc(f, &idx, &idx_ss,
+						vd->desc, vd->desc, vd->desc);
+			}
+		}
+	}
+
+	return 0;
+
+err:
+	kfree(f->ss_descriptors);
+	f->ss_descriptors = NULL;
+	kfree(f->hs_descriptors);
+	f->hs_descriptors = NULL;
+	kfree(f->fs_descriptors);
+	f->fs_descriptors = NULL;
+
+	return -ENOMEM;
+}
+
+/*
+ * composite_generate_old_descs() - generate descriptors arrays for each
+ *	function in each configuraion
+ */
+int composite_generate_old_descs(struct usb_composite_dev *cdev)
+{
+	struct usb_configuration *c;
+	struct usb_function *f;
+	int ret;
+
+	list_for_each_entry(c, &cdev->configs, list)
+		list_for_each_entry(f, &c->functions, list) {
+			if (!usb_function_is_new_api(f))
+				continue;
+			ret = function_generate_old_descs(f);
+			if (ret)
+				return ret;
+		}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(composite_generate_old_descs);
+
 static int composite_bind(struct usb_gadget *gadget,
 		struct usb_gadget_driver *gdriver)
 {
@@ -2626,6 +2797,10 @@ static int composite_bind(struct usb_gadget *gadget,
 	if (status < 0)
 		goto fail;
 
+	status = composite_generate_old_descs(cdev);
+	if (status < 0)
+		goto fail;
+
 	if (cdev->use_os_string) {
 		status = composite_os_desc_req_prepare(cdev, gadget->ep0);
 		if (status)
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
index 0db473f..e985f3a 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -600,9 +600,11 @@ extern int composite_os_desc_req_prepare(struct usb_composite_dev *cdev,
 void composite_dev_cleanup(struct usb_composite_dev *cdev);
 
 int composite_prep_vendor_descs(struct usb_composite_dev *cdev);
+int composite_generate_old_descs(struct usb_composite_dev *cdev);
 
 void composite_free_descs(struct usb_composite_dev *cdev);
 void composite_free_vendor_descs(struct usb_composite_dev *cdev);
+void composite_free_old_descs(struct usb_composite_dev *cdev);
 
 static inline struct usb_composite_driver *to_cdriver(
 		struct usb_gadget_driver *gdrv)
-- 
1.9.1

[toc] | [prev] | [next] | [standalone]


#1325392 — [PATCH v4 07/43] usb: gadget: composite: add functions for descriptors handling

FromRobert Baldyga <r.baldyga@samsung.com>
Date2016-02-03 14:00 +0100
Subject[PATCH v4 07/43] usb: gadget: composite: add functions for descriptors handling
Message-ID<qY5co-24p-45@gated-at.bofh.it>
In reply to#1325342
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@samsung.com>
---
 drivers/usb/gadget/composite.c | 343 +++++++++++++++++++++++++++++++++++++++++
 include/linux/usb/composite.h  |  52 +++++++
 2 files changed, 395 insertions(+)

diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 65abc24..3f9cad8 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -327,6 +327,314 @@ 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;
+
+	if (!descs->intfs_num)
+		return -EINVAL;
+
+	size = sizeof(*descs);
+
+	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);
+				if (ep->hs.desc)
+					size += sizeof(*ep->hs.desc);
+				if (ep->ss.desc) {
+					if (!ep->ss_comp.desc)
+						return -EINVAL;
+					size += sizeof(*ep->ss.desc) +
+						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));
+					descs_c->fullspeed = true;
+				}
+				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));
+					descs_c->highspeed = true;
+				}
+				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));
+					descs_c->superspeed = true;
+				}
+				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,
+		const 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);
+	f->descs->vendor_descs_num++;
+
+	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,
+		const 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);
+	alt->vendor_descs_num++;
+
+	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,
+		const 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[a]->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);
+	ep->vendor_descs_num++;
+
+	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 +2201,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 +2244,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 8bb5c35..91948c7 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -178,6 +178,43 @@ struct usb_composite_descs {
 	unsigned superspeed:1;
 };
 
+/*
+ * 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
@@ -363,6 +400,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,
+		const struct usb_descriptor_header *desc);
+
+int usb_altset_add_vendor_desc(struct usb_function *f, int i, int a,
+		const struct usb_descriptor_header *desc);
+
+int usb_ep_add_vendor_desc(struct usb_function *f, int i, int a, int e,
+		const 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,
@@ -539,6 +588,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

[toc] | [prev] | [standalone]


Page 3 of 3 — ← Prev page 1 2 [3]

Back to top | Article view | linux.kernel


csiph-web