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


Groups > linux.kernel > #1278738 > unrolled thread

[PATCH v2 00/29] usb: gadget: composite: introduce new function API

Started byRobert Baldyga <r.baldyga@samsung.com>
First post2015-11-27 12:10 +0100
Last post2015-11-27 12:10 +0100
Articles 10 — 1 participant

Back to article view | Back to linux.kernel


Contents

  [PATCH v2 00/29] usb: gadget: composite: introduce new function API Robert Baldyga <r.baldyga@samsung.com> - 2015-11-27 12:10 +0100
    [PATCH v2 04/29] usb: gadget: configfs: fix error path Robert Baldyga <r.baldyga@samsung.com> - 2015-11-27 12:10 +0100
    [PATCH v2 24/29] usb: gadget: f_hid: conversion to new API Robert Baldyga <r.baldyga@samsung.com> - 2015-11-27 12:10 +0100
    [PATCH v2 05/29] usb: gadget: composite: introduce new descriptors  format Robert Baldyga <r.baldyga@samsung.com> - 2015-11-27 12:10 +0100
    [PATCH v2 02/29] usb: gadget: f_sourcesink: free requests in  sourcesink_disable() Robert Baldyga <r.baldyga@samsung.com> - 2015-11-27 12:10 +0100
    [PATCH v2 26/29] usb: gadget: f_eem: conversion to new API Robert Baldyga <r.baldyga@samsung.com> - 2015-11-27 12:10 +0100
    [PATCH v2 20/29] usb: gadget: f_sourcesink: convert to new API Robert Baldyga <r.baldyga@samsung.com> - 2015-11-27 12:10 +0100
    [PATCH v2 08/29] usb: gadget: composite: handle function bind Robert Baldyga <r.baldyga@samsung.com> - 2015-11-27 12:10 +0100
    [PATCH v2 29/29] usb: gadget: f_serial: conversion to new API Robert Baldyga <r.baldyga@samsung.com> - 2015-11-27 12:10 +0100
    [PATCH v2 19/29] usb: gadget: f_loopback: convert to new API Robert Baldyga <r.baldyga@samsung.com> - 2015-11-27 12:10 +0100

#1278738 — [PATCH v2 00/29] usb: gadget: composite: introduce new function API

FromRobert Baldyga <r.baldyga@samsung.com>
Date2015-11-27 12:10 +0100
Subject[PATCH v2 00/29] usb: gadget: composite: introduce new function API
Message-ID<qzp4B-243-3@gated-at.bofh.it>
Hi Felipe,

Here is my new patch series doing some changes in composite framework
and modifying USB Function API. Some of concepts changed significantly,
for example bind process is done automatically inside composite framework
after collecting descriptors from all Functions. Hence bind() operation
of USB Function has been replaced with prep_descs(). Besides the other
benefits, such as simple implementation of gadget-level autoconfig solver,
changes in API allowed to simplify code of USB Functions, which contain
lots of boilerplate code.

First five patches of this series are fixes or improvements, being
preparation for further changes. Patches from 6 to 19 add implementation
of new features. Some code allowing coexistence of both old and new API
is added. This code will be removed after converting all USB Functions
present in kernel to new API.
Last ten patches converts Functions: loopback, sourcesink, ecm, rndis,
hid, acm, eem, ncm, printer and serial to new API. Conversion of another
Functions will be done soon.

*** What has changed? ***

The main changes are listed below:
A. Introduce new descriptors format. It makes descriptors creation process
   simpler plus creates good place to contain additional information such
   as result of automatic bind or actually selected altsetting.
B. Split descriptors creation process into two stages, implemented by
   two new operations:
   - prep_descs() provide entity descriptors (interfaces, altsettings
     and endpoints)
   - prep_vendor_descs() provide class and vendor specific descriptors.
   The first one is called before binding funciton to UDC and it's
   mandatory, because it provides information needed during bind process.
   The second one is optional and a Function can implement it if it wants
   to attach some class or vendor specific descriptors. It's called after
   bind process, so from it's context all information about interface
   numbers and endpoint addresses is accessible.
C. Perform bind automatically inside composite framework after collecting
   descriptors from all USB Functions. Besides removing lots of repetitive
   code from USB Functions, it gives us two main advantages:
   - We can have gadget-level autoconfig solver providing better endpoint
     resources usage. We can choose best endpoint configuration for all
     Functions in all configurations.
   - We have composite driver structure creation process separated from
     bind process which allows to modify configfs to operate directly
     on composite driver state - both legacy gadgets and configfs can
     use common composite driver creation process.
   Function allowing to obtain endpoints after bind process is provided,
   and it should be called in set_alt().
D. Replace disable() operation with more powerful clear_alt(). It is
   called when Function is being disabled or when altsetting being
   selected on interface which already has active altsetting. It makes
   API more symmetric, which greatly simplifies resource management.
E. Handle endpoint enable/disable automatically, which means, that in
   set_alt() we obtain set of already enabled endpoints for current
   altsetting. Likewise in clear_alt() endpoints are already disabled.
F. Change meaning of second parameter of set_alt() operation. Now it
   contains index of interface within desctiptors array of given USB
   Function instead of bInterfaceNumber of this interface, which
   simplifies altsetting handling (so far it was necessary to compare
   this value with bInterfaceNumber of each interface to find out which
   altsetting of which interface is being selected).
G. Handle get_alt() automatically. Currently selected altsetting number
   is stored for each interface.

*** How did it work before? ***

So far USB Functions had to handle bind process manually and deal with
endpoints state explicitly, which has been making code lengthy and
bug-prone. USB Functions contained lots of repetitive code which was
usually copied while creating new USB Function module. This resulted
with lots of boilerplate code scattered across all Functions present
in Linux kernel.

BIND:

During bind process we had to obtain interface id manually and assign
it to each interface descriptor (altsetting) of given interface. We also
had to obtain endpoints manually using usb_ep_autoconfig(). Beside its
verbosity, this solution resulted with suboptimal endpoints distribution,
because autoconfig algorithm was aware of requirements of only single
endpoint at a time.

udc_bind_to_driver() {
  composite_bind() {
    configuration1->bind() {
      function1->bind() {
        intf1_id = usb_interface_id(); // Obtain intf id manually
        ep1 = usb_ep_autoconfig(); // Endpoint-level autoconfig
        ep2 = usb_ep_autoconfig();
        intf2_id = usb_interface_id();
        ep3 = usb_ep_autoconfig();
        ep4 = usb_ep_autoconfig();
      }
      function2->bind() {
	intf1_id = usb_interface_id();
        ep1 = usb_ep_autoconfig();
        ep2 = usb_ep_autoconfig();
        ep3 = usb_ep_autoconfig();
      }
      function3->bind() {
        intf1_id = usb_interface_id();
        ep1 = usb_ep_autoconfig();
        intf2_id = usb_interface_id();
        ep2 = usb_ep_autoconfig();
      }
    }
    configuration2->bind() {
      function1->bind() {
        intf1_id = usb_interface_id();
        ep1 = usb_ep_autoconfig();
      }
      function2->bind() {
        intf1_id = usb_interface_id();
        ep1 = usb_ep_autoconfig();
      }
    }
  }
}

SET_ALT:

In set_alt() we had to guess if any altsetting for given interface had
been selected before or not. In fact many functions have been doing this
by storing some information in driver_data field of endpoints or simply
by doing interface reset regardless of previous state. We also needed
to guess for which interface set_alt() has been called, because interface
number passed to this callback was the interface index in configuration,
not index in function descriptors. It has been making set_alt() handling
quite problematic.

function1->set_alt() {
  id = detect_intf_id();
  if (id == intf1_id) {
    intf_specific_disable(); // Disable everything just in case
    usb_ep_disable(ep1); // Disable endpoint manually
    usb_ep_disable(ep2);
    config_ep_by_speed(ep1); // Configure endpoint manually
    config_ep_by_speed(ep2);
    usb_ep_enable(ep1); // Enable endpoint manually
    usb_ep_enable(ep2);
    intf_specific_enable();
  } else {
    intf_specific_disable();
    usb_ep_disable(ep3);
    usb_ep_disable(ep4);
    config_ep_by_speed(ep3);
    config_ep_by_speed(ep4);
    usb_ep_enable(ep3);
    usb_ep_enable(ep4);
    intf_specific_enable();
  }
}
function2->set_alt() {
  function_specific_disable();
  usb_ep_disable(ep1);
  usb_ep_disable(ep2);
  config_ep_by_speed(ep1);
  config_ep_by_speed(ep2);
  usb_ep_enable(ep1);
  usb_ep_enable(ep2);
  function_specific_enable();
}

DISABLE:

The disable() callback has been called only when entire Function had
being disabled. In this function we had to deactivate all functionality
and disable all endpoints manually.

function1->disable() {
  function_specific_disable();
  usb_ep_disable(ep1); // Disable endpoint manually
  usb_ep_disable(ep2);
  usb_ep_disable(ep3);
  usb_ep_disable(ep4);
}
function2->disable() {
  function_specific_disable();
  usb_ep_disable(ep1);
  usb_ep_disable(ep2);
}

*** How does it work now? ***

BIND:

Bind process is done entirely by composite framework internals. To
achieve this, composite needs to have a knowledge about interfaces and
endpoints which the Function is comprised of. This knowledge is provided
by prep_descs() callback which assigns descriptors needed for bind process
to Function. Having all descriptors collected allows to implement
configuration-level or even gadget-level autoconfig solver. This solver
could, basing on information from descriptors and endpoint capabilities
of UDC hardware, which gadget driver is being bound to, distribute
endpoints over interfaces in much more optimal way than it has been done
so far. At the end, after binding gadget to UDC hardware,
prep_vendor_descs() callback is invoked for each Function to allow it
to provide some class and vendor specific descriptors.

udc_bind_to_driver() {
  composite_bind() {
    configuration1->bind() {
      function1->prep_descs() { // Gather descriptors
        usb_function_set_descs(); // Set descs to Function
      }
      function2->prep_descs() {
        usb_function_set_descs();
      }
      function3->prep_descs() {
        usb_function_set_descs();
      }
    }
    usb_config_do_bind(); // Bind entire configuration

    configuration2->bind() {
      function1->prep_descs() {
        usb_function_set_descs();
      }
      function2->prep_descs() {
        usb_function_set_descs();
      }
    }
    usb_config_do_bind();

    composite_prep_vendor_descs(); // Gather class and vendor descs
  }
}

SET_ALT:

In set_alt() function we have to obtain endpoints for currently selected
altsetting. Endpoints are already configured and enabled. Interface number
passed to set_alt() is its index within Function descriptors array, which
simplifies set_alt() handling. We also don't need to remember if any
altsetting has been previously selected, because in such situation
clear_alt() is called before set_alt(), to allow function to cleanup
interface state.

function1->set_alt() {
  if (intf == 0) {
    ep1 = usb_function_get_ep();
    ep2 = usb_function_get_ep();
    intf_specific_enable();
  } else {
    ep3 = usb_function_get_ep();
    ep4 = usb_function_get_ep();
    intf_specific_enable();
  }
}
function2->set_alt() {
  ep1 = usb_function_get_ep();
  ep2 = usb_function_get_ep();
  function_specific_enable();
}

CLEAR_ALT:

In clear_alt() callback function can clear interface state and free
resources allocated in set_alt(). It's called before selecting altsetting
in interface which has already selected active altsetting, or when
function is being disabled. Thanks to this clear_alt() can be used as
an enhanced replacement of disable() operation.

function1->clear_alt() {
  if (intf == 0) {
    intf_specific_disable();
  } else {
    intf_specific_disable();
  }
}
function2->clear_alt() {
  function_specific_disable();
}

*** What's next? ***

The next step is to convert all Functions to new API and cleanup composite
code. Then it will be possible to implement intelligent configuration-level
autoconfig solver. We can also try to implement gadget-level autoconfig
solver which could be capable to reconfigure UDC hardware according to
requirements of specific gadget driver.

Thanks to separation of bind process from composite driver creation
process (adding function to configuration doesn't involve its bind, so
it can be done before hardware is available), we can also simplify
configfs gadget implementation. We can make legacy gadgets and configfs
using common composite driver creation process.

I believe it's also possible to make descriptors creation process less
verbose by providing set of macros/functions dedicated for this purpose
(now descriptors take at average over 200 lines of code per Function).

I have some WIP version of patches in which I'm doing part of changes
mentioned above. I can send them as RFC to show what is final result
which I want to achieve.

Best regards,
Robert Baldyga

Changelog:

v2:
- Addressed comments from Sergei
- Added 6 new patches converting Functions to new API

v1: https://lkml.org/lkml/2015/11/3/288

Robert Baldyga (29):
  usb: gadget: f_sourcesink: make ISO altset user-selectable
  usb: gadget: f_sourcesink: free requests in sourcesink_disable()
  usb: gadget: f_loopback: free requests in loopback_disable()
  usb: gadget: configfs: fix error path
  usb: gadget: composite: introduce new descriptors format
  usb: gadget: composite: add functions for descriptors handling
  usb: gadget: composite: introduce new USB function ops
  usb: gadget: composite: handle function bind
  usb: gadget: composite: handle vendor descs
  usb: gadget: composite: generate old descs for compatibility
  usb: gadget: composite: disable eps before calling disable() callback
  usb: gadget: composite: enable eps before calling set_alt() callback
  usb: gadget: composite: introduce clear_alt() operation
  usb: gadget: composite: handle get_alt() automatically
  usb: gadget: composite: add usb_function_get_ep() function
  usb: gadget: composite: add usb_get_interface_id() function
  usb: gadget: composite: enable adding USB functions using new API
  usb: gadget: configfs: add new composite API support
  usb: gadget: f_loopback: convert to new API
  usb: gadget: f_sourcesink: convert to new API
  usb: gadget: f_ecm: conversion to new API
  usb: gadget: f_rndis: conversion to new API
  usb: gadget: f_hid: handle requests lifetime properly
  usb: gadget: f_hid: conversion to new API
  usb: gadget: f_acm: conversion to new API
  usb: gadget: f_eem: conversion to new API
  usb: gadget: f_ncm: conversion to new API
  usb: gadget: f_printer: conversion to new API
  usb: gadget: f_serial: conversion to new API

 drivers/usb/gadget/composite.c             | 947 ++++++++++++++++++++++++++++-
 drivers/usb/gadget/configfs.c              |  24 +-
 drivers/usb/gadget/function/f_acm.c        | 248 +++-----
 drivers/usb/gadget/function/f_ecm.c        | 321 +++-------
 drivers/usb/gadget/function/f_eem.c        | 154 +----
 drivers/usb/gadget/function/f_hid.c        | 307 ++++------
 drivers/usb/gadget/function/f_loopback.c   | 218 ++-----
 drivers/usb/gadget/function/f_ncm.c        | 320 ++++------
 drivers/usb/gadget/function/f_printer.c    | 300 +++------
 drivers/usb/gadget/function/f_rndis.c      | 321 ++++------
 drivers/usb/gadget/function/f_serial.c     | 122 +---
 drivers/usb/gadget/function/f_sourcesink.c | 415 +++++--------
 drivers/usb/gadget/function/g_zero.h       |   6 +-
 drivers/usb/gadget/legacy/zero.c           |  12 +
 include/linux/usb/composite.h              | 194 ++++++
 15 files changed, 2025 insertions(+), 1884 deletions(-)

-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [next] | [standalone]


#1278739 — [PATCH v2 04/29] usb: gadget: configfs: fix error path

FromRobert Baldyga <r.baldyga@samsung.com>
Date2015-11-27 12:10 +0100
Subject[PATCH v2 04/29] usb: gadget: configfs: fix error path
Message-ID<qzp4E-243-63@gated-at.bofh.it>
In reply to#1278738
As usb_gstrings_attach() failure can happen when some USB functions are
are already added to some configurations (in previous loop iterations),
we should always call purge_configs_funcs() to be sure that failure is
be handled properly.

Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
---
 drivers/usb/gadget/configfs.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index 163d305..0557f80 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -1342,7 +1342,7 @@ static int configfs_composite_bind(struct usb_gadget *gadget,
 			s = usb_gstrings_attach(&gi->cdev, cfg->gstrings, 1);
 			if (IS_ERR(s)) {
 				ret = PTR_ERR(s);
-				goto err_comp_cleanup;
+				goto err_purge_funcs;
 			}
 			c->iConfiguration = s[0].id;
 		}
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

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


#1278740 — [PATCH v2 24/29] usb: gadget: f_hid: conversion to new API

FromRobert Baldyga <r.baldyga@samsung.com>
Date2015-11-27 12:10 +0100
Subject[PATCH v2 24/29] usb: gadget: f_hid: conversion to new API
Message-ID<qzp4E-243-61@gated-at.bofh.it>
In reply to#1278738
Generate descriptors in new format and attach them to USB function in
prep_descs(). Implement prep_vendor_descs() to supply class specific
descriptors. Change set_alt() implementation and implement clear_alt()
operation. Move cdev initialization to hidg_alloc(). Remove boilerplate
code.

Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
---
 drivers/usb/gadget/function/f_hid.c | 305 ++++++++++++++----------------------
 1 file changed, 119 insertions(+), 186 deletions(-)

diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index 0456a53..8770289 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -125,14 +125,6 @@ static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = {
 				      */
 };
 
-static struct usb_descriptor_header *hidg_hs_descriptors[] = {
-	(struct usb_descriptor_header *)&hidg_interface_desc,
-	(struct usb_descriptor_header *)&hidg_desc,
-	(struct usb_descriptor_header *)&hidg_hs_in_ep_desc,
-	(struct usb_descriptor_header *)&hidg_hs_out_ep_desc,
-	NULL,
-};
-
 /* Full-Speed Support */
 
 static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = {
@@ -159,13 +151,16 @@ static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = {
 				       */
 };
 
-static struct usb_descriptor_header *hidg_fs_descriptors[] = {
-	(struct usb_descriptor_header *)&hidg_interface_desc,
-	(struct usb_descriptor_header *)&hidg_desc,
-	(struct usb_descriptor_header *)&hidg_fs_in_ep_desc,
-	(struct usb_descriptor_header *)&hidg_fs_out_ep_desc,
-	NULL,
-};
+USB_COMPOSITE_ENDPOINT(ep_in, &hidg_fs_in_ep_desc,
+		&hidg_hs_in_ep_desc, NULL, NULL);
+USB_COMPOSITE_ENDPOINT(ep_out, &hidg_fs_out_ep_desc,
+		&hidg_hs_out_ep_desc, NULL, NULL);
+
+USB_COMPOSITE_ALTSETTING(intf0alt0, &hidg_interface_desc, &ep_in, &ep_out);
+
+USB_COMPOSITE_INTERFACE(intf0, &intf0alt0);
+
+USB_COMPOSITE_DESCRIPTORS(hidg_descs, &intf0);
 
 /*-------------------------------------------------------------------------*/
 /*                                 Strings                                 */
@@ -487,27 +482,6 @@ respond:
 	return status;
 }
 
-static void hidg_disable(struct usb_function *f)
-{
-	struct f_hidg *hidg = func_to_hidg(f);
-	struct f_hidg_req_list *list, *next;
-	int i;
-
-	usb_ep_disable(hidg->in_ep);
-	usb_ep_disable(hidg->out_ep);
-
-	list_for_each_entry_safe(list, next, &hidg->completed_out_req, list) {
-		list_del(&list->list);
-		kfree(list);
-	}
-
-	for (i = 0; i < hidg->qlen; ++i) {
-		kfree(hidg->out_reqs[i]->buf);
-		kfree(hidg->out_reqs[i]);
-	}
-	kfree(hidg->out_reqs);
-}
-
 static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
 {
 	struct usb_composite_dev		*cdev = f->config->cdev;
@@ -516,65 +490,46 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
 
 	VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt);
 
-	if (hidg->in_ep != NULL) {
-		/* restart endpoint */
-		usb_ep_disable(hidg->in_ep);
+	hidg->in_ep = usb_function_get_ep(f, intf, 0);
+	if (!hidg->in_ep)
+		return -ENODEV;
+	hidg->in_ep->driver_data = hidg;
 
-		status = config_ep_by_speed(f->config->cdev->gadget, f,
-					    hidg->in_ep);
-		if (status) {
-			ERROR(cdev, "config_ep_by_speed FAILED!\n");
-			goto fail;
-		}
-		status = usb_ep_enable(hidg->in_ep);
-		if (status < 0) {
-			ERROR(cdev, "Enable IN endpoint FAILED!\n");
-			goto fail;
-		}
-		hidg->in_ep->driver_data = hidg;
-	}
+	hidg->out_ep = usb_function_get_ep(f, intf, 1);
+	if (!hidg->out_ep)
+		return -ENODEV;
+	hidg->out_ep->driver_data = hidg;
 
+	/* preallocate request and buffer */
+	hidg->req = usb_ep_alloc_request(hidg->in_ep, GFP_KERNEL);
+	if (!hidg->req)
+		return -ENOMEM;
 
-	if (hidg->out_ep != NULL) {
-		/* restart endpoint */
-		usb_ep_disable(hidg->out_ep);
+	hidg->req->buf = kmalloc(hidg->report_length, GFP_KERNEL);
+	if (!hidg->req->buf)
+		return -ENOMEM;
 
-		status = config_ep_by_speed(f->config->cdev->gadget, f,
-					    hidg->out_ep);
-		if (status) {
-			ERROR(cdev, "config_ep_by_speed FAILED!\n");
-			goto fail;
-		}
-		status = usb_ep_enable(hidg->out_ep);
-		if (status < 0) {
-			ERROR(cdev, "Enable IN endpoint FAILED!\n");
-			goto fail;
-		}
-		hidg->out_ep->driver_data = hidg;
-
-		/*
-		 * allocate a bunch of read buffers and queue them all at once.
-		 */
-		hidg->out_reqs = kzalloc(hidg->qlen *
-				sizeof(*hidg->out_reqs), GFP_KERNEL);
-		for (i = 0; i < hidg->qlen && status == 0; i++) {
-			struct usb_request *req =
-					hidg_alloc_ep_req(hidg->out_ep,
-							  hidg->report_length);
-			if (req) {
+	/*
+	 * allocate a bunch of read buffers and queue them all at once.
+	 */
+	hidg->out_reqs = kzalloc(hidg->qlen *
+			sizeof(*hidg->out_reqs), GFP_KERNEL);
+	for (i = 0; i < hidg->qlen && status == 0; i++) {
+		struct usb_request *req =
+			hidg_alloc_ep_req(hidg->out_ep,
+					hidg->report_length);
+		if (req) {
 				hidg->out_reqs[i] = req;
-				req->complete = hidg_set_report_complete;
-				req->context  = hidg;
-				status = usb_ep_queue(hidg->out_ep, req,
-						      GFP_ATOMIC);
-				if (status)
-					ERROR(cdev, "%s queue req --> %d\n",
+			req->complete = hidg_set_report_complete;
+			req->context  = hidg;
+			status = usb_ep_queue(hidg->out_ep, req,
+					GFP_ATOMIC);
+			if (status)
+				ERROR(cdev, "%s queue req --> %d\n",
 						hidg->out_ep->name, status);
-			} else {
-				usb_ep_disable(hidg->out_ep);
-				status = -ENOMEM;
-				goto free_req;
-			}
+		} else {
+			status = -ENOMEM;
+			goto free_req;
 		}
 	}
 
@@ -587,10 +542,31 @@ free_req:
 		kfree(hidg->out_reqs);
 	}
 
-fail:
 	return status;
 }
 
+static void hidg_clear_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+	struct f_hidg *hidg = func_to_hidg(f);
+	struct f_hidg_req_list *list, *next;
+	int i;
+
+	list_for_each_entry_safe(list, next, &hidg->completed_out_req, list) {
+		list_del(&list->list);
+		kfree(list);
+	}
+
+	for (i = 0; i < hidg->qlen; ++i) {
+		kfree(hidg->out_reqs[i]->buf);
+		kfree(hidg->out_reqs[i]);
+	}
+	kfree(hidg->out_reqs);
+
+	/* disable/free request and end point */
+	kfree(hidg->req->buf);
+	usb_ep_free_request(hidg->in_ep, hidg->req);
+}
+
 static const struct file_operations f_hidg_fops = {
 	.owner		= THIS_MODULE,
 	.open		= f_hidg_open,
@@ -601,50 +577,19 @@ static const struct file_operations f_hidg_fops = {
 	.llseek		= noop_llseek,
 };
 
-static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
+static int hidg_prep_descs(struct usb_function *f)
 {
-	struct usb_ep		*ep;
+	struct usb_composite_dev *cdev = f->config->cdev;
 	struct f_hidg		*hidg = func_to_hidg(f);
 	struct usb_string	*us;
-	struct device		*device;
-	int			status;
-	dev_t			dev;
 
 	/* maybe allocate device-global string IDs, and patch descriptors */
-	us = usb_gstrings_attach(c->cdev, ct_func_strings,
+	us = usb_gstrings_attach(cdev, ct_func_strings,
 				 ARRAY_SIZE(ct_func_string_defs));
 	if (IS_ERR(us))
 		return PTR_ERR(us);
 	hidg_interface_desc.iInterface = us[CT_FUNC_HID_IDX].id;
 
-	/* allocate instance-specific interface IDs, and patch descriptors */
-	status = usb_interface_id(c, f);
-	if (status < 0)
-		goto fail;
-	hidg_interface_desc.bInterfaceNumber = status;
-
-	/* allocate instance-specific endpoints */
-	status = -ENODEV;
-	ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_in_ep_desc);
-	if (!ep)
-		goto fail;
-	hidg->in_ep = ep;
-
-	ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_out_ep_desc);
-	if (!ep)
-		goto fail;
-	hidg->out_ep = ep;
-
-	/* preallocate request and buffer */
-	status = -ENOMEM;
-	hidg->req = usb_ep_alloc_request(hidg->in_ep, GFP_KERNEL);
-	if (!hidg->req)
-		goto fail;
-
-	hidg->req->buf = kmalloc(hidg->report_length, GFP_KERNEL);
-	if (!hidg->req->buf)
-		goto fail;
-
 	/* set descriptor dynamic values */
 	hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass;
 	hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol;
@@ -652,6 +597,14 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
 	hidg_fs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
 	hidg_hs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
 	hidg_fs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
+
+	return usb_function_set_descs(f, &hidg_descs);
+}
+
+static int hidg_prep_vendor_descs(struct usb_function *f)
+{
+	struct f_hidg		*hidg = func_to_hidg(f);
+
 	/*
 	 * We can use hidg_desc struct here but we should not relay
 	 * that its content won't change after returning from this function.
@@ -660,50 +613,10 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
 	hidg_desc.desc[0].wDescriptorLength =
 		cpu_to_le16(hidg->report_desc_length);
 
-	hidg_hs_in_ep_desc.bEndpointAddress =
-		hidg_fs_in_ep_desc.bEndpointAddress;
-	hidg_hs_out_ep_desc.bEndpointAddress =
-		hidg_fs_out_ep_desc.bEndpointAddress;
-
-	status = usb_assign_descriptors(f, hidg_fs_descriptors,
-			hidg_hs_descriptors, NULL);
-	if (status)
-		goto fail;
-
-	mutex_init(&hidg->lock);
-	spin_lock_init(&hidg->spinlock);
-	init_waitqueue_head(&hidg->write_queue);
-	init_waitqueue_head(&hidg->read_queue);
-	INIT_LIST_HEAD(&hidg->completed_out_req);
-
-	/* create char device */
-	cdev_init(&hidg->cdev, &f_hidg_fops);
-	dev = MKDEV(major, hidg->minor);
-	status = cdev_add(&hidg->cdev, dev, 1);
-	if (status)
-		goto fail_free_descs;
-
-	device = device_create(hidg_class, NULL, dev, NULL,
-			       "%s%d", "hidg", hidg->minor);
-	if (IS_ERR(device)) {
-		status = PTR_ERR(device);
-		goto del;
-	}
+	usb_altset_add_vendor_desc(f, 0, 0,
+			(struct usb_descriptor_header *)&hidg_desc);
 
 	return 0;
-del:
-	cdev_del(&hidg->cdev);
-fail_free_descs:
-	usb_free_all_descriptors(f);
-fail:
-	ERROR(f->config->cdev, "hidg_bind FAILED\n");
-	if (hidg->req != NULL) {
-		kfree(hidg->req->buf);
-		if (hidg->in_ep != NULL)
-			usb_ep_free_request(hidg->in_ep, hidg->req);
-	}
-
-	return status;
 }
 
 static inline int hidg_get_minor(void)
@@ -914,6 +827,10 @@ static void hidg_free(struct usb_function *f)
 
 	hidg = func_to_hidg(f);
 	opts = container_of(f->fi, struct f_hid_opts, func_inst);
+
+	device_destroy(hidg_class, MKDEV(major, hidg->minor));
+	cdev_del(&hidg->cdev);
+
 	kfree(hidg->report_desc);
 	kfree(hidg);
 	mutex_lock(&opts->lock);
@@ -921,31 +838,25 @@ static void hidg_free(struct usb_function *f)
 	mutex_unlock(&opts->lock);
 }
 
-static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
-{
-	struct f_hidg *hidg = func_to_hidg(f);
-
-	device_destroy(hidg_class, MKDEV(major, hidg->minor));
-	cdev_del(&hidg->cdev);
-
-	/* disable/free request and end point */
-	usb_ep_disable(hidg->in_ep);
-	kfree(hidg->req->buf);
-	usb_ep_free_request(hidg->in_ep, hidg->req);
-
-	usb_free_all_descriptors(f);
-}
-
 static struct usb_function *hidg_alloc(struct usb_function_instance *fi)
 {
 	struct f_hidg *hidg;
 	struct f_hid_opts *opts;
+	struct device *device;
+	dev_t dev;
+	int ret;
 
 	/* allocate and initialize one new instance */
 	hidg = kzalloc(sizeof(*hidg), GFP_KERNEL);
 	if (!hidg)
 		return ERR_PTR(-ENOMEM);
 
+	mutex_init(&hidg->lock);
+	spin_lock_init(&hidg->spinlock);
+	init_waitqueue_head(&hidg->write_queue);
+	init_waitqueue_head(&hidg->read_queue);
+	INIT_LIST_HEAD(&hidg->completed_out_req);
+
 	opts = container_of(fi, struct f_hid_opts, func_inst);
 
 	mutex_lock(&opts->lock);
@@ -961,26 +872,48 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi)
 					    opts->report_desc_length,
 					    GFP_KERNEL);
 		if (!hidg->report_desc) {
-			kfree(hidg);
 			mutex_unlock(&opts->lock);
-			return ERR_PTR(-ENOMEM);
+			ret = -ENOMEM;
+			goto err_hidg;
 		}
 	}
 
 	mutex_unlock(&opts->lock);
 
 	hidg->func.name    = "hid";
-	hidg->func.bind    = hidg_bind;
-	hidg->func.unbind  = hidg_unbind;
+	hidg->func.prep_descs = hidg_prep_descs;
+	hidg->func.prep_vendor_descs = hidg_prep_vendor_descs;
 	hidg->func.set_alt = hidg_set_alt;
-	hidg->func.disable = hidg_disable;
+	hidg->func.clear_alt = hidg_clear_alt;
 	hidg->func.setup   = hidg_setup;
 	hidg->func.free_func = hidg_free;
 
 	/* this could me made configurable at some point */
 	hidg->qlen	   = 4;
 
+	/* create char device */
+	cdev_init(&hidg->cdev, &f_hidg_fops);
+	dev = MKDEV(major, hidg->minor);
+	ret = cdev_add(&hidg->cdev, dev, 1);
+	if (ret < 0)
+		goto err_report;
+
+	device = device_create(hidg_class, NULL, dev, NULL,
+			       "%s%d", "hidg", hidg->minor);
+	if (IS_ERR(device)) {
+		ret = PTR_ERR(device);
+		goto err_cdev;
+	}
+
 	return &hidg->func;
+
+err_cdev:
+	cdev_del(&hidg->cdev);
+err_report:
+	kfree(hidg->report_desc);
+err_hidg:
+	kfree(hidg);
+	return ERR_PTR(ret);
 }
 
 DECLARE_USB_FUNCTION_INIT(hid, hidg_alloc_inst, hidg_alloc);
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

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


#1278741 — [PATCH v2 05/29] usb: gadget: composite: introduce new descriptors format

FromRobert Baldyga <r.baldyga@samsung.com>
Date2015-11-27 12:10 +0100
Subject[PATCH v2 05/29] usb: gadget: composite: introduce new descriptors format
Message-ID<qzp4E-243-65@gated-at.bofh.it>
In reply to#1278738
Introduce new structures designed to contain information about
descriptors. It splits descriptors in two categories:
1. Entity descs - interface and endpoint descriptors
2. Vendor descs - all other vendor and class specific descriptors

Entity descriptors are embedded in hierarchy of structures while vendor
descriptors are contained in linked lists. This distinction is caused
by fact, that entity descriptors are needed during gadget bind procedure,
while vendor descriptors can be supplied later, which is usually desired,
as these descriptors may need to be filled with interface numbers and
endpoint addresses which are assigned during gadget bind.

In result we can split descriptors creation process in two steps - first
collecs entity descriptors, perform the bind and then update and attach
all other descriptors. This process can be done this way not only for
each function separately, but also for entire gadget at once, which means
we can first gather descriptors from all functions in gadget, next perform
bind procedure, and then allow functions to supply additional descriptors.

It allows us to have autoconfig solver capable to better distibute ep
resources, and additionally, because we now store information about
endpoints, allows us to handle endpoint state inside composite framework,
and in result remove lots of boilerplate code from USB functions.

Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
---
 include/linux/usb/composite.h | 119 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 119 insertions(+)

diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
index 1074b89..686c5f7 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -57,6 +57,121 @@
 struct usb_configuration;
 
 /**
+ * struct usb_composite_vendor_desc - vendor specific descriptor
+ * @desc: pointer to vendor specific descriptor
+ * @list: descriptor list element
+ *
+ * It's designed to be element of vendor specific descriptor list,
+ * which can be attached to function, interface (per altsetting) or
+ * endpoint.
+ */
+struct usb_composite_vendor_desc {
+	struct usb_descriptor_header *desc;
+	struct list_head list;
+};
+
+/**
+ * struct usb_composite_ep - representation of USB endpoint
+ * @fs.desc: FullSpeed descriptor
+ * @hs.desc: HighSpeed descriptor
+ * @ss.desc: SuperSpeed descriptor
+ * @ss_comp.desc: SuperSpeed Companion descriptor
+ * @vendor_descs: list of vendor specific descriptors
+ * @vendor_descs_num: count of vendor specific descriptors
+ * @ep: pointer to endpoint obtained during bind process
+ *
+ * We have pointer to each descriptor in union with pointer to descriptor
+ * header in order to avoid casting in many places in code, because in
+ * some situations we want to have access to fields of particular type
+ * of descriptor, while in other situations we want to treat all types
+ * of descriptors in the same way.
+ */
+struct usb_composite_ep {
+	union {
+		struct usb_descriptor_header *header;
+		struct usb_endpoint_descriptor *desc;
+	} fs;
+
+	union {
+		struct usb_descriptor_header *header;
+		struct usb_endpoint_descriptor *desc;
+	} hs;
+
+	union {
+		struct usb_descriptor_header *header;
+		struct usb_endpoint_descriptor *desc;
+	} ss;
+
+	union {
+		struct usb_descriptor_header *header;
+		struct usb_ss_ep_comp_descriptor *desc;
+	} ss_comp;
+
+	struct list_head vendor_descs;
+	int vendor_descs_num;
+
+	struct usb_ep *ep;
+};
+
+/**
+ * struct usb_composite_altset - representation of USB altsetting
+ * @alt.desc: interface (altsetting) descriptor
+ * @eps: array of endpoints in altsetting
+ * @eps_num: number of endpoints
+ * @vendor_descs: list of vendor specific descriptors
+ * @vendor_descs_num: count of vendor specific descriptors
+ *
+ * We have pointer to alt descriptor in union with pointer to descriptor
+ * header in order to avoid casting in many places in code, because in
+ * some situations we want to have access to fields of particular type
+ * of descriptor, while in other situations we want to treat all types
+ * of descriptors in the same way.
+ */
+struct usb_composite_altset {
+	union {
+		struct usb_descriptor_header *header;
+		struct usb_interface_descriptor *desc;
+	} alt;
+
+	struct usb_composite_ep **eps;
+	int eps_num;
+
+	struct list_head vendor_descs;
+	int vendor_descs_num;
+};
+
+/**
+ * struct usb_composite_intf - representation of USB interface
+ * @altsets: array of altsettings in interface
+ * @altsets_num: number of altsettings
+ * @cur_altset: number of currently selected altsetting
+ * @id: id number of interface in configuraion (value of
+ *	bInterfaceNumber in interface descriptor)
+ */
+struct usb_composite_intf {
+	struct usb_composite_altset **altsets;
+	int altsets_num;
+
+	int cur_altset;
+	u8 id;
+};
+
+/**
+ * struct usb_composite_descs - representation of USB descriptors
+ * @intfs: array of interfaces in function
+ * @intfs_num: number of interfaces
+ * @vendor_descs: list of vendor specific descriptors
+ * @vendor_descs_num: count of vendor specific descriptors
+ */
+struct usb_composite_descs {
+	struct usb_composite_intf **intfs;
+	int intfs_num;
+
+	struct list_head vendor_descs;
+	int vendor_descs_num;
+};
+
+/**
  * struct usb_os_desc_ext_prop - describes one "Extended Property"
  * @entry: used to keep a list of extended properties
  * @type: Extended Property type
@@ -126,6 +241,8 @@ struct usb_os_desc_table {
  *	string identifiers assigned during @bind(). If this
  *	pointer is null after initiation, the function will not
  *	be available at super speed.
+ * @descs: structure containing information about descriptors and endpoints
+ *	assigned during gadget bind.
  * @config: assigned when @usb_add_function() is called; this is the
  *	configuration with which this function is associated.
  * @os_desc_table: Table of (interface id, os descriptors) pairs. The function
@@ -187,6 +304,8 @@ struct usb_function {
 	struct usb_descriptor_header	**hs_descriptors;
 	struct usb_descriptor_header	**ss_descriptors;
 
+	struct usb_composite_descs	*descs;
+
 	struct usb_configuration	*config;
 
 	struct usb_os_desc_table	*os_desc_table;
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

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


#1278742 — [PATCH v2 02/29] usb: gadget: f_sourcesink: free requests in sourcesink_disable()

FromRobert Baldyga <r.baldyga@samsung.com>
Date2015-11-27 12:10 +0100
Subject[PATCH v2 02/29] usb: gadget: f_sourcesink: free requests in sourcesink_disable()
Message-ID<qzp4E-243-59@gated-at.bofh.it>
In reply to#1278738
USB requests in SourceSink function are allocated in sourcesink_get_alt()
function, so we prefer to free them rather in sourcesink_disable() than
in source_sink_complete() when request is completed with error. It provides
better symetry in resource management and improves code readability.

Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
---
 drivers/usb/gadget/function/f_sourcesink.c | 63 ++++++++++++++++++++++++------
 1 file changed, 51 insertions(+), 12 deletions(-)

diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c
index e950031..6193b47 100644
--- a/drivers/usb/gadget/function/f_sourcesink.c
+++ b/drivers/usb/gadget/function/f_sourcesink.c
@@ -44,6 +44,11 @@ struct f_sourcesink {
 	struct usb_ep		*iso_out_ep;
 	int			cur_alt;
 
+	struct usb_request	**in_reqs;
+	struct usb_request	**out_reqs;
+	struct usb_request	**iso_in_reqs;
+	struct usb_request	**iso_out_reqs;
+
 	unsigned pattern;
 	unsigned isoc_interval;
 	unsigned isoc_maxpacket;
@@ -550,7 +555,6 @@ static void source_sink_complete(struct usb_ep *ep, struct usb_request *req)
 				req->actual, req->length);
 		if (ep == ss->out_ep)
 			check_read_data(ss, req);
-		free_ep_req(ep, req);
 		return;
 
 	case -EOVERFLOW:		/* buffer overrun on read means that
@@ -579,7 +583,7 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
 		bool is_iso, int speed)
 {
 	struct usb_ep		*ep;
-	struct usb_request	*req;
+	struct usb_request	**reqs;
 	int			i, size, qlen, status = 0;
 
 	if (is_iso) {
@@ -604,19 +608,23 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
 		qlen = ss->bulk_qlen;
 		size = 0;
 	}
-
+	
+	reqs = kzalloc(qlen * sizeof(*reqs), GFP_ATOMIC);
+	
 	for (i = 0; i < qlen; i++) {
-		req = ss_alloc_ep_req(ep, size);
-		if (!req)
-			return -ENOMEM;
+		reqs[i] = ss_alloc_ep_req(ep, size);
+		if (!reqs[i]) {
+			status = -ENOMEM;
+			goto err;
+		}
 
-		req->complete = source_sink_complete;
+		reqs[i]->complete = source_sink_complete;
 		if (is_in)
-			reinit_write_data(ep, req);
+			reinit_write_data(ep, reqs[i]);
 		else if (ss->pattern != 2)
-			memset(req->buf, 0x55, req->length);
+			memset(reqs[i]->buf, 0x55, reqs[i]->length);
 
-		status = usb_ep_queue(ep, req, GFP_ATOMIC);
+		status = usb_ep_queue(ep, reqs[i], GFP_ATOMIC);
 		if (status) {
 			struct usb_composite_dev	*cdev;
 
@@ -624,12 +632,30 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
 			ERROR(cdev, "start %s%s %s --> %d\n",
 			      is_iso ? "ISO-" : "", is_in ? "IN" : "OUT",
 			      ep->name, status);
-			free_ep_req(ep, req);
-			return status;
+			free_ep_req(ep, reqs[i]);
+			goto err;
+		}
+
+		if (is_iso) {
+			if (is_in)
+				ss->iso_in_reqs = reqs;
+			else
+				ss->iso_out_reqs = reqs;
+		} else {
+			if (is_in)
+				ss->in_reqs = reqs;
+			else
+				ss->out_reqs = reqs;
 		}
 	}
 
 	return status;
+
+err:
+	while (--i)
+		free_ep_req(ep, reqs[i]);
+	kfree(reqs);
+	return status;
 }
 
 static void disable_source_sink(struct f_sourcesink *ss)
@@ -754,8 +780,21 @@ static int sourcesink_get_alt(struct usb_function *f, unsigned intf)
 static void sourcesink_disable(struct usb_function *f)
 {
 	struct f_sourcesink	*ss = func_to_ss(f);
+	int i;
 
 	disable_source_sink(ss);
+
+	for (i = 0; i < ss->bulk_qlen; ++i) {
+		free_ep_req(ss->in_ep, ss->in_reqs[i]);
+		free_ep_req(ss->out_ep, ss->out_reqs[i]);
+	}
+
+	if (ss->iso_in_ep) {
+		for (i = 0; i < ss->iso_qlen; ++i) {
+			free_ep_req(ss->iso_in_ep, ss->iso_in_reqs[i]);
+			free_ep_req(ss->iso_out_ep, ss->iso_out_reqs[i]);
+		}
+	}
 }
 
 /*-------------------------------------------------------------------------*/
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

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


#1278743 — [PATCH v2 26/29] usb: gadget: f_eem: conversion to new API

FromRobert Baldyga <r.baldyga@samsung.com>
Date2015-11-27 12:10 +0100
Subject[PATCH v2 26/29] usb: gadget: f_eem: conversion to new API
Message-ID<qzp4E-243-67@gated-at.bofh.it>
In reply to#1278738
Generate descriptors in new format and attach them to USB function in
prep_descs(). Implement prep_vendor_descs() to supply class specific
descriptors. Change set_alt() implementation and implement clear_alt()
operation. Remove boilerplate code.

Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
---
 drivers/usb/gadget/function/f_eem.c | 154 ++++++++----------------------------
 1 file changed, 33 insertions(+), 121 deletions(-)

diff --git a/drivers/usb/gadget/function/f_eem.c b/drivers/usb/gadget/function/f_eem.c
index cad35a5..8896419 100644
--- a/drivers/usb/gadget/function/f_eem.c
+++ b/drivers/usb/gadget/function/f_eem.c
@@ -73,14 +73,6 @@ static struct usb_endpoint_descriptor eem_fs_out_desc = {
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 };
 
-static struct usb_descriptor_header *eem_fs_function[] = {
-	/* CDC EEM control descriptors */
-	(struct usb_descriptor_header *) &eem_intf,
-	(struct usb_descriptor_header *) &eem_fs_in_desc,
-	(struct usb_descriptor_header *) &eem_fs_out_desc,
-	NULL,
-};
-
 /* high speed support: */
 
 static struct usb_endpoint_descriptor eem_hs_in_desc = {
@@ -101,14 +93,6 @@ static struct usb_endpoint_descriptor eem_hs_out_desc = {
 	.wMaxPacketSize =	cpu_to_le16(512),
 };
 
-static struct usb_descriptor_header *eem_hs_function[] = {
-	/* CDC EEM control descriptors */
-	(struct usb_descriptor_header *) &eem_intf,
-	(struct usb_descriptor_header *) &eem_hs_in_desc,
-	(struct usb_descriptor_header *) &eem_hs_out_desc,
-	NULL,
-};
-
 /* super speed support: */
 
 static struct usb_endpoint_descriptor eem_ss_in_desc = {
@@ -138,15 +122,16 @@ static struct usb_ss_ep_comp_descriptor eem_ss_bulk_comp_desc = {
 	/* .bmAttributes =	0, */
 };
 
-static struct usb_descriptor_header *eem_ss_function[] = {
-	/* CDC EEM control descriptors */
-	(struct usb_descriptor_header *) &eem_intf,
-	(struct usb_descriptor_header *) &eem_ss_in_desc,
-	(struct usb_descriptor_header *) &eem_ss_bulk_comp_desc,
-	(struct usb_descriptor_header *) &eem_ss_out_desc,
-	(struct usb_descriptor_header *) &eem_ss_bulk_comp_desc,
-	NULL,
-};
+USB_COMPOSITE_ENDPOINT(ep_in, &eem_fs_in_desc, &eem_hs_in_desc,
+		&eem_ss_in_desc, &eem_ss_bulk_comp_desc);
+USB_COMPOSITE_ENDPOINT(ep_out, &eem_fs_out_desc, &eem_hs_out_desc,
+		&eem_ss_out_desc, &eem_ss_bulk_comp_desc);
+
+USB_COMPOSITE_ALTSETTING(intf0alt0, &eem_intf, &ep_in, &ep_out);
+
+USB_COMPOSITE_INTERFACE(intf0, &intf0alt0);
+
+USB_COMPOSITE_DESCRIPTORS(eem_descs, &intf0);
 
 /* string descriptors: */
 
@@ -190,65 +175,46 @@ static int eem_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
 	struct usb_composite_dev *cdev = f->config->cdev;
 	struct net_device	*net;
 
-	/* we know alt == 0, so this is an activation or a reset */
-	if (alt != 0)
-		goto fail;
-
-	if (intf == eem->ctrl_id) {
-		DBG(cdev, "reset eem\n");
-		gether_disconnect(&eem->port);
-
-		if (!eem->port.in_ep->desc || !eem->port.out_ep->desc) {
-			DBG(cdev, "init eem\n");
-			if (config_ep_by_speed(cdev->gadget, f,
-					       eem->port.in_ep) ||
-			    config_ep_by_speed(cdev->gadget, f,
-					       eem->port.out_ep)) {
-				eem->port.in_ep->desc = NULL;
-				eem->port.out_ep->desc = NULL;
-				goto fail;
-			}
-		}
+	eem->port.in_ep = usb_function_get_ep(f, intf, 0);
+	if (!eem->port.in_ep)
+		return -ENODEV;
 
-		/* zlps should not occur because zero-length EEM packets
-		 * will be inserted in those cases where they would occur
-		 */
-		eem->port.is_zlp_ok = 1;
-		eem->port.cdc_filter = DEFAULT_FILTER;
-		DBG(cdev, "activate eem\n");
-		net = gether_connect(&eem->port);
-		if (IS_ERR(net))
-			return PTR_ERR(net);
-	} else
-		goto fail;
+	eem->port.out_ep = usb_function_get_ep(f, intf, 1);
+	if (!eem->port.out_ep)
+		return -ENODEV;
+
+	/* zlps should not occur because zero-length EEM packets
+	 * will be inserted in those cases where they would occur
+	 */
+	eem->port.is_zlp_ok = 1;
+	eem->port.cdc_filter = DEFAULT_FILTER;
+	DBG(cdev, "activate eem\n");
+	net = gether_connect(&eem->port);
+	if (IS_ERR(net))
+		return PTR_ERR(net);
 
 	return 0;
-fail:
-	return -EINVAL;
 }
 
-static void eem_disable(struct usb_function *f)
+static void eem_clear_alt(struct usb_function *f, unsigned intf, unsigned alt)
 {
 	struct f_eem		*eem = func_to_eem(f);
 	struct usb_composite_dev *cdev = f->config->cdev;
 
 	DBG(cdev, "eem deactivated\n");
 
-	if (eem->port.in_ep->enabled)
-		gether_disconnect(&eem->port);
+	gether_disconnect(&eem->port);
 }
 
 /*-------------------------------------------------------------------------*/
 
 /* EEM function driver setup/binding */
 
-static int eem_bind(struct usb_configuration *c, struct usb_function *f)
+static int eem_prep_descs(struct usb_function *f)
 {
-	struct usb_composite_dev *cdev = c->cdev;
-	struct f_eem		*eem = func_to_eem(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
 	struct usb_string	*us;
 	int			status;
-	struct usb_ep		*ep;
 
 	struct f_eem_opts	*eem_opts;
 
@@ -276,53 +242,7 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f)
 		return PTR_ERR(us);
 	eem_intf.iInterface = us[0].id;
 
-	/* allocate instance-specific interface IDs */
-	status = usb_interface_id(c, f);
-	if (status < 0)
-		goto fail;
-	eem->ctrl_id = status;
-	eem_intf.bInterfaceNumber = status;
-
-	status = -ENODEV;
-
-	/* allocate instance-specific endpoints */
-	ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_in_desc);
-	if (!ep)
-		goto fail;
-	eem->port.in_ep = ep;
-
-	ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_out_desc);
-	if (!ep)
-		goto fail;
-	eem->port.out_ep = ep;
-
-	status = -ENOMEM;
-
-	/* support all relevant hardware speeds... we expect that when
-	 * hardware is dual speed, all bulk-capable endpoints work at
-	 * both speeds
-	 */
-	eem_hs_in_desc.bEndpointAddress = eem_fs_in_desc.bEndpointAddress;
-	eem_hs_out_desc.bEndpointAddress = eem_fs_out_desc.bEndpointAddress;
-
-	eem_ss_in_desc.bEndpointAddress = eem_fs_in_desc.bEndpointAddress;
-	eem_ss_out_desc.bEndpointAddress = eem_fs_out_desc.bEndpointAddress;
-
-	status = usb_assign_descriptors(f, eem_fs_function, eem_hs_function,
-			eem_ss_function);
-	if (status)
-		goto fail;
-
-	DBG(cdev, "CDC Ethernet (EEM): %s speed IN/%s OUT/%s\n",
-			gadget_is_superspeed(c->cdev->gadget) ? "super" :
-			gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
-			eem->port.in_ep->name, eem->port.out_ep->name);
-	return 0;
-
-fail:
-	ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
-
-	return status;
+	return usb_function_set_descs(f, &eem_descs);
 }
 
 static void eem_cmd_complete(struct usb_ep *ep, struct usb_request *req)
@@ -604,13 +524,6 @@ static void eem_free(struct usb_function *f)
 	mutex_unlock(&opts->lock);
 }
 
-static void eem_unbind(struct usb_configuration *c, struct usb_function *f)
-{
-	DBG(c->cdev, "eem unbind\n");
-
-	usb_free_all_descriptors(f);
-}
-
 static struct usb_function *eem_alloc(struct usb_function_instance *fi)
 {
 	struct f_eem	*eem;
@@ -631,11 +544,10 @@ static struct usb_function *eem_alloc(struct usb_function_instance *fi)
 
 	eem->port.func.name = "cdc_eem";
 	/* descriptors are per-instance copies */
-	eem->port.func.bind = eem_bind;
-	eem->port.func.unbind = eem_unbind;
+	eem->port.func.prep_descs = eem_prep_descs;
 	eem->port.func.set_alt = eem_set_alt;
+	eem->port.func.clear_alt = eem_clear_alt;
 	eem->port.func.setup = eem_setup;
-	eem->port.func.disable = eem_disable;
 	eem->port.func.free_func = eem_free;
 	eem->port.wrap = eem_wrap;
 	eem->port.unwrap = eem_unwrap;
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

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


#1278744 — [PATCH v2 20/29] usb: gadget: f_sourcesink: convert to new API

FromRobert Baldyga <r.baldyga@samsung.com>
Date2015-11-27 12:10 +0100
Subject[PATCH v2 20/29] usb: gadget: f_sourcesink: convert to new API
Message-ID<qzp4E-243-69@gated-at.bofh.it>
In reply to#1278738
Generate descriptors in new format and attach them to USB function in
prep_descs(). Change set_alt() implementation and implement clear_alt()
operation. Get rid of get_alt() callback, as now USB_REQ_GET_INTERFACE
is handled automatically by composite framwework. 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_sourcesink.c | 314 ++++++-----------------------
 drivers/usb/gadget/function/g_zero.h       |   3 -
 drivers/usb/gadget/legacy/zero.c           |   3 +
 3 files changed, 65 insertions(+), 255 deletions(-)

diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c
index 6193b47..262dae8 100644
--- a/drivers/usb/gadget/function/f_sourcesink.c
+++ b/drivers/usb/gadget/function/f_sourcesink.c
@@ -42,7 +42,6 @@ struct f_sourcesink {
 	struct usb_ep		*out_ep;
 	struct usb_ep		*iso_in_ep;
 	struct usb_ep		*iso_out_ep;
-	int			cur_alt;
 
 	struct usb_request	**in_reqs;
 	struct usb_request	**out_reqs;
@@ -125,19 +124,6 @@ static struct usb_endpoint_descriptor fs_iso_sink_desc = {
 	.bInterval =		4,
 };
 
-static struct usb_descriptor_header *fs_source_sink_descs[] = {
-	(struct usb_descriptor_header *) &source_sink_intf_alt0,
-	(struct usb_descriptor_header *) &fs_sink_desc,
-	(struct usb_descriptor_header *) &fs_source_desc,
-	(struct usb_descriptor_header *) &source_sink_intf_alt1,
-#define FS_ALT_IFC_1_OFFSET	3
-	(struct usb_descriptor_header *) &fs_sink_desc,
-	(struct usb_descriptor_header *) &fs_source_desc,
-	(struct usb_descriptor_header *) &fs_iso_sink_desc,
-	(struct usb_descriptor_header *) &fs_iso_source_desc,
-	NULL,
-};
-
 /* high speed support: */
 
 static struct usb_endpoint_descriptor hs_source_desc = {
@@ -174,19 +160,6 @@ static struct usb_endpoint_descriptor hs_iso_sink_desc = {
 	.bInterval =		4,
 };
 
-static struct usb_descriptor_header *hs_source_sink_descs[] = {
-	(struct usb_descriptor_header *) &source_sink_intf_alt0,
-	(struct usb_descriptor_header *) &hs_source_desc,
-	(struct usb_descriptor_header *) &hs_sink_desc,
-	(struct usb_descriptor_header *) &source_sink_intf_alt1,
-#define HS_ALT_IFC_1_OFFSET	3
-	(struct usb_descriptor_header *) &hs_source_desc,
-	(struct usb_descriptor_header *) &hs_sink_desc,
-	(struct usb_descriptor_header *) &hs_iso_source_desc,
-	(struct usb_descriptor_header *) &hs_iso_sink_desc,
-	NULL,
-};
-
 /* super speed support: */
 
 static struct usb_endpoint_descriptor ss_source_desc = {
@@ -259,24 +232,24 @@ static struct usb_ss_ep_comp_descriptor ss_iso_sink_comp_desc = {
 	.wBytesPerInterval =	cpu_to_le16(1024),
 };
 
-static struct usb_descriptor_header *ss_source_sink_descs[] = {
-	(struct usb_descriptor_header *) &source_sink_intf_alt0,
-	(struct usb_descriptor_header *) &ss_source_desc,
-	(struct usb_descriptor_header *) &ss_source_comp_desc,
-	(struct usb_descriptor_header *) &ss_sink_desc,
-	(struct usb_descriptor_header *) &ss_sink_comp_desc,
-	(struct usb_descriptor_header *) &source_sink_intf_alt1,
-#define SS_ALT_IFC_1_OFFSET	5
-	(struct usb_descriptor_header *) &ss_source_desc,
-	(struct usb_descriptor_header *) &ss_source_comp_desc,
-	(struct usb_descriptor_header *) &ss_sink_desc,
-	(struct usb_descriptor_header *) &ss_sink_comp_desc,
-	(struct usb_descriptor_header *) &ss_iso_source_desc,
-	(struct usb_descriptor_header *) &ss_iso_source_comp_desc,
-	(struct usb_descriptor_header *) &ss_iso_sink_desc,
-	(struct usb_descriptor_header *) &ss_iso_sink_comp_desc,
-	NULL,
-};
+USB_COMPOSITE_ENDPOINT(ep_source, &fs_source_desc, &hs_source_desc,
+		&ss_source_desc, &ss_source_comp_desc);
+USB_COMPOSITE_ENDPOINT(ep_sink, &fs_sink_desc, &hs_sink_desc,
+		&ss_sink_desc, &ss_sink_comp_desc);
+USB_COMPOSITE_ENDPOINT(ep_iso_source, &fs_iso_source_desc, &hs_iso_source_desc,
+		&ss_iso_source_desc, &ss_iso_source_comp_desc);
+USB_COMPOSITE_ENDPOINT(ep_iso_sink, &fs_iso_sink_desc, &hs_iso_sink_desc,
+		&ss_iso_sink_desc, &ss_iso_sink_comp_desc);
+
+USB_COMPOSITE_ALTSETTING(altset0, &source_sink_intf_alt0, &ep_source, &ep_sink);
+USB_COMPOSITE_ALTSETTING(altset1, &source_sink_intf_alt1, &ep_source, &ep_sink,
+		&ep_iso_source, &ep_iso_sink);
+
+USB_COMPOSITE_INTERFACE(intf0, &altset0, &altset1);
+USB_COMPOSITE_INTERFACE(intf0_no_iso, &altset0);
+
+USB_COMPOSITE_DESCRIPTORS(source_sink_descs, &intf0);
+USB_COMPOSITE_DESCRIPTORS(source_sink_descs_no_iso, &intf0_no_iso);
 
 /* function-specific strings: */
 
@@ -304,65 +277,12 @@ static inline struct usb_request *ss_alloc_ep_req(struct usb_ep *ep, int len)
 	return alloc_ep_req(ep, len, ss->buflen);
 }
 
-static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep)
-{
-	int			value;
-
-	value = usb_ep_disable(ep);
-	if (value < 0)
-		DBG(cdev, "disable %s --> %d\n", ep->name, value);
-}
-
-void disable_endpoints(struct usb_composite_dev *cdev,
-		struct usb_ep *in, struct usb_ep *out,
-		struct usb_ep *iso_in, struct usb_ep *iso_out)
-{
-	disable_ep(cdev, in);
-	disable_ep(cdev, out);
-	if (iso_in)
-		disable_ep(cdev, iso_in);
-	if (iso_out)
-		disable_ep(cdev, iso_out);
-}
-
-static int
-sourcesink_bind(struct usb_configuration *c, struct usb_function *f)
+static int sourcesink_prep_descs(struct usb_function *f)
 {
-	struct usb_composite_dev *cdev = c->cdev;
 	struct f_sourcesink	*ss = func_to_ss(f);
-	int	id;
-	int ret;
 
-	/* allocate interface ID(s) */
-	id = usb_interface_id(c, f);
-	if (id < 0)
-		return id;
-	source_sink_intf_alt0.bInterfaceNumber = id;
-	source_sink_intf_alt1.bInterfaceNumber = id;
-
-	/* allocate bulk endpoints */
-	ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
-	if (!ss->in_ep)
-		goto autoconf_fail;
-
-	ss->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_sink_desc);
-	if (!ss->out_ep)
-		goto autoconf_fail;
-
-	/* support high speed hardware */
-	hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress;
-	hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress;
-
-	/* support super speed hardware */
-	ss_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress;
-	ss_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress;
-
-	if (!ss->isoc_enabled) {
-		fs_source_sink_descs[FS_ALT_IFC_1_OFFSET] = NULL;
-		hs_source_sink_descs[HS_ALT_IFC_1_OFFSET] = NULL;
-		ss_source_sink_descs[SS_ALT_IFC_1_OFFSET] = NULL;
-		goto no_iso;
-	}
+	if (!ss->isoc_enabled)
+		return usb_function_set_descs(f, &source_sink_descs_no_iso);
 
 	/* sanity check the isoc module parameters */
 	if (ss->isoc_interval < 1)
@@ -382,15 +302,6 @@ sourcesink_bind(struct usb_configuration *c, struct usb_function *f)
 						1023 : ss->isoc_maxpacket;
 	fs_iso_sink_desc.bInterval = ss->isoc_interval;
 
-	/* allocate iso endpoints */
-	ss->iso_in_ep = usb_ep_autoconfig(cdev->gadget, &fs_iso_source_desc);
-	if (!ss->iso_in_ep)
-		goto autoconf_fail;
-
-	ss->iso_out_ep = usb_ep_autoconfig(cdev->gadget, &fs_iso_sink_desc);
-	if (!ss->iso_out_ep)
-		goto autoconf_fail;
-
 	if (ss->isoc_maxpacket > 1024)
 		ss->isoc_maxpacket = 1024;
 	/*
@@ -401,14 +312,10 @@ sourcesink_bind(struct usb_configuration *c, struct usb_function *f)
 	hs_iso_source_desc.wMaxPacketSize = ss->isoc_maxpacket;
 	hs_iso_source_desc.wMaxPacketSize |= ss->isoc_mult << 11;
 	hs_iso_source_desc.bInterval = ss->isoc_interval;
-	hs_iso_source_desc.bEndpointAddress =
-		fs_iso_source_desc.bEndpointAddress;
 
 	hs_iso_sink_desc.wMaxPacketSize = ss->isoc_maxpacket;
 	hs_iso_sink_desc.wMaxPacketSize |= ss->isoc_mult << 11;
 	hs_iso_sink_desc.bInterval = ss->isoc_interval;
-	hs_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress;
-
 	/*
 	 * Fill in the SS isoc descriptors from the module parameters.
 	 * We assume that the user knows what they are doing and won't
@@ -420,8 +327,6 @@ sourcesink_bind(struct usb_configuration *c, struct usb_function *f)
 	ss_iso_source_comp_desc.bMaxBurst = ss->isoc_maxburst;
 	ss_iso_source_comp_desc.wBytesPerInterval = ss->isoc_maxpacket *
 		(ss->isoc_mult + 1) * (ss->isoc_maxburst + 1);
-	ss_iso_source_desc.bEndpointAddress =
-		fs_iso_source_desc.bEndpointAddress;
 
 	ss_iso_sink_desc.wMaxPacketSize = ss->isoc_maxpacket;
 	ss_iso_sink_desc.bInterval = ss->isoc_interval;
@@ -429,26 +334,8 @@ sourcesink_bind(struct usb_configuration *c, struct usb_function *f)
 	ss_iso_sink_comp_desc.bMaxBurst = ss->isoc_maxburst;
 	ss_iso_sink_comp_desc.wBytesPerInterval = ss->isoc_maxpacket *
 		(ss->isoc_mult + 1) * (ss->isoc_maxburst + 1);
-	ss_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress;
 
-no_iso:
-	ret = usb_assign_descriptors(f, fs_source_sink_descs,
-			hs_source_sink_descs, ss_source_sink_descs);
-	if (ret)
-		return ret;
-
-	DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s\n",
-	    (gadget_is_superspeed(c->cdev->gadget) ? "super" :
-	     (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")),
-			f->name, ss->in_ep->name, ss->out_ep->name,
-			ss->iso_in_ep ? ss->iso_in_ep->name : "<none>",
-			ss->iso_out_ep ? ss->iso_out_ep->name : "<none>");
-	return 0;
-
-autoconf_fail:
-	ERROR(cdev, "%s: can't autoconfigure on %s\n",
-			f->name, cdev->gadget->name);
-	return -ENODEV;
+	return usb_function_set_descs(f, &source_sink_descs);
 }
 
 static void
@@ -462,7 +349,6 @@ sourcesink_free_func(struct usb_function *f)
 	opts->refcnt--;
 	mutex_unlock(&opts->lock);
 
-	usb_free_all_descriptors(f);
 	kfree(func_to_ss(f));
 }
 
@@ -658,138 +544,63 @@ err:
 	return status;
 }
 
-static void disable_source_sink(struct f_sourcesink *ss)
-{
-	struct usb_composite_dev	*cdev;
-
-	cdev = ss->function.config->cdev;
-	disable_endpoints(cdev, ss->in_ep, ss->out_ep, ss->iso_in_ep,
-			ss->iso_out_ep);
-	VDBG(cdev, "%s disabled\n", ss->function.name);
-}
-
-static int
-enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss,
-		int alt)
-{
-	int					result = 0;
-	int					speed = cdev->gadget->speed;
-	struct usb_ep				*ep;
-
-	/* one bulk endpoint writes (sources) zeroes IN (to the host) */
-	ep = ss->in_ep;
-	result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
-	if (result)
-		return result;
-	result = usb_ep_enable(ep);
-	if (result < 0)
-		return result;
-	ep->driver_data = ss;
-
-	result = source_sink_start_ep(ss, true, false, speed);
-	if (result < 0) {
-fail:
-		ep = ss->in_ep;
-		usb_ep_disable(ep);
-		return result;
-	}
-
-	/* one bulk endpoint reads (sinks) anything OUT (from the host) */
-	ep = ss->out_ep;
-	result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
-	if (result)
-		goto fail;
-	result = usb_ep_enable(ep);
-	if (result < 0)
-		goto fail;
-	ep->driver_data = ss;
-
-	result = source_sink_start_ep(ss, false, false, speed);
-	if (result < 0) {
-fail2:
-		ep = ss->out_ep;
-		usb_ep_disable(ep);
-		goto fail;
-	}
-
-	if (alt == 0)
-		goto out;
-
-	/* one iso endpoint writes (sources) zeroes IN (to the host) */
-	ep = ss->iso_in_ep;
-	if (ep) {
-		result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
-		if (result)
-			goto fail2;
-		result = usb_ep_enable(ep);
-		if (result < 0)
-			goto fail2;
-		ep->driver_data = ss;
-
-		result = source_sink_start_ep(ss, true, true, speed);
-		if (result < 0) {
-fail3:
-			ep = ss->iso_in_ep;
-			if (ep)
-				usb_ep_disable(ep);
-			goto fail2;
-		}
-	}
-
-	/* one iso endpoint reads (sinks) anything OUT (from the host) */
-	ep = ss->iso_out_ep;
-	if (ep) {
-		result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
-		if (result)
-			goto fail3;
-		result = usb_ep_enable(ep);
-		if (result < 0)
-			goto fail3;
-		ep->driver_data = ss;
-
-		result = source_sink_start_ep(ss, false, true, speed);
-		if (result < 0) {
-			usb_ep_disable(ep);
-			goto fail3;
-		}
-	}
-out:
-	ss->cur_alt = alt;
-
-	DBG(cdev, "%s enabled, alt intf %d\n", ss->function.name, alt);
-	return result;
-}
-
 static int sourcesink_set_alt(struct usb_function *f,
 		unsigned intf, unsigned alt)
 {
 	struct f_sourcesink		*ss = func_to_ss(f);
 	struct usb_composite_dev	*cdev = f->config->cdev;
+	int				speed = cdev->gadget->speed;
+	int				ret;
 
-	disable_source_sink(ss);
-	return enable_source_sink(cdev, ss, alt);
-}
+	ss->in_ep = usb_function_get_ep(f, intf, 0);
+	if (!ss->in_ep)
+		return -ENODEV;
+	ss->in_ep->driver_data = ss;
+	ret = source_sink_start_ep(ss, true, false, speed);
+	if (ret < 0)
+		return ret;
 
-static int sourcesink_get_alt(struct usb_function *f, unsigned intf)
-{
-	struct f_sourcesink		*ss = func_to_ss(f);
+	ss->out_ep = usb_function_get_ep(f, intf, 1);
+	if (!ss->out_ep)
+		return -ENODEV;
+	ss->out_ep->driver_data = ss;
+	ret = source_sink_start_ep(ss, false, false, speed);
+	if (ret < 0)
+		return ret;
+
+	if (alt == 1) {
+		ss->iso_in_ep = usb_function_get_ep(f, intf, 2);
+		if (!ss->iso_in_ep)
+			return -ENODEV;
+		ss->iso_in_ep->driver_data = ss;
+		ret = source_sink_start_ep(ss, true, true, speed);
+		if (ret < 0)
+			return ret;
+
+		ss->iso_out_ep = usb_function_get_ep(f, intf, 3);
+		if (!ss->iso_out_ep)
+			return -ENODEV;
+		ss->iso_out_ep->driver_data = ss;
+		ret = source_sink_start_ep(ss, false, true, speed);
+		if (ret < 0)
+			return ret;
+	}
 
-	return ss->cur_alt;
+	return 0;
 }
 
-static void sourcesink_disable(struct usb_function *f)
+static void sourcesink_clear_alt(struct usb_function *f,
+		unsigned intf, unsigned alt)
 {
 	struct f_sourcesink	*ss = func_to_ss(f);
 	int i;
 
-	disable_source_sink(ss);
-
 	for (i = 0; i < ss->bulk_qlen; ++i) {
 		free_ep_req(ss->in_ep, ss->in_reqs[i]);
 		free_ep_req(ss->out_ep, ss->out_reqs[i]);
 	}
 
-	if (ss->iso_in_ep) {
+	if (alt == 1) {
 		for (i = 0; i < ss->iso_qlen; ++i) {
 			free_ep_req(ss->iso_in_ep, ss->iso_in_reqs[i]);
 			free_ep_req(ss->iso_out_ep, ss->iso_out_reqs[i]);
@@ -898,10 +709,9 @@ static struct usb_function *source_sink_alloc_func(
 	ss->iso_qlen = ss_opts->iso_qlen;
 
 	ss->function.name = "source/sink";
-	ss->function.bind = sourcesink_bind;
+	ss->function.prep_descs = sourcesink_prep_descs;
 	ss->function.set_alt = sourcesink_set_alt;
-	ss->function.get_alt = sourcesink_get_alt;
-	ss->function.disable = sourcesink_disable;
+	ss->function.clear_alt = sourcesink_clear_alt;
 	ss->function.setup = sourcesink_setup;
 	ss->function.strings = sourcesink_strings;
 
diff --git a/drivers/usb/gadget/function/g_zero.h b/drivers/usb/gadget/function/g_zero.h
index ae03278..e8d832d 100644
--- a/drivers/usb/gadget/function/g_zero.h
+++ b/drivers/usb/gadget/function/g_zero.h
@@ -68,8 +68,5 @@ void lb_modexit(void);
 int lb_modinit(void);
 
 /* common utilities */
-void disable_endpoints(struct usb_composite_dev *cdev,
-		struct usb_ep *in, struct usb_ep *out,
-		struct usb_ep *iso_in, struct usb_ep *iso_out);
 
 #endif /* __G_ZERO_H */
diff --git a/drivers/usb/gadget/legacy/zero.c b/drivers/usb/gadget/legacy/zero.c
index 781ca94..4c52b4a 100644
--- a/drivers/usb/gadget/legacy/zero.c
+++ b/drivers/usb/gadget/legacy/zero.c
@@ -373,6 +373,9 @@ static int zero_bind(struct usb_composite_dev *cdev)
 	status = usb_add_function(&sourcesink_driver, func_ss);
 	if (status)
 		goto err_free_otg_desc;
+	status = usb_config_do_bind(&sourcesink_driver);
+	if (status)
+		goto err_free_otg_desc;
 
 	usb_ep_autoconfig_reset(cdev->gadget);
 	status = usb_add_function(&loopback_driver, func_lb);
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

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


#1278746 — [PATCH v2 08/29] usb: gadget: composite: handle function bind

FromRobert Baldyga <r.baldyga@samsung.com>
Date2015-11-27 12:10 +0100
Subject[PATCH v2 08/29] usb: gadget: composite: handle function bind
Message-ID<qzp4E-243-73@gated-at.bofh.it>
In reply to#1278738
As now USB function supplies entity descriptors to composite in
prep_descs() callback, we can perform bind inside composite framework
without involving bind() callback (which now is unused and will be
removed after converting all functions in kernel to new API).

For now we bind each configuration when it's added, because we have
to support functions based on old API, but after completing conversion
of functions, we will be able to do bind after adding all configurations.
Also more sophisticated autoconfig solver will be provided to improve
utilization of available hardware endpoints.

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

diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 3ecfaca..f4189b1 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -327,6 +327,21 @@ int usb_function_activate(struct usb_function *function)
 EXPORT_SYMBOL_GPL(usb_function_activate);
 
 /**
+ * usb_function_is_new_api - checks if USB function uses new API
+ * @f: USB function
+ *
+ * This function is added temporarily to allow both old and new function API
+ * to coexist. It function will be removed after converting all USB functions
+ * in kernel to new API.
+ *
+ * Returns true if function uses new API.
+ */
+static inline bool usb_function_is_new_api(struct usb_function *f)
+{
+	return !!f->prep_descs;
+}
+
+/**
  * usb_function_set_descs - assing descriptors to USB function
  * @f: USB function
  * @descs: USB descriptors to be assigned to function
@@ -1109,6 +1124,12 @@ int usb_add_config(struct usb_composite_dev *cdev,
 		goto done;
 
 	status = bind(config);
+	if (status < 0)
+		goto out;
+
+	status = usb_config_do_bind(config);
+
+out:
 	if (status < 0) {
 		while (!list_empty(&config->functions)) {
 			struct usb_function		*f;
@@ -2404,6 +2425,147 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev)
 	device_remove_file(&cdev->gadget->dev, &dev_attr_suspended);
 }
 
+/**
+ * usb_cmp_ep_descs - compare descriptors of two endpoints
+ *
+ * As currently during autoconfig procedure we take into consideration only
+ * FullSpeed and SuperSpeed Companion descriptors, we need to compare only
+ * these descriptors. It they are the same, endpoints are identical from
+ * autoconfig point of view.
+ */
+static int usb_cmp_ep_descs(struct usb_composite_ep *ep1,
+		struct usb_composite_ep *ep2)
+{
+	if (ep1->fs.desc->bLength != ep2->fs.desc->bLength)
+		return 0;
+	if (usb_endpoint_dir_in(ep1->fs.desc) ^
+			usb_endpoint_dir_in(ep2->fs.desc))
+		return 0;
+	if (ep1->fs.desc->bmAttributes != ep2->fs.desc->bmAttributes)
+		return 0;
+	if (ep1->fs.desc->wMaxPacketSize != ep2->fs.desc->wMaxPacketSize)
+		return 0;
+	if (ep1->fs.desc->bInterval != ep2->fs.desc->bInterval)
+		return 0;
+
+	if (ep1->fs.desc->bLength != USB_DT_ENDPOINT_AUDIO_SIZE)
+		goto ss_comp;
+
+	if (ep1->fs.desc->bRefresh != ep2->fs.desc->bRefresh)
+		return 0;
+	if (ep1->fs.desc->bSynchAddress != ep2->fs.desc->bSynchAddress)
+		return 0;
+
+ss_comp:
+	if (!ep1->ss_comp.desc ^ !ep2->ss_comp.desc)
+		return 0;
+	if (!ep1->ss_comp.desc)
+		return 1;
+
+	if (ep1->ss_comp.desc->bMaxBurst != ep2->ss_comp.desc->bMaxBurst)
+		return 0;
+	if (ep1->ss_comp.desc->bmAttributes != ep2->ss_comp.desc->bmAttributes)
+		return 0;
+	if (ep1->ss_comp.desc->wBytesPerInterval !=
+			ep2->ss_comp.desc->wBytesPerInterval)
+		return 0;
+
+	return 1;
+}
+
+/**
+ * ep_update_address() - update endpoint address in descriptors
+ * @ep: composite endpoint with assigned hardware ep
+ *
+ * This function should be called after setting ep->ep to endpoint obtained
+ * from usb_ep_autoconfig_ss(), to update endpoint address in descriptors for
+ * all supported speeds.
+ */
+static inline void ep_update_address(struct usb_composite_ep *ep)
+{
+	if (ep->fs.desc)
+		ep->hs.desc->bEndpointAddress = ep->ep->address;
+	if (ep->hs.desc)
+		ep->hs.desc->bEndpointAddress = ep->ep->address;
+	if (ep->ss.desc)
+		ep->ss.desc->bEndpointAddress = ep->ep->address;
+}
+
+/**
+ * interface_do_bind() - bind interface to UDC
+ * @c: USB configuration
+ * @f: USB function in configuration c
+ * @intf: USB interface in function f
+ *
+ * For now we use only simple interface-level ep aucoconfig solver.
+ * We share endpoints between altsettings where it's possible.
+ */
+static int interface_do_bind(struct usb_configuration *c,
+		struct usb_function *f, struct usb_composite_intf *intf)
+{
+	struct usb_composite_altset *alt, *altx;
+	struct usb_composite_ep *ep, *epx;
+	int a, e, ax, ex;
+
+	intf->id = usb_interface_id(c, f);
+	intf->cur_altset = -1;
+
+	for (a = 0; a < intf->altsets_num; ++a) {
+		alt = intf->altsets[a];
+		alt->alt.desc->bInterfaceNumber = intf->id;
+		for (e = 0; e < alt->eps_num; ++e) {
+			ep = alt->eps[e];
+			if (ep->ep)
+				continue;
+			ep->ep = usb_ep_autoconfig_ss(c->cdev->gadget,
+					ep->fs.desc, ep->ss_comp.desc);
+			if (!ep->ep)
+				return -ENODEV;
+			ep_update_address(ep);
+			/* Try endpoint for other altsets */
+			for (ax = a + 1; ax < intf->altsets_num; ++ax) {
+				altx = intf->altsets[ax];
+				for (ex = 0; ex < altx->eps_num; ++ex) {
+					epx = altx->eps[ex];
+					if (usb_cmp_ep_descs(ep, epx)) {
+						epx->ep = ep->ep;
+						ep_update_address(epx);
+					}
+				}
+			}
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * config_do_bind() - bind configuration to UDC
+ * @c: USB configuration
+ *
+ * Bind the all functions in configuration to UDC.
+ */
+int usb_config_do_bind(struct usb_configuration *c)
+{
+	struct usb_function *f;
+	struct usb_composite_intf *intf;
+	int i, ret;
+
+	list_for_each_entry(f, &c->functions, list) {
+		if (!usb_function_is_new_api(f))
+			continue;
+		for (i = 0; i < f->descs->intfs_num; ++i) {
+			intf = f->descs->intfs[i];
+			ret = interface_do_bind(c, f, intf);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usb_config_do_bind);
+
 static int composite_bind(struct usb_gadget *gadget,
 		struct usb_gadget_driver *gdriver)
 {
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
index 58d2929..a92da38 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -413,6 +413,7 @@ int usb_altset_add_vendor_desc(struct usb_function *f, int i, int a,
 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,
@@ -503,6 +504,8 @@ int usb_add_config(struct usb_composite_dev *,
 void usb_remove_config(struct usb_composite_dev *,
 		struct usb_configuration *);
 
+int usb_config_do_bind(struct usb_configuration *c);
+
 /* predefined index for usb_composite_driver */
 enum {
 	USB_GADGET_MANUFACTURER_IDX	= 0,
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

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


#1278747 — [PATCH v2 29/29] usb: gadget: f_serial: conversion to new API

FromRobert Baldyga <r.baldyga@samsung.com>
Date2015-11-27 12:10 +0100
Subject[PATCH v2 29/29] usb: gadget: f_serial: conversion to new API
Message-ID<qzp4E-243-75@gated-at.bofh.it>
In reply to#1278738
Generate descriptors in new format and attach them to USB function in
prep_descs(). Change set_alt() implementation and implement clear_alt()
operation. Remove boilerplate code.

Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
---
 drivers/usb/gadget/function/f_serial.c | 122 +++++++--------------------------
 1 file changed, 25 insertions(+), 97 deletions(-)

diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c
index 6bb44d61..526e664 100644
--- a/drivers/usb/gadget/function/f_serial.c
+++ b/drivers/usb/gadget/function/f_serial.c
@@ -69,13 +69,6 @@ static struct usb_endpoint_descriptor gser_fs_out_desc = {
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 };
 
-static struct usb_descriptor_header *gser_fs_function[] = {
-	(struct usb_descriptor_header *) &gser_interface_desc,
-	(struct usb_descriptor_header *) &gser_fs_in_desc,
-	(struct usb_descriptor_header *) &gser_fs_out_desc,
-	NULL,
-};
-
 /* high speed support: */
 
 static struct usb_endpoint_descriptor gser_hs_in_desc = {
@@ -92,13 +85,6 @@ static struct usb_endpoint_descriptor gser_hs_out_desc = {
 	.wMaxPacketSize =	cpu_to_le16(512),
 };
 
-static struct usb_descriptor_header *gser_hs_function[] = {
-	(struct usb_descriptor_header *) &gser_interface_desc,
-	(struct usb_descriptor_header *) &gser_hs_in_desc,
-	(struct usb_descriptor_header *) &gser_hs_out_desc,
-	NULL,
-};
-
 static struct usb_endpoint_descriptor gser_ss_in_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
@@ -118,14 +104,16 @@ static struct usb_ss_ep_comp_descriptor gser_ss_bulk_comp_desc = {
 	.bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
 };
 
-static struct usb_descriptor_header *gser_ss_function[] = {
-	(struct usb_descriptor_header *) &gser_interface_desc,
-	(struct usb_descriptor_header *) &gser_ss_in_desc,
-	(struct usb_descriptor_header *) &gser_ss_bulk_comp_desc,
-	(struct usb_descriptor_header *) &gser_ss_out_desc,
-	(struct usb_descriptor_header *) &gser_ss_bulk_comp_desc,
-	NULL,
-};
+USB_COMPOSITE_ENDPOINT(ep_in, &gser_fs_in_desc, &gser_hs_in_desc,
+		&gser_ss_in_desc, &gser_ss_bulk_comp_desc);
+USB_COMPOSITE_ENDPOINT(ep_out, &gser_fs_out_desc, &gser_hs_out_desc,
+		&gser_ss_out_desc, &gser_ss_bulk_comp_desc);
+
+USB_COMPOSITE_ALTSETTING(intf0alt0, &gser_interface_desc, &ep_in, &ep_out);
+
+USB_COMPOSITE_INTERFACE(intf0, &intf0alt0);
+
+USB_COMPOSITE_DESCRIPTORS(serial_descs, &intf0);
 
 /* string descriptors: */
 
@@ -151,28 +139,21 @@ static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
 	struct f_gser		*gser = func_to_gser(f);
 	struct usb_composite_dev *cdev = f->config->cdev;
 
-	/* we know alt == 0, so this is an activation or a reset */
-
-	if (gser->port.in->enabled) {
-		dev_dbg(&cdev->gadget->dev,
-			"reset generic ttyGS%d\n", gser->port_num);
-		gserial_disconnect(&gser->port);
-	}
-	if (!gser->port.in->desc || !gser->port.out->desc) {
-		dev_dbg(&cdev->gadget->dev,
+	dev_dbg(&cdev->gadget->dev,
 			"activate generic ttyGS%d\n", gser->port_num);
-		if (config_ep_by_speed(cdev->gadget, f, gser->port.in) ||
-		    config_ep_by_speed(cdev->gadget, f, gser->port.out)) {
-			gser->port.in->desc = NULL;
-			gser->port.out->desc = NULL;
-			return -EINVAL;
-		}
-	}
+
+	gser->port.in  = usb_function_get_ep(f, intf, 0);
+	if (!gser->port.in)
+		return -ENODEV;
+	gser->port.out = usb_function_get_ep(f, intf, 0);
+	if (!gser->port.out)
+		return -ENODEV;
+
 	gserial_connect(&gser->port, gser->port_num);
 	return 0;
 }
 
-static void gser_disable(struct usb_function *f)
+static void gser_clear_alt(struct usb_function *f, unsigned intf, unsigned alt)
 {
 	struct f_gser	*gser = func_to_gser(f);
 	struct usb_composite_dev *cdev = f->config->cdev;
@@ -186,12 +167,9 @@ static void gser_disable(struct usb_function *f)
 
 /* serial function driver setup/binding */
 
-static int gser_bind(struct usb_configuration *c, struct usb_function *f)
+static int gser_prep_descs(struct usb_function *f)
 {
-	struct usb_composite_dev *cdev = c->cdev;
-	struct f_gser		*gser = func_to_gser(f);
 	int			status;
-	struct usb_ep		*ep;
 
 	/* REVISIT might want instance-specific strings to help
 	 * distinguish instances ...
@@ -199,57 +177,13 @@ static int gser_bind(struct usb_configuration *c, struct usb_function *f)
 
 	/* maybe allocate device-global string ID */
 	if (gser_string_defs[0].id == 0) {
-		status = usb_string_id(c->cdev);
+		status = usb_string_id(f->config->cdev);
 		if (status < 0)
 			return status;
 		gser_string_defs[0].id = status;
 	}
 
-	/* allocate instance-specific interface IDs */
-	status = usb_interface_id(c, f);
-	if (status < 0)
-		goto fail;
-	gser->data_id = status;
-	gser_interface_desc.bInterfaceNumber = status;
-
-	status = -ENODEV;
-
-	/* allocate instance-specific endpoints */
-	ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_in_desc);
-	if (!ep)
-		goto fail;
-	gser->port.in = ep;
-
-	ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_out_desc);
-	if (!ep)
-		goto fail;
-	gser->port.out = ep;
-
-	/* support all relevant hardware speeds... we expect that when
-	 * hardware is dual speed, all bulk-capable endpoints work at
-	 * both speeds
-	 */
-	gser_hs_in_desc.bEndpointAddress = gser_fs_in_desc.bEndpointAddress;
-	gser_hs_out_desc.bEndpointAddress = gser_fs_out_desc.bEndpointAddress;
-
-	gser_ss_in_desc.bEndpointAddress = gser_fs_in_desc.bEndpointAddress;
-	gser_ss_out_desc.bEndpointAddress = gser_fs_out_desc.bEndpointAddress;
-
-	status = usb_assign_descriptors(f, gser_fs_function, gser_hs_function,
-			gser_ss_function);
-	if (status)
-		goto fail;
-	dev_dbg(&cdev->gadget->dev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n",
-		gser->port_num,
-		gadget_is_superspeed(c->cdev->gadget) ? "super" :
-		gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
-		gser->port.in->name, gser->port.out->name);
-	return 0;
-
-fail:
-	ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
-
-	return status;
+	return usb_function_set_descs(f, &serial_descs);
 }
 
 static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item)
@@ -325,11 +259,6 @@ static void gser_free(struct usb_function *f)
 	kfree(serial);
 }
 
-static void gser_unbind(struct usb_configuration *c, struct usb_function *f)
-{
-	usb_free_all_descriptors(f);
-}
-
 static struct usb_function *gser_alloc(struct usb_function_instance *fi)
 {
 	struct f_gser	*gser;
@@ -346,10 +275,9 @@ static struct usb_function *gser_alloc(struct usb_function_instance *fi)
 
 	gser->port.func.name = "gser";
 	gser->port.func.strings = gser_strings;
-	gser->port.func.bind = gser_bind;
-	gser->port.func.unbind = gser_unbind;
+	gser->port.func.prep_descs = gser_prep_descs;
 	gser->port.func.set_alt = gser_set_alt;
-	gser->port.func.disable = gser_disable;
+	gser->port.func.clear_alt = gser_clear_alt;
 	gser->port.func.free_func = gser_free;
 
 	return &gser->port.func;
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

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


#1278748 — [PATCH v2 19/29] usb: gadget: f_loopback: convert to new API

FromRobert Baldyga <r.baldyga@samsung.com>
Date2015-11-27 12:10 +0100
Subject[PATCH v2 19/29] usb: gadget: f_loopback: convert to new API
Message-ID<qzp4C-243-9@gated-at.bofh.it>
In reply to#1278738
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 7d1fa10..14ba8a9 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

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web