All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/7] The FunctionFS composite function
@ 2010-04-07 13:41 Michal Nazarewicz
  2010-04-07 13:41 ` [PATCH 1/8] USB: composite: allow optional removal of __init and __exit tags Michal Nazarewicz
  0 siblings, 1 reply; 47+ messages in thread
From: Michal Nazarewicz @ 2010-04-07 13:41 UTC (permalink / raw)
  To: linux-usb
  Cc: Peter Korsgaard, Rupesh Gujare, linux-kernel, David Brownell,
	Kyungmin Park, Marek Szyprowski, Michal Nazarewicz

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: TEXT/PLAIN; CHARSET=EUC-KR, Size: 8189 bytes --]

Hello everyone,

*Prologue*

Patches that follow implement the FunctionFS composite function that
is a conversion of GadgetFS to use the composite framework.  Possible
uses are:

1. what Peter Korsgaard has described[1]:

> I could certainly imagine doing something like that, E.G. reuse the
> existing g_serial functionality while implementing E.G. a HID device
> though gadgetfs.

2. or what Rupesh Gujare has described[2]:

> In my requirement, I want to have Media Transfer Protocol (MTP)
> (which uses user level usb driver (usb.c) & gadgetfs) along with
> modem functionality(f_acm.c).
>
> Currently either of them ie. f_acm (as part of composite driver
> along with ums) or gadgetfs (as independent module) can be loaded,
> which allows only one active USB function at a time.
>
> If gadgetfs can be made as a composite function then I can have both
> functionality at same time.


*The Patches*

First five patches implement the FunctionFS and a composite gadget
that uses FunctionFS and Ethernet function (with the later function
optionally disabled via Kconfig).

The first patch has been submitted to the list some time ago and now
I resubmit it with the FunctionFS since it is required.

The last three patches provide a testing code for the FunctionFS.
Those are not really intended to be included in the Linux source tree
but provided for anyone who might want to test it.


*How FunctionFS works*

(Copied from second patch commit message.)

>From kernel point of view it is just a composite function with some
unique behaviour.  It may be added to an USB configuration only after
the user space driver has registered by writing descriptors and
strings (the user space program has to provide the same information
that kernel level composite functions provide when they are added to
the configuration).

This in particular means that the composite initialisation functions
may not be in init section (ie. may not use the __init tag) hence the
first and fourth patch in the series.


>From user space point of view it is a file system which when
mounted provide an "ep0" file.  User space driver need to
write descriptors and strings to that file.  It does not need
to worry about endpoints, interfaces or strings numbers but
simply provide descriptors such as if the function was the
only one (endpoints and strings numbers starting from one and
interface numbers starting from core).  The FunctionFS changes
numbers of those as needed also handling situation when
numbers differ in different configurations.

When descriptors and strings are written "ep#" files appear
(one for each declared endpoint) which handle communication on
a single endpoint.  Again, FunctionFS takes care of the real
numbers and changing of the configuration (which means that
"ep1" file may be really mapped to (say) endpoint 3 (and when
configuration changes to (say) endpoint 2)).  "ep0" is used
for receiving events and handling setup requests.

When all files are closed the function disables itself.


*Testing*

The fifth patch implement a simple source/sink FunctionFS driver based
on similar driver for GadgetFS by David Brownell[3].  It registers
a dual-speed function with a single IN and single OUT endpoints.

The sixth and seventh patch provide a host-side testing code.  This is
what David Brownell has created a while back[4] with a simple fix to
make the tool detect the number of our source/sink interface.

Still, you will need to configure the gadget to report idProduct ==
0xa4a4 (an "echo 0xa4a4 >/sys/module/g_ffs/parameters/usb_product"
should suffice) or configure host to handle 0x0525:0xa4ac devices
using the usbtest driver.

Hence, the simplest way to run the test is to do the following:

* On target (machine that runs has the gadget) as root:
  $ echo 0xa4a4 >/sys/module/g_ffs/parameters/usb_product &&
  $ mkdir /dev/ffs &&
  $ mount -t functionfs ffs /dev/ffs &&
  $ cd /dev/ffs &&
  $ /path/to/ffs-test
* On host (as root):
  $ testusb -a

At this point I have to admit that communication on EP0 has not yet
been tested, so beware of bugs there.


*Request for Comments and Future Work*

Regarding presented version there are two aspects I'd like to discuss.

1. First of all, the current code uses similar approach GadgetFS
   used -- there is a single file ("ep0" in case of FunctionFS and
   named after the controller in case of GadgetFS) that is used to
   receive events from kernel and handle ep0 communication.

   I think it is not the best approach as it'd be simpler and cleaner
   if there were two files: one for receiving events and another for
   handling ep0 communication.

   What do you think? Should I keep the current version or change to
   code to use two files?

2. What still needs to be implemented is a mechanism allowing double
   buffering (and in effect transmission without pauses) and maybe
   single-thread user-space driver implementation.

   I'd like to ask what would be the best way to achieve this.
   GadgetFS implements asynchronous I/O -- is it still the best
   option?

3. The last thing I'd like to mention is that the FunctionFS is
   designed in such a way that with some more work it will be able
   to mount it several times so in the end a gadget could use several
   FunctionFS functions.

   The idea is that each FunctionFS instance is identified by the
   device name used when mounting.

   One can imagine a gadget that has an Ethernet, MTP and HID
   interfaces where the last two are implemented via FunctionFS.  On
   user space level it would look like this:

   $ modprobe g_foo
   $ mkdir /dev/ffs-mtp && mount -t functionfs mtp /dev/ffs-mtp
   $ ( cd /dev/ffs-mtp && mtp-daemon ) &
   $ mkdir /dev/ffs-hid && mount -t functionfs hid /dev/ffs-hid
   $ ( cd /dev/ffs-hid && hid-daemon ) &

   On kernel level the gadget would check ffs_data->dev_name to
   identify whether it's FunctionFS designed for MTP ("mtp") or HID
   ("hid").


PS. Peter, Rupesh, due to your ealier interest in referenced threads
([1] and [2]) I took the liberty of ccing you.

____________________________________________________________
[1] http://article.gmane.org/gmane.linux.usb.general/23890
[2] http://article.gmane.org/gmane.linux.usb.general/23902
[3] http://www.linux-usb.org/gadget/usb.c
[4] http://www.linux-usb.org/usbtest/testusb.c


Michal Nazarewicz (8):
  USB: composite: allow optional removal of __init and __exit tags
  sched: __wake_up_locked() exported
  USB: f_fs: the FunctionFS driver
  USB: Ethernet: allow optional removal of __init and __init_data tags
  USB: g_ffs: the FunctionFS gadget driver
  USB: ffs-test: FunctionFS testing program
  USB: testusb: imported David Brownell's USB testing application
  USB: testusb: testusb compatibility with FunctionFS gadget

 drivers/usb/gadget/Kconfig         |   21 +-
 drivers/usb/gadget/Makefile        |    2 +
 drivers/usb/gadget/composite.c     |   25 +-
 drivers/usb/gadget/config.c        |    6 +-
 drivers/usb/gadget/epautoconf.c    |   13 +-
 drivers/usb/gadget/f_acm.c         |   34 +-
 drivers/usb/gadget/f_ecm.c         |   33 +-
 drivers/usb/gadget/f_fs.c          | 2475 ++++++++++++++++++++++++++++++++++++
 drivers/usb/gadget/f_rndis.c       |   34 +-
 drivers/usb/gadget/g_ffs.c         |  322 +++++
 drivers/usb/gadget/mass_storage.c  |    4 +
 drivers/usb/gadget/u_ether.c       |    6 +-
 drivers/usb/gadget/usb-init-exit.h |   23 +
 include/linux/usb/functionfs.h     |  199 +++
 kernel/sched.c                     |    1 +
 tools/usb/ffs-test.c               |  554 ++++++++
 tools/usb/testusb.c                |  489 +++++++
 17 files changed, 4170 insertions(+), 71 deletions(-)
 create mode 100644 drivers/usb/gadget/f_fs.c
 create mode 100644 drivers/usb/gadget/g_ffs.c
 create mode 100644 drivers/usb/gadget/usb-init-exit.h
 create mode 100644 include/linux/usb/functionfs.h
 create mode 100644 tools/usb/ffs-test.c
 create mode 100644 tools/usb/testusb.c

-- 
Best regards,                                           _     _
 .o. | Liege of Serenely Enlightened Majesty of       o' \,=./ `o
 ..o | Computer Science,  Michał "mina86" Nazarewicz     (o o)
 ooo +---[mina86@mina86.com]---[mina86@jabber.org]---ooO--(_)--Ooo--

^ permalink raw reply	[flat|nested] 47+ messages in thread

* [PATCH 1/8] USB: composite: allow optional removal of __init and __exit tags
  2010-04-07 13:41 [PATCH 0/7] The FunctionFS composite function Michal Nazarewicz
@ 2010-04-07 13:41 ` Michal Nazarewicz
  2010-04-07 13:41   ` [PATCH 2/8] sched: __wake_up_locked() exported Michal Nazarewicz
  2010-04-07 15:28   ` [PATCH 1/8] USB: composite: allow optional removal of __init and __exit tags Greg KH
  0 siblings, 2 replies; 47+ messages in thread
From: Michal Nazarewicz @ 2010-04-07 13:41 UTC (permalink / raw)
  To: linux-usb
  Cc: Peter Korsgaard, Rupesh Gujare, linux-kernel, David Brownell,
	Kyungmin Park, Marek Szyprowski, Michal Nazarewicz

The composite framework has been written using __init and __exit tags
to mark init and exit functions as such.  This works with most of the
composite gadgets however some may need to call init/exit functions
during normal operations.  One example is mass storage gadget which
needs to call exit functions.

This patch allows gadgets to define USB_NO_INIT_SEGMENT or
USB_NO_EXIT_SEGMENT to remove the __init and __exit declarations
from composite framework.

For example see mass_storage.c.

Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com>
Cc: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
---
 drivers/usb/gadget/composite.c     |   25 +++++++++++++------------
 drivers/usb/gadget/config.c        |    6 ++++--
 drivers/usb/gadget/epautoconf.c    |   13 +++++++------
 drivers/usb/gadget/mass_storage.c  |    4 ++++
 drivers/usb/gadget/usb-init-exit.h |   23 +++++++++++++++++++++++
 5 files changed, 51 insertions(+), 20 deletions(-)
 create mode 100644 drivers/usb/gadget/usb-init-exit.h

diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 09289bb..0039aba 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -27,6 +27,8 @@
 
 #include <linux/usb/composite.h>
 
+#include "usb-init-exit.h"
+
 
 /*
  * The code in this file is utility code, used to build a gadget driver
@@ -85,7 +87,7 @@ MODULE_PARM_DESC(iSerialNumber, "SerialNumber string");
  * This function returns the value of the function's bind(), which is
  * zero for success else a negative errno value.
  */
-int __init usb_add_function(struct usb_configuration *config,
+int __usb_init usb_add_function(struct usb_configuration *config,
 		struct usb_function *function)
 {
 	int	value = -EINVAL;
@@ -215,7 +217,7 @@ int usb_function_activate(struct usb_function *function)
  * Returns the interface ID which was allocated; or -ENODEV if no
  * more interface IDs can be allocated.
  */
-int __init usb_interface_id(struct usb_configuration *config,
+int __usb_init usb_interface_id(struct usb_configuration *config,
 		struct usb_function *function)
 {
 	unsigned id = config->next_interface_id;
@@ -480,7 +482,7 @@ done:
  * assigns global resources including string IDs, and per-configuration
  * resources such as interface IDs and endpoints.
  */
-int __init usb_add_config(struct usb_composite_dev *cdev,
+int __usb_init usb_add_config(struct usb_composite_dev *cdev,
 		struct usb_configuration *config)
 {
 	int				status = -EINVAL;
@@ -677,7 +679,7 @@ static int get_string(struct usb_composite_dev *cdev,
  * ensure that for example different functions don't wrongly assign
  * different meanings to the same identifier.
  */
-int __init usb_string_id(struct usb_composite_dev *cdev)
+int __usb_init usb_string_id(struct usb_composite_dev *cdev)
 {
 	if (cdev->next_string_id < 254) {
 		/* string id 0 is reserved */
@@ -898,7 +900,7 @@ static void composite_disconnect(struct usb_gadget *gadget)
 
 /*-------------------------------------------------------------------------*/
 
-static void /* __init_or_exit */
+static void __usb_init_or_exit
 composite_unbind(struct usb_gadget *gadget)
 {
 	struct usb_composite_dev	*cdev = get_gadget_data(gadget);
@@ -947,7 +949,7 @@ composite_unbind(struct usb_gadget *gadget)
 	composite = NULL;
 }
 
-static void __init
+static void __usb_init
 string_override_one(struct usb_gadget_strings *tab, u8 id, const char *s)
 {
 	struct usb_string		*str = tab->strings;
@@ -960,7 +962,7 @@ string_override_one(struct usb_gadget_strings *tab, u8 id, const char *s)
 	}
 }
 
-static void __init
+static void __usb_init
 string_override(struct usb_gadget_strings **tab, u8 id, const char *s)
 {
 	while (*tab) {
@@ -969,7 +971,7 @@ string_override(struct usb_gadget_strings **tab, u8 id, const char *s)
 	}
 }
 
-static int __init composite_bind(struct usb_gadget *gadget)
+static int __usb_init composite_bind(struct usb_gadget *gadget)
 {
 	struct usb_composite_dev	*cdev;
 	int				status = -ENOMEM;
@@ -1092,8 +1094,7 @@ static struct usb_gadget_driver composite_driver = {
 	.speed		= USB_SPEED_HIGH,
 
 	.bind		= composite_bind,
-	/* .unbind		= __exit_p(composite_unbind), */
-	.unbind		= composite_unbind,
+	.unbind		= __usb_exit_p(composite_unbind),
 
 	.setup		= composite_setup,
 	.disconnect	= composite_disconnect,
@@ -1121,7 +1122,7 @@ static struct usb_gadget_driver composite_driver = {
  * while it was binding.  That would usually be done in order to wait for
  * some userspace participation.
  */
-int __init usb_composite_register(struct usb_composite_driver *driver)
+int __usb_init usb_composite_register(struct usb_composite_driver *driver)
 {
 	if (!driver || !driver->dev || !driver->bind || composite)
 		return -EINVAL;
@@ -1142,7 +1143,7 @@ int __init usb_composite_register(struct usb_composite_driver *driver)
  * This function is used to unregister drivers using the composite
  * driver framework.
  */
-void /* __exit */ usb_composite_unregister(struct usb_composite_driver *driver)
+void __usb_exit usb_composite_unregister(struct usb_composite_driver *driver)
 {
 	if (composite != driver)
 		return;
diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c
index 47e8e72..2f15ee7 100644
--- a/drivers/usb/gadget/config.c
+++ b/drivers/usb/gadget/config.c
@@ -28,6 +28,8 @@
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
 
+#include "usb-init-exit.h"
+
 
 /**
  * usb_descriptor_fillbuf - fill buffer with descriptors
@@ -128,7 +130,7 @@ int usb_gadget_config_buf(
  * with identifiers (for interfaces, strings, endpoints, and more)
  * as needed by a given function instance.
  */
-struct usb_descriptor_header **__init
+struct usb_descriptor_header **__usb_init
 usb_copy_descriptors(struct usb_descriptor_header **src)
 {
 	struct usb_descriptor_header **tmp;
@@ -175,7 +177,7 @@ usb_copy_descriptors(struct usb_descriptor_header **src)
  * intended use is to help remembering the endpoint descriptor to use
  * when enabling a given endpoint.
  */
-struct usb_endpoint_descriptor *__init
+struct usb_endpoint_descriptor *__usb_init
 usb_find_endpoint(
 	struct usb_descriptor_header **src,
 	struct usb_descriptor_header **copy,
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index 3568de2..706af4c 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -31,15 +31,16 @@
 #include <linux/usb/gadget.h>
 
 #include "gadget_chips.h"
+#include "usb-init-exit.h"
 
 
 /* we must assign addresses for configurable endpoints (like net2280) */
-static __initdata unsigned epnum;
+static __usb_initdata unsigned epnum;
 
 // #define MANY_ENDPOINTS
 #ifdef MANY_ENDPOINTS
 /* more than 15 configurable endpoints */
-static __initdata unsigned in_epnum;
+static __usb_initdata unsigned in_epnum;
 #endif
 
 
@@ -59,7 +60,7 @@ static __initdata unsigned in_epnum;
  * NOTE:  each endpoint is unidirectional, as specified by its USB
  * descriptor; and isn't specific to a configuration or altsetting.
  */
-static int __init
+static int __usb_init
 ep_matches (
 	struct usb_gadget		*gadget,
 	struct usb_ep			*ep,
@@ -187,7 +188,7 @@ ep_matches (
 	return 1;
 }
 
-static struct usb_ep * __init
+static struct usb_ep * __usb_init
 find_ep (struct usb_gadget *gadget, const char *name)
 {
 	struct usb_ep	*ep;
@@ -229,7 +230,7 @@ find_ep (struct usb_gadget *gadget, const char *name)
  *
  * On failure, this returns a null endpoint descriptor.
  */
-struct usb_ep * __init usb_ep_autoconfig (
+struct usb_ep * __usb_init usb_ep_autoconfig (
 	struct usb_gadget		*gadget,
 	struct usb_endpoint_descriptor	*desc
 )
@@ -304,7 +305,7 @@ struct usb_ep * __init usb_ep_autoconfig (
  * state such as ep->driver_data and the record of assigned endpoints
  * used by usb_ep_autoconfig().
  */
-void __init usb_ep_autoconfig_reset (struct usb_gadget *gadget)
+void __usb_init usb_ep_autoconfig_reset (struct usb_gadget *gadget)
 {
 	struct usb_ep	*ep;
 
diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c
index 705cc1f..438a395 100644
--- a/drivers/usb/gadget/mass_storage.c
+++ b/drivers/usb/gadget/mass_storage.c
@@ -57,6 +57,10 @@
  * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
  */
 
+/* Do not mark composite_unbind() and usb_composite_unregister() as
+ * __exit, we may need it even if we are not unloaded. */
+#define USB_NO_EXIT_SEGMENT 1
+
 #include "composite.c"
 #include "usbstring.c"
 #include "config.c"
diff --git a/drivers/usb/gadget/usb-init-exit.h b/drivers/usb/gadget/usb-init-exit.h
new file mode 100644
index 0000000..04cd624
--- /dev/null
+++ b/drivers/usb/gadget/usb-init-exit.h
@@ -0,0 +1,23 @@
+#ifndef __usb_init
+#  if defined USB_NO_INIT_SEGMENT
+#    define __usb_init          __cold
+#    define __usb_initdata
+#  else
+#    define __usb_init          __init
+#    define __usb_initdata      __initdata
+#  endif
+#  if defined USB_NO_EXIT_SEGMENT
+#    define __usb_exit          __cold
+#    define __usb_exit_p(func)  func
+#    define __usb_exitdata
+#  else
+#    define __usb_exit          __exit
+#    define __usb_exit_p(func)  __exit_p(func)
+#    define __usb_exitdata      __exitdata
+#  endif
+#  if defined USB_NO_INIT_SEGMENT || defined USB_NO_EXIT_SEGMENT
+#    define __usb_init_or_exit  __cold
+#  else
+#    define __usb_init_or_exit  /* __init_or_exit */
+#  endif
+#endif
-- 
1.7.0


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [PATCH 2/8] sched: __wake_up_locked() exported
  2010-04-07 13:41 ` [PATCH 1/8] USB: composite: allow optional removal of __init and __exit tags Michal Nazarewicz
@ 2010-04-07 13:41   ` Michal Nazarewicz
  2010-04-07 13:41     ` [PATCH 3/8] USB: f_fs: the FunctionFS driver Michal Nazarewicz
  2010-04-07 15:29     ` [PATCH 2/8] sched: __wake_up_locked() exported Greg KH
  2010-04-07 15:28   ` [PATCH 1/8] USB: composite: allow optional removal of __init and __exit tags Greg KH
  1 sibling, 2 replies; 47+ messages in thread
From: Michal Nazarewicz @ 2010-04-07 13:41 UTC (permalink / raw)
  To: linux-usb
  Cc: Peter Korsgaard, Rupesh Gujare, linux-kernel, David Brownell,
	Kyungmin Park, Marek Szyprowski, Michal Nazarewicz

The __wake_up_locked() function has been exported in case modules need it.

Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com>
Cc: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
---
 kernel/sched.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/kernel/sched.c b/kernel/sched.c
index 0a1fce0..5fb8a37 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -3940,6 +3940,7 @@ void __wake_up_locked(wait_queue_head_t *q, unsigned int mode)
 {
 	__wake_up_common(q, mode, 1, 0, NULL);
 }
+EXPORT_SYMBOL_GPL(__wake_up_locked);
 
 void __wake_up_locked_key(wait_queue_head_t *q, unsigned int mode, void *key)
 {
-- 
1.7.0


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [PATCH 3/8] USB: f_fs: the FunctionFS driver
  2010-04-07 13:41   ` [PATCH 2/8] sched: __wake_up_locked() exported Michal Nazarewicz
@ 2010-04-07 13:41     ` Michal Nazarewicz
  2010-04-07 13:41       ` [PATCH 4/8] USB: Ethernet: allow optional removal of __init and __init_data tags Michal Nazarewicz
  2010-04-07 17:11       ` [PATCH 3/8] USB: f_fs: the FunctionFS driver Michał Nazarewicz
  2010-04-07 15:29     ` [PATCH 2/8] sched: __wake_up_locked() exported Greg KH
  1 sibling, 2 replies; 47+ messages in thread
From: Michal Nazarewicz @ 2010-04-07 13:41 UTC (permalink / raw)
  To: linux-usb
  Cc: Peter Korsgaard, Rupesh Gujare, linux-kernel, David Brownell,
	Kyungmin Park, Marek Szyprowski, Michal Nazarewicz

The FunctionFS is a USB composite function that can be used
with the composite framework to create an USB gadget.

>From kernel point of view it is just a composite function with
some unique behaviour.  It may be added to an USB
configuration only after the user space driver has registered
by writing descriptors and strings (the user space program has
to provide the same information that kernel level composite
functions provide when they are added to the configuration).

>From user space point of view it is a file system which when
mounted provide an "ep0" file.  User space driver need to
write descriptors and strings to that file.  It does not need
to worry about endpoints, interfaces or strings numbers but
simply provide descriptors such as if the function was the
only one (endpoints and strings numbers starting from one and
interface numbers starting from core).  The FunctionFS changes
numbers of those as needed also handling situation when
numbers differ in different configurations.

When descriptors and strings are written "ep#" files appear
(one for each declared endpoint) which handle communication on
a single endpoint.  Again, FunctionFS takes care of the real
numbers and changing of the configuration (which means that
"ep1" file may be really mapped to (say) endpoint 3 (and when
configuration changes to (say) endpoint 2)).  "ep0" is used
for receiving events and handling setup requests.

When all files are closed the function disables itself.

Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com>
Cc: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
---
 drivers/usb/gadget/f_fs.c      | 2475 ++++++++++++++++++++++++++++++++++++++++
 include/linux/usb/functionfs.h |  199 ++++
 2 files changed, 2674 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/gadget/f_fs.c
 create mode 100644 include/linux/usb/functionfs.h

diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c
new file mode 100644
index 0000000..d218a40
--- /dev/null
+++ b/drivers/usb/gadget/f_fs.c
@@ -0,0 +1,2475 @@
+/*
+ * f_fs.c -- user mode filesystem api for usb composite funtcion controllers
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ * Author: Michal Nazarewicz <m.nazarewicz@samsung.com>
+ *
+ * Based on inode.c (GadgetFS):
+ * Copyright (C) 2003-2004 David Brownell
+ * Copyright (C) 2003 Agilent Technologies
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+/* #define DEBUG */
+/* #define VERBOSE_DEBUG */
+
+#include <linux/blkdev.h>
+#include <asm/unaligned.h>
+#include <linux/smp_lock.h>
+
+#include <linux/usb/composite.h>
+#include <linux/usb/functionfs.h>
+
+
+#define FUNCTIONFS_MAGIC	0xa647361 /* Chosen by a honest dice roll ;) */
+
+
+/* Debuging *****************************************************************/
+
+#define ffs_printk(level, fmt, args...) printk(level "f_fs: " fmt "\n", ## args)
+
+#define FERR(...)  ffs_printk(KERN_ERR,  __VA_ARGS__)
+#define FINFO(...) ffs_printk(KERN_INFO, __VA_ARGS__)
+
+#ifdef DEBUG
+#  define FDBG(...) ffs_printk(KERN_DEBUG, __VA_ARGS__)
+#else
+#  define FDBG(...) do { } while (0)
+#endif /* DEBUG */
+
+#ifdef VERBOSE_DEBUG
+#  define FVDBG FDBG
+#else
+#  define FVDBG(...) do { } while (0)
+#endif /* VERBOSE_DEBUG */
+
+#define ENTER()    FVDBG("%s()", __func__)
+
+#ifdef VERBOSE_DEBUG
+#  define ffs_dump_mem(prefix, ptr, len) \
+	print_hex_dump_bytes("f_fs" prefix ": ", DUMP_PREFIX_NONE, ptr, len)
+#else
+#  define ffs_dump_mem(prefix, ptr, len) do { } while (0)
+#endif
+
+
+/* The data structure and setup file ****************************************/
+
+enum ffs_state {
+	/* Waiting for descriptors and strings. */
+	/* In this state no open(2), read(2) or write(2) on epfiles
+	 * may succeed (which should not be the problem as there
+	 * should be no such files opened in the firts place). */
+	FFS_READ_DESCRIPTORS,
+	FFS_READ_STRINGS,
+
+	/* We've got descriptors and strings.  We are or have called
+	 * functionfs_ready_callback().  functionfs_bind() may have
+	 * been called but we don't know. */
+	/* This is the only state in which operations on epfiles may
+	 * succeed. */
+	FFS_ACTIVE,
+
+	/* All endpoints have been closed.  This state is also set if
+	 * we encounter an unrecoverable error.  The only
+	 * unrecoverable error is situation when after reading strings
+	 * from user space we fail to initialise EP files or
+	 * functionfs_ready_callback() returns with error (<0). */
+	/* In this state no open(2), read(2) or write(2) (both on ep0
+	 * as well as epfile) may succeed (at this point epfiles are
+	 * unlinked and all closed so this is not a problem; ep0 is
+	 * also closed but ep0 file exists and so open(2) on ep0 must
+	 * fail). */
+	FFS_CLOSING
+};
+
+
+enum ffs_setup_state {
+	/* There is no setup request pending. */
+	FFS_NO_SETUP,
+	/* User has read events and there was a setup request event
+	 * there.  The next read/write on ep0 will handle the
+	 * request. */
+	FFS_SETUP_PENDING,
+	/* There was event pending but before user space handled it
+	 * some other event was introduced which canceled existing
+	 * setup.  If this state is set read/write on ep0 return
+	 * -EIDRM.  This state is only set when adding event. */
+	FFS_SETUP_CANCELED
+};
+
+
+
+struct ffs_epfile;
+struct ffs_function;
+
+struct ffs_data {
+	struct usb_gadget		*gadget;
+
+	/* Protect access read/write operations, only one read/write
+	 * at a time.  As a consequence protects ep0req and company.
+	 * While setup request is being processed (queued) this is
+	 * held. */
+	struct mutex			mutex;
+
+	/* Protect access to enpoint related structures (basically
+	 * usb_ep_queue(), usb_ep_dequeue(), etc. calls) except for
+	 * endpint zero. */
+	spinlock_t			eps_lock;
+
+	/* XXX REVISIT do we need our own request? Since we are not
+	 * handling setup requests immidiatelly user space may be so
+	 * slow that another setup will be sent to the gadget but this
+	 * time not to us but another function and then there could be
+	 * a race.  Is taht the case? Or maybe we can use cdev->req
+	 * after all, maybe we just need some spinlock for that? */
+	struct usb_request		*ep0req;		/* P: mutex */
+	struct completion		ep0req_completion;	/* P: mutex */
+	int				ep0req_status;		/* P: mutex */
+
+	/* reference counter */
+	atomic_t			ref;
+	/* how many files are opened (EP0 and others) */
+	atomic_t			opened;
+
+	/* EP0 state */
+	enum ffs_state			state;
+
+	/*
+	 * Possible transations:
+	 * + FFS_NO_SETUP       -> FFS_SETUP_PENDING  -- P: ev.waitq.lock
+	 *               happens only in ep0 read which is P: mutex
+	 * + FFS_SETUP_PENDING  -> FFS_NO_SETUP       -- P: ev.waitq.lock
+	 *               happens only in ep0 i/o  which is P: mutex
+	 * + FFS_SETUP_PENDING  -> FFS_SETUP_CANCELED -- P: ev.waitq.lock
+	 * + FFS_SETUP_CANCELED -> FFS_NO_SETUP       -- cmpxchg
+	 */
+	enum ffs_setup_state		setup_state;
+
+#define FFS_SETUP_STATE(ffs)					\
+	((enum ffs_setup_state)cmpxchg(&(ffs)->setup_state,	\
+				       FFS_SETUP_CANCELED, FFS_NO_SETUP))
+
+	/* Events & such. */
+	struct {
+		u8				types[4];
+		unsigned short			count;
+		/* XXX REVISIT need to update it in some places, or do we? */
+		unsigned short			can_stall;
+		struct usb_ctrlrequest		setup;
+
+		wait_queue_head_t		waitq;
+	} ev; /* the whole structure, P: ev.waitq.lock */
+
+	/* Flags */
+	unsigned long			flags;
+#define FFS_FL_CALL_CLOSED_CALLBACK 0
+#define FFS_FL_BOUND                1
+
+	/* Active function */
+	struct ffs_function		*func;
+
+	/* Device name, write once when file system is mounted.
+	 * Intendet for user to read if she wants. */
+	const char			*dev_name;
+	/* Private data for our user (ie. gadget).  Managed by
+	 * user. */
+	void				*private_data;
+
+	/* filled by __ffs_data_got_descs() */
+	/* real descriptors are 16 bytes after raw_descs (so you need
+	 * to skip 16 bytes (ie. ffs->raw_descs + 16) to get to the
+	 * first full speed descriptor).  raw_descs_length and
+	 * raw_fs_descs_length do not have those 16 bytes added. */
+	const void			*raw_descs;
+	unsigned			raw_descs_length;
+	unsigned			raw_fs_descs_length;
+	unsigned			fs_descs_count;
+	unsigned			hs_descs_count;
+
+	unsigned short			strings_count;
+	unsigned short			interfaces_count;
+	unsigned short			eps_count;
+	unsigned short			_pad1;
+
+	/* filled by __ffs_data_got_strings() */
+	/* ids in stringtabs are set in functionfs_bind() */
+	const void			*raw_strings;
+	struct usb_gadget_strings	**stringtabs;
+
+	/* File system's super block, write once when file system is mounted. */
+	struct super_block		*sb;
+
+	/* File permissions, written once when fs is mounted*/
+	struct ffs_file_perms {
+		umode_t				mode;
+		uid_t				uid;
+		gid_t				gid;
+	}				file_perms;
+
+	/* The endpoint files, filled by ffs_epfiles_create(),
+	 * destroyed by ffs_epfiles_destroy(). */
+	struct ffs_epfile		*epfiles;
+};
+
+/* Reference counter handling */
+static void ffs_data_get(struct ffs_data *ffs);
+static void ffs_data_put(struct ffs_data *ffs);
+/* Creates new ffs_data object. */
+static struct ffs_data *__must_check ffs_data_new(void) __attribute__((malloc));
+
+/* Opened counter handling. */
+static void ffs_data_opened(struct ffs_data *ffs);
+static void ffs_data_closed(struct ffs_data *ffs);
+
+/* Called with ffs->mutex held; take over ownerrship of data. */
+static int __must_check
+__ffs_data_got_descs(struct ffs_data *ffs, char *data, size_t len);
+static int __must_check
+__ffs_data_got_strings(struct ffs_data *ffs, char *data, size_t len);
+
+
+/* The function structure ***************************************************/
+
+struct ffs_ep;
+
+struct ffs_function {
+	struct usb_configuration	*conf;
+	struct usb_gadget		*gadget;
+	struct ffs_data			*ffs;
+
+	struct ffs_ep			*eps;
+	u8				eps_revmap[16];
+	short				*interfaces_nums;
+
+	struct usb_function		function;
+};
+
+
+static struct ffs_function *ffs_func_from_usb(struct usb_function *f)
+{
+	return container_of(f, struct ffs_function, function);
+}
+
+static void ffs_func_free(struct ffs_function *func);
+
+
+static void ffs_func_eps_disable(struct ffs_function *func);
+static int __must_check ffs_func_eps_enable(struct ffs_function *func);
+
+
+static int ffs_func_bind(struct usb_configuration *,
+			 struct usb_function *);
+static void ffs_func_unbind(struct usb_configuration *,
+			    struct usb_function *);
+static int ffs_func_set_alt(struct usb_function *, unsigned, unsigned);
+static void ffs_func_disable(struct usb_function *);
+static int ffs_func_setup(struct usb_function *,
+			  const struct usb_ctrlrequest *);
+static void ffs_func_suspend(struct usb_function *);
+static void ffs_func_resume(struct usb_function *);
+
+
+static int ffs_func_revmap_ep(struct ffs_function *func, u8 num);
+static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf);
+
+
+
+/* The endpoints structures *************************************************/
+
+struct ffs_ep {
+	struct usb_ep			*ep;	/* P: ffs->eps_lock */
+	struct usb_request		*req;	/* P: epfile->mutex */
+
+	/* [0]: full speed, [1]: high speed */
+	struct usb_endpoint_descriptor	*descs[2];
+
+	u8				num;
+
+	int				status;	/* P: epfile->mutex */
+};
+
+struct ffs_epfile {
+	/* Protects ep->ep and ep->req. */
+	struct mutex			mutex;
+	wait_queue_head_t		wait;
+
+	struct ffs_data			*ffs;
+	struct ffs_ep			*ep;	/* P: ffs->eps_lock */
+
+	struct dentry			*dentry;
+
+	char				name[5];
+
+	unsigned char			in;	/* P: ffs->eps_lock */
+	unsigned char			isoc;	/* P: ffs->eps_lock */
+
+	unsigned char			_pad;
+};
+
+
+static int  __must_check ffs_epfiles_create(struct ffs_data *ffs);
+static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count);
+
+static struct inode *__must_check
+ffs_sb_create_file(struct super_block *sb, const char *name, void *data,
+		   const struct file_operations *fops,
+		   struct dentry **dentry_p);
+
+
+/* Misc helper functions ****************************************************/
+
+static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock)
+	__attribute__((warn_unused_result, nonnull));
+static char *ffs_prepare_buffer(const char * __user buf, size_t len)
+	__attribute__((warn_unused_result));
+
+
+/* Control file aka ep0 *****************************************************/
+
+static void ffs_ep0_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct ffs_data *ffs = req->context;
+
+	complete_all(&ffs->ep0req_completion);
+}
+
+
+static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
+{
+	struct usb_request *req = ffs->ep0req;
+	int ret;
+
+	req->zero     = len < le16_to_cpu(ffs->ev.setup.wLength);
+
+	spin_unlock_irq(&ffs->ev.waitq.lock);
+
+	req->buf      = data;
+	req->length   = len;
+
+	INIT_COMPLETION(ffs->ep0req_completion);
+
+	ret = usb_ep_queue(ffs->gadget->ep0, req, GFP_ATOMIC);
+	if (unlikely(ret < 0))
+		return ret;
+
+	ret = wait_for_completion_interruptible(&ffs->ep0req_completion);
+	if (unlikely(ret)) {
+		usb_ep_dequeue(ffs->gadget->ep0, req);
+		return -EINTR;
+	}
+
+	ffs->setup_state = FFS_NO_SETUP;
+	return ffs->ep0req_status;
+}
+
+static int __ffs_ep0_stall(struct ffs_data *ffs)
+{
+	if (ffs->ev.can_stall) {
+		FVDBG("ep0 stall\n");
+		usb_ep_set_halt(ffs->gadget->ep0);
+		ffs->setup_state = FFS_NO_SETUP;
+		return -EL2HLT;
+	} else {
+		FDBG("bogus ep0 stall!\n");
+		return -ESRCH;
+	}
+}
+
+
+static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
+			     size_t len, loff_t *ptr)
+{
+	struct ffs_data *ffs = file->private_data;
+	ssize_t ret;
+	char *data;
+
+	ENTER();
+
+	/* Fast check if setup was canceled */
+	if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED)
+		return -EIDRM;
+
+	/* Acquire mutex */
+	ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
+	if (unlikely(ret < 0))
+		return ret;
+
+
+	/* Check state */
+	switch (ffs->state) {
+	case FFS_READ_DESCRIPTORS:
+	case FFS_READ_STRINGS:
+		/* Copy data */
+		if (unlikely(len < 16)) {
+			ret = -EINVAL;
+			break;
+		}
+
+		data = ffs_prepare_buffer(buf, len);
+		if (unlikely(IS_ERR(data))) {
+			ret = PTR_ERR(data);
+			break;
+		}
+
+		/* Handle data */
+		if (ffs->state == FFS_READ_DESCRIPTORS) {
+			FINFO("read descriptors");
+			ret = __ffs_data_got_descs(ffs, data, len);
+			if (unlikely(ret < 0))
+				break;
+
+			ffs->state = FFS_READ_STRINGS;
+			ret = len;
+		} else {
+			FINFO("read strings");
+			ret = __ffs_data_got_strings(ffs, data, len);
+			if (unlikely(ret < 0))
+				break;
+
+			ret = ffs_epfiles_create(ffs);
+			if (unlikely(ret)) {
+				ffs->state = FFS_CLOSING;
+				break;
+			}
+
+			ffs->state = FFS_ACTIVE;
+			mutex_unlock(&ffs->mutex);
+
+			ret = functionfs_ready_callback(ffs);
+			if (unlikely(ret < 0)) {
+				ffs->state = FFS_CLOSING;
+				return ret;
+			}
+
+			set_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags);
+			return len;
+		}
+		break;
+
+
+	case FFS_ACTIVE:
+		data = NULL;
+		/* We're called from user space, we can use _irq
+		 * rather then _irqsave */
+		spin_lock_irq(&ffs->ev.waitq.lock);
+		switch (FFS_SETUP_STATE(ffs)) {
+		case FFS_SETUP_CANCELED:
+			ret = -EIDRM;
+			goto done_spin;
+
+		case FFS_NO_SETUP:
+			ret = -ESRCH;
+			goto done_spin;
+
+		case FFS_SETUP_PENDING:
+			break;
+		}
+
+		/* FFS_SETUP_PENDING */
+		if (!(ffs->ev.setup.bRequestType & USB_DIR_IN)) {
+			ret = __ffs_ep0_stall(ffs);
+			goto done_spin;
+		}
+
+		/* FFS_SETUP_PENDING and not stall */
+		len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength));
+
+		spin_unlock_irq(&ffs->ev.waitq.lock);
+
+		data = ffs_prepare_buffer(buf, len);
+		if (unlikely(IS_ERR(data))) {
+			ret = PTR_ERR(data);
+			break;
+		}
+
+		spin_lock_irq(&ffs->ev.waitq.lock);
+
+		/* We are guaranteed to be still in FFS_ACTIVE state
+		 * but the state of setup could have changed from
+		 * FFS_SETUP_PENDING to FFS_SETUP_CANCELED so we need
+		 * to check for that.  If that happened we copied data
+		 * from user space in vain but it's unlikely. */
+		/* For sure we are not in FFS_NO_SETUP since this is
+		 * the only place FFS_SETUP_PENDING -> FFS_NO_SETUP
+		 * transition can be performed and it's protected by
+		 * mutex. */
+
+		if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) {
+			ret = -EIDRM;
+done_spin:
+			spin_unlock_irq(&ffs->ev.waitq.lock);
+		} else {
+			/* unlocks spinlock */
+			ret = __ffs_ep0_queue_wait(ffs, data, len);
+		}
+		kfree(data);
+		break;
+
+
+	default:
+		ret = -EBADFD;
+		break;
+	}
+
+
+	mutex_unlock(&ffs->mutex);
+	return ret;
+}
+
+
+
+static int __ffs_ep0_read_wait_for_events(struct ffs_data *ffs)
+{
+	/* We are holding ffs->ev.waitq.lock */
+
+	DEFINE_WAIT(wait);
+	int ret = 0;
+
+	wait.flags |= WQ_FLAG_EXCLUSIVE;
+	__add_wait_queue_tail(&ffs->ev.waitq, &wait);
+
+	do {
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (signal_pending(current)) {
+			ret = -ERESTARTSYS;
+			break;
+		}
+
+		spin_unlock_irq(&ffs->ev.waitq.lock);
+		schedule();
+		spin_lock_irq(&ffs->ev.waitq.lock);
+	} while (!ffs->ev.count);
+
+	__remove_wait_queue(&ffs->ev.waitq, &wait);
+	__set_current_state(TASK_RUNNING);
+
+	return ret;
+}
+
+static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
+				     size_t n)
+{
+	/* We are holding ffs->ev.waitq.lock and ffs->mutex and we need
+	 * to release them. */
+
+	struct usb_functionfs_event events[n];
+	unsigned i = 0;
+
+	memset(events, 0, sizeof events);
+
+	do {
+		events[i].type = ffs->ev.types[i];
+		if (events[i].type == FUNCTIONFS_SETUP) {
+			events[i].u.setup = ffs->ev.setup;
+			ffs->setup_state = FFS_SETUP_PENDING;
+		}
+	} while (++i < n);
+
+	ffs->ev.count = 0;
+
+	spin_unlock_irq(&ffs->ev.waitq.lock);
+	mutex_unlock(&ffs->mutex);
+
+	return unlikely(__copy_to_user(buf, events, sizeof events))
+		? -EFAULT : sizeof events;
+}
+
+
+static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
+			    size_t len, loff_t *ptr)
+{
+	struct ffs_data *ffs = file->private_data;
+	size_t n;
+	char *data;
+	int ret;
+
+	ENTER();
+
+	/* Fast check if setup was canceled */
+	if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED)
+		return -EIDRM;
+
+	/* Acquire mutex */
+	ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
+	if (unlikely(ret < 0))
+		return ret;
+
+
+	/* Check state */
+	if (ffs->state != FFS_ACTIVE) {
+		ret = -EBADFD;
+		goto done_mutex;
+	}
+
+
+	/* We're called from user space, we can use _irq rather then
+	 * _irqsave */
+	spin_lock_irq(&ffs->ev.waitq.lock);
+
+	switch (FFS_SETUP_STATE(ffs)) {
+	case FFS_SETUP_CANCELED:
+		ret = -EIDRM;
+		break;
+
+	case FFS_NO_SETUP:
+		n = len / sizeof(struct usb_functionfs_event);
+		if (unlikely(!n)) {
+			ret = -EINVAL;
+			break;
+		}
+
+		if ((file->f_flags & O_NONBLOCK) && !ffs->ev.count) {
+			ret = -EAGAIN;
+			break;
+		}
+
+		if (!ffs->ev.count &&
+		    unlikely(__ffs_ep0_read_wait_for_events(ffs))) {
+			ret = -EINTR;
+			break;
+		}
+
+		return __ffs_ep0_read_events(ffs, buf,
+					     min(n, (size_t)ffs->ev.count));
+
+
+	case FFS_SETUP_PENDING:
+		if (ffs->ev.setup.bRequestType & USB_DIR_IN) {
+			ret = __ffs_ep0_stall(ffs);
+			break;
+		}
+
+		len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength));
+
+		spin_unlock_irq(&ffs->ev.waitq.lock);
+
+		data = ffs_prepare_buffer(NULL, len);
+		if (unlikely(IS_ERR(data))) {
+			ret = PTR_ERR(data);
+			goto done_mutex;
+		}
+
+		spin_lock_irq(&ffs->ev.waitq.lock);
+
+		/* See ffs_ep0_write() */
+		if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) {
+			kfree(data);
+			ret = -EIDRM;
+			break;
+		}
+
+		/* unlocks spinlock */
+		ret = __ffs_ep0_queue_wait(ffs, data, len);
+		if (likely(ret > 0) && unlikely(__copy_to_user(buf, data, len)))
+			ret = -EFAULT;
+		kfree(data);
+		goto done_mutex;
+
+	default:
+		ret = -EBADFD;
+		break;
+	}
+
+	spin_unlock_irq(&ffs->ev.waitq.lock);
+done_mutex:
+	mutex_unlock(&ffs->mutex);
+	return ret;
+}
+
+
+
+static int ffs_ep0_open(struct inode *inode, struct file *file)
+{
+	struct ffs_data *ffs = inode->i_private;
+
+	ENTER();
+
+	if (unlikely(ffs->state == FFS_CLOSING))
+		return -EBUSY;
+
+	file->private_data = ffs;
+	ffs_data_opened(ffs);
+
+	return 0;
+}
+
+
+static int ffs_ep0_release(struct inode *inode, struct file *file)
+{
+	struct ffs_data *ffs = file->private_data;
+
+	ENTER();
+
+	ffs_data_closed(ffs);
+
+	return 0;
+}
+
+
+static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value)
+{
+	struct ffs_data *ffs = file->private_data;
+	struct usb_gadget *gadget = ffs->gadget;
+	long ret;
+
+	ENTER();
+
+	if (code == FUNCTIONFS_INTERFACE_REVMAP) {
+		struct ffs_function *func = ffs->func;
+		ret = func ? ffs_func_revmap_intf(func, value) : -ENODEV;
+	} else if (gadget->ops->ioctl) {
+		lock_kernel();
+		ret = gadget->ops->ioctl(gadget, code, value);
+		unlock_kernel();
+	} else {
+		ret = -ENOTTY;
+	}
+
+	return ret;
+}
+
+
+static const struct file_operations ffs_ep0_operations = {
+	.owner =	THIS_MODULE,
+	.llseek =	no_llseek,
+
+	.open =		ffs_ep0_open,
+	.write =	ffs_ep0_write,
+	.read =		ffs_ep0_read,
+	.release =	ffs_ep0_release,
+	.unlocked_ioctl =	ffs_ep0_ioctl,
+};
+
+
+/* "Normal" endpoints operations ********************************************/
+
+
+static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req)
+{
+	ENTER();
+	if (likely(req->context)) {
+		struct ffs_ep *ep = _ep->driver_data;
+		ep->status = req->status ? req->status : req->actual;
+		complete(req->context);
+	}
+}
+
+
+static ssize_t ffs_epfile_io(struct file *file,
+			     char __user *buf, size_t len, int read)
+{
+	struct ffs_epfile *epfile = file->private_data;
+	struct ffs_ep *ep;
+	char *data = NULL;
+	ssize_t ret;
+	int halt;
+
+	goto first_try;
+	do {
+		spin_unlock_irq(&epfile->ffs->eps_lock);
+		mutex_unlock(&epfile->mutex);
+
+first_try:
+		/* Are we still active? */
+		if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) {
+			ret = -ENODEV;
+			goto error;
+		}
+
+		/* Wait for endpoint to be enabled */
+		ep = epfile->ep;
+		if (!ep) {
+			if (file->f_flags & O_NONBLOCK) {
+				ret = -EAGAIN;
+				goto error;
+			}
+
+			if (unlikely(wait_event_interruptible
+				     (epfile->wait, (ep = epfile->ep)))) {
+				ret = -EINTR;
+				goto error;
+			}
+		}
+
+		/* Do we halt? */
+		halt = !read == !epfile->in;
+		if (halt && epfile->isoc) {
+			ret = -EINVAL;
+			goto error;
+		}
+
+		/* Allocate & copy */
+		if (!halt && !data) {
+			data = kzalloc(len, GFP_KERNEL);
+			if (unlikely(!data))
+				return -ENOMEM;
+
+			if (!read &&
+			    unlikely(__copy_from_user(data, buf, len))) {
+				ret = -EFAULT;
+				goto error;
+			}
+		}
+
+		/* We will be using request */
+		ret = ffs_mutex_lock(&epfile->mutex,
+				     file->f_flags & O_NONBLOCK);
+		if (unlikely(ret))
+			goto error;
+
+		/* We're called from user space, we can use _irq rather then
+		 * _irqsave */
+		spin_lock_irq(&epfile->ffs->eps_lock);
+
+		/* While we were acquiring mutex endpoint got disabled
+		 * or changed? */
+	} while (unlikely(epfile->ep != ep));
+
+	/* Halt */
+	if (unlikely(halt)) {
+		if (likely(epfile->ep == ep) && !WARN_ON(!ep->ep))
+			usb_ep_set_halt(ep->ep);
+		spin_unlock_irq(&epfile->ffs->eps_lock);
+		ret = -EBADMSG;
+	} else {
+		/* Fire the request */
+		DECLARE_COMPLETION_ONSTACK(done);
+
+		struct usb_request *req = ep->req;
+		req->context  = &done;
+		req->complete = ffs_epfile_io_complete;
+		req->buf      = data;
+		req->length   = len;
+
+		ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
+
+		spin_unlock_irq(&epfile->ffs->eps_lock);
+
+		if (unlikely(ret < 0)) {
+			/* nop */
+		} else if (unlikely(wait_for_completion_interruptible(&done))) {
+			ret = -EINTR;
+			usb_ep_dequeue(ep->ep, req);
+		} else {
+			ret = ep->status;
+			if (read && ret > 0 &&
+			    unlikely(copy_to_user(buf, data, ret)))
+				ret = -EFAULT;
+		}
+	}
+
+	mutex_unlock(&epfile->mutex);
+error:
+	kfree(data);
+	return ret;
+}
+
+
+static ssize_t
+ffs_epfile_write(struct file *file, const char __user *buf, size_t len,
+		 loff_t *ptr)
+{
+	ENTER();
+
+	return ffs_epfile_io(file, (char __user *)buf, len, 0);
+}
+
+static ssize_t
+ffs_epfile_read(struct file *file, char __user *buf, size_t len, loff_t *ptr)
+{
+	ENTER();
+
+	return ffs_epfile_io(file, buf, len, 1);
+}
+
+static int
+ffs_epfile_open(struct inode *inode, struct file *file)
+{
+	struct ffs_epfile *epfile = inode->i_private;
+
+	ENTER();
+
+	if (WARN_ON(epfile->ffs->state != FFS_ACTIVE))
+		return -ENODEV;
+
+	file->private_data = epfile;
+	ffs_data_opened(epfile->ffs);
+
+	return 0;
+}
+
+static int
+ffs_epfile_release(struct inode *inode, struct file *file)
+{
+	struct ffs_epfile *epfile = inode->i_private;
+
+	ENTER();
+
+	ffs_data_closed(epfile->ffs);
+
+	return 0;
+}
+
+
+static long ffs_epfile_ioctl(struct file *file, unsigned code,
+			     unsigned long value)
+{
+	struct ffs_epfile *epfile = file->private_data;
+	int ret;
+
+	ENTER();
+
+	if (WARN_ON(epfile->ffs->state != FFS_ACTIVE))
+		return -ENODEV;
+
+	spin_lock_irq(&epfile->ffs->eps_lock);
+	if (likely(epfile->ep)) {
+		switch (code) {
+		case FUNCTIONFS_FIFO_STATUS:
+			ret = usb_ep_fifo_status(epfile->ep->ep);
+			break;
+		case FUNCTIONFS_FIFO_FLUSH:
+			usb_ep_fifo_flush(epfile->ep->ep);
+			ret = 0;
+			break;
+		case FUNCTIONFS_CLEAR_HALT:
+			ret = usb_ep_clear_halt(epfile->ep->ep);
+			break;
+		case FUNCTIONFS_ENDPOINT_REVMAP:
+			ret = epfile->ep->num;
+			break;
+		default:
+			ret = -ENOTTY;
+		}
+	} else {
+		ret = -ENODEV;
+	}
+	spin_unlock_irq(&epfile->ffs->eps_lock);
+
+	return ret;
+}
+
+
+static const struct file_operations ffs_epfile_operations = {
+	.owner =	THIS_MODULE,
+	.llseek =	no_llseek,
+
+	.open =		ffs_epfile_open,
+	.write =	ffs_epfile_write,
+	.read =		ffs_epfile_read,
+	.release =	ffs_epfile_release,
+	.unlocked_ioctl =	ffs_epfile_ioctl,
+};
+
+
+
+/* File system and super block operations ***********************************/
+
+/*
+ * Mounting the filesystem creates a controller file, used first for
+ * function configuration then later for event monitoring.
+ */
+
+
+static struct inode *__must_check
+ffs_sb_make_inode(struct super_block *sb, void *data,
+		  const struct file_operations *fops,
+		  const struct inode_operations *iops,
+		  struct ffs_file_perms *perms)
+{
+	struct inode *inode;
+
+	ENTER();
+
+	inode = new_inode(sb);
+
+	if (likely(inode)) {
+		struct timespec current_time = CURRENT_TIME;
+
+		inode->i_mode    = perms->mode;
+		inode->i_uid     = perms->uid;
+		inode->i_gid     = perms->gid;
+		inode->i_atime   = current_time;
+		inode->i_mtime   = current_time;
+		inode->i_ctime   = current_time;
+		inode->i_private = data;
+		if (fops)
+			inode->i_fop = fops;
+		if (iops)
+			inode->i_op  = iops;
+	}
+
+	return inode;
+}
+
+
+/* Create "regular" file */
+
+static struct inode *ffs_sb_create_file(struct super_block *sb,
+					const char *name, void *data,
+					const struct file_operations *fops,
+					struct dentry **dentry_p)
+{
+	struct ffs_data	*ffs = sb->s_fs_info;
+	struct dentry	*dentry;
+	struct inode	*inode;
+
+	ENTER();
+
+	dentry = d_alloc_name(sb->s_root, name);
+	if (unlikely(!dentry))
+		return NULL;
+
+	inode = ffs_sb_make_inode(sb, data, fops, NULL, &ffs->file_perms);
+	if (unlikely(!inode)) {
+		dput(dentry);
+		return NULL;
+	}
+
+	d_add(dentry, inode);
+	if (dentry_p)
+		*dentry_p = dentry;
+
+	return inode;
+}
+
+
+/* Super block */
+
+static const struct super_operations ffs_sb_operations = {
+	.statfs =	simple_statfs,
+	.drop_inode =	generic_delete_inode,
+};
+
+struct ffs_sb_fill_data {
+	struct ffs_file_perms perms;
+	umode_t root_mode;
+	const char *dev_name;
+};
+
+static int ffs_sb_fill(struct super_block *sb, void *_data, int silent)
+{
+	struct ffs_sb_fill_data *data = _data;
+	struct inode	*inode;
+	struct dentry	*d;
+	struct ffs_data	*ffs;
+
+	ENTER();
+
+	/* Initialize data */
+	ffs = ffs_data_new();
+	if (unlikely(!ffs))
+		goto enomem0;
+
+	ffs->sb              = sb;
+	ffs->dev_name        = data->dev_name;
+	ffs->file_perms      = data->perms;
+
+	sb->s_fs_info        = ffs;
+	sb->s_blocksize      = PAGE_CACHE_SIZE;
+	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+	sb->s_magic          = FUNCTIONFS_MAGIC;
+	sb->s_op             = &ffs_sb_operations;
+	sb->s_time_gran      = 1;
+
+	/* Root inode */
+	data->perms.mode = data->root_mode;
+	inode = ffs_sb_make_inode(sb, NULL,
+				  &simple_dir_operations,
+				  &simple_dir_inode_operations,
+				  &data->perms);
+	if (unlikely(!inode))
+		goto enomem1;
+	d = d_alloc_root(inode);
+	if (unlikely(!d))
+		goto enomem2;
+	sb->s_root = d;
+
+	/* EP0 file */
+	if (unlikely(!ffs_sb_create_file(sb, "ep0", ffs,
+					 &ffs_ep0_operations, NULL)))
+		goto enomem3;
+
+	return 0;
+
+enomem3:
+	dput(d);
+enomem2:
+	iput(inode);
+enomem1:
+	ffs_data_put(ffs);
+enomem0:
+	return -ENOMEM;
+}
+
+
+static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
+{
+	ENTER();
+
+	if (!opts || !*opts)
+		return 0;
+
+	for (;;) {
+		char *end, *eq, *comma;
+		unsigned long value;
+
+		/* Option limit */
+		comma = strchr(opts, ',');
+		if (comma)
+			*comma = 0;
+
+		/* Value limit */
+		eq = strchr(opts, '=');
+		if (unlikely(!eq)) {
+			FERR("'=' missing in %s", opts);
+			return -EINVAL;
+		}
+		*eq = 0;
+
+		/* Parse value */
+		value = simple_strtoul(eq + 1, &end, 0);
+		if (unlikely(*end != ',' && *end != 0)) {
+			FERR("%s: invalid value: %s", opts, eq + 1);
+			return -EINVAL;
+		}
+
+		/* Interpret option */
+		switch (eq - opts) {
+		case 5:
+			if (!memcmp(opts, "rmode", 5))
+				data->root_mode  = (value & 0555) | S_IFDIR;
+			else if (!memcmp(opts, "fmode", 5))
+				data->perms.mode = (value & 0666) | S_IFREG;
+			else
+				goto invalid;
+			break;
+
+		case 4:
+			if (!memcmp(opts, "mode", 4)) {
+				data->root_mode  = (value & 0555) | S_IFDIR;
+				data->perms.mode = (value & 0666) | S_IFREG;
+			} else {
+				goto invalid;
+			}
+			break;
+
+		case 3:
+			if (!memcmp(opts, "uid", 3))
+				data->perms.uid = value;
+			else if (!memcmp(opts, "gid", 3))
+				data->perms.gid = value;
+			else
+				goto invalid;
+			break;
+
+		default:
+invalid:
+			FERR("%s: invalid option", opts);
+			return -EINVAL;
+		}
+
+		/* Next iteration */
+		if (!comma)
+			break;
+		opts = comma + 1;
+	}
+
+	return 0;
+}
+
+
+/* "mount -t functionfs dev_name /dev/function" ends up here */
+
+static int
+ffs_fs_get_sb(struct file_system_type *t, int flags,
+	      const char *dev_name, void *opts, struct vfsmount *mnt)
+{
+	struct ffs_sb_fill_data data = {
+		.perms = {
+			.mode = S_IFREG | 0600,
+			.uid = 0,
+			.gid = 0
+		},
+		.root_mode = S_IFDIR | 0500,
+	};
+	int ret;
+
+	ENTER();
+
+	ret = functionfs_check_dev_callback(dev_name);
+	if (unlikely(ret < 0))
+		return ret;
+
+	ret = ffs_fs_parse_opts(&data, opts);
+	if (unlikely(ret < 0))
+		return ret;
+
+	data.dev_name = dev_name;
+	return get_sb_single(t, flags, &data, ffs_sb_fill, mnt);
+}
+
+static void
+ffs_fs_kill_sb(struct super_block *sb)
+{
+	void *ptr;
+
+	ENTER();
+
+	kill_litter_super(sb);
+	ptr = xchg(&sb->s_fs_info, NULL);
+	if (ptr)
+		ffs_data_put(ptr);
+}
+
+static struct file_system_type ffs_fs_type = {
+	.owner		= THIS_MODULE,
+	.name		= "functionfs",
+	.get_sb		= ffs_fs_get_sb,
+	.kill_sb	= ffs_fs_kill_sb,
+};
+
+
+
+/* Driver's main init/cleanup functions *************************************/
+
+
+static int functionfs_init(void)
+{
+	int ret;
+
+	ENTER();
+
+	ret = register_filesystem(&ffs_fs_type);
+	if (likely(!ret))
+		FINFO("file system registered");
+	else
+		FERR("failed registering file system (%d)", ret);
+
+	return ret;
+}
+
+static void functionfs_cleanup(void)
+{
+	ENTER();
+
+	FINFO("unloading");
+	unregister_filesystem(&ffs_fs_type);
+}
+
+
+
+/* ffs_data and ffs_function construction and destruction code **************/
+
+static void ffs_data_clear(struct ffs_data *ffs);
+static void ffs_data_reset(struct ffs_data *ffs);
+
+
+static void ffs_data_get(struct ffs_data *ffs)
+{
+	ENTER();
+
+	atomic_inc(&ffs->ref);
+}
+
+static void ffs_data_opened(struct ffs_data *ffs)
+{
+	ENTER();
+
+	atomic_inc(&ffs->ref);
+	atomic_inc(&ffs->opened);
+}
+
+static void ffs_data_put(struct ffs_data *ffs)
+{
+	ENTER();
+
+	if (unlikely(atomic_dec_and_test(&ffs->ref))) {
+		FINFO("%s(): freeing", __func__);
+		ffs_data_clear(ffs);
+		BUG_ON(mutex_is_locked(&ffs->mutex) ||
+		       spin_is_locked(&ffs->ev.waitq.lock) ||
+		       waitqueue_active(&ffs->ev.waitq) ||
+		       waitqueue_active(&ffs->ep0req_completion.wait));
+		kfree(ffs);
+	}
+}
+
+
+
+static void ffs_data_closed(struct ffs_data *ffs)
+{
+	ENTER();
+
+	if (atomic_dec_and_test(&ffs->opened)) {
+		ffs->state = FFS_CLOSING;
+		ffs_data_reset(ffs);
+	}
+
+	ffs_data_put(ffs);
+}
+
+
+static struct ffs_data *ffs_data_new(void)
+{
+	struct ffs_data *ffs = kzalloc(sizeof *ffs, GFP_KERNEL);
+	if (unlikely(!ffs))
+		return 0;
+
+	ENTER();
+
+	atomic_set(&ffs->ref, 1);
+	atomic_set(&ffs->opened, 0);
+	ffs->state = FFS_READ_DESCRIPTORS;
+	mutex_init(&ffs->mutex);
+	spin_lock_init(&ffs->eps_lock);
+	init_waitqueue_head(&ffs->ev.waitq);
+	init_completion(&ffs->ep0req_completion);
+
+	/* XXX REVISIT need to update it in some places, or do we? */
+	ffs->ev.can_stall = 1;
+
+	return ffs;
+}
+
+
+static void ffs_data_clear(struct ffs_data *ffs)
+{
+	ENTER();
+
+	if (test_and_clear_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags))
+		functionfs_closed_callback(ffs);
+
+	BUG_ON(ffs->gadget);
+
+	if (ffs->epfiles)
+		ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count);
+
+	kfree(ffs->raw_descs);
+	kfree(ffs->raw_strings);
+	kfree(ffs->stringtabs);
+}
+
+
+static void ffs_data_reset(struct ffs_data *ffs)
+{
+	ENTER();
+
+	ffs_data_clear(ffs);
+
+	ffs->epfiles = NULL;
+	ffs->raw_descs = NULL;
+	ffs->raw_strings = NULL;
+	ffs->stringtabs = NULL;
+
+	ffs->raw_descs_length = 0;
+	ffs->raw_fs_descs_length = 0;
+	ffs->fs_descs_count = 0;
+	ffs->hs_descs_count = 0;
+
+	ffs->strings_count = 0;
+	ffs->interfaces_count = 0;
+	ffs->eps_count = 0;
+
+	ffs->ev.count = 0;
+
+	ffs->state = FFS_READ_DESCRIPTORS;
+	ffs->setup_state = FFS_NO_SETUP;
+	ffs->flags = 0;
+}
+
+
+static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev)
+{
+	unsigned i, count;
+
+	ENTER();
+
+	if (WARN_ON(ffs->state != FFS_ACTIVE
+		 || test_and_set_bit(FFS_FL_BOUND, &ffs->flags)))
+		return -EBADFD;
+
+	ffs_data_get(ffs);
+
+	ffs->gadget = cdev->gadget;
+	ffs->ep0req = usb_ep_alloc_request(ffs->gadget->ep0, GFP_KERNEL);
+	if (unlikely(!ffs->ep0req))
+		return -ENOMEM;
+	ffs->ep0req->complete = ffs_ep0_complete;
+	ffs->ep0req->context = ffs;
+
+	/* Get strings identifiers */
+	for (count = ffs->strings_count, i = 0; i < count; ++i) {
+		struct usb_gadget_strings **lang;
+
+		int id = usb_string_id(cdev);
+		if (unlikely(id < 0))
+			return id;
+
+		lang = ffs->stringtabs;
+		do {
+			(*lang)->strings[i].id = id;
+			++lang;
+		} while (*lang);
+	}
+
+	return 0;
+}
+
+
+static void functionfs_unbind(struct ffs_data *ffs)
+{
+	ENTER();
+
+	if (!WARN_ON(!ffs->gadget)) {
+		usb_ep_free_request(ffs->gadget->ep0, ffs->ep0req);
+		ffs->ep0req = NULL;
+		ffs->gadget = NULL;
+		ffs_data_put(ffs);
+	}
+}
+
+
+static int ffs_epfiles_create(struct ffs_data *ffs)
+{
+	struct ffs_epfile *epfile, *epfiles;
+	unsigned i, count;
+
+	ENTER();
+
+	count = ffs->eps_count;
+	epfiles = kzalloc(count * sizeof *epfiles, GFP_KERNEL);
+	if (!epfiles)
+		return -ENOMEM;
+
+	epfile = epfiles;
+	for (i = 1; i <= count; ++i, ++epfile) {
+		epfile->ffs = ffs;
+		mutex_init(&epfile->mutex);
+		init_waitqueue_head(&epfile->wait);
+		sprintf(epfiles->name, "ep%u",  i);
+		if (!unlikely(ffs_sb_create_file(ffs->sb, epfiles->name, epfile,
+						 &ffs_epfile_operations,
+						 &epfile->dentry))) {
+			ffs_epfiles_destroy(epfiles, i - 1);
+			return -ENOMEM;
+		}
+	}
+
+	ffs->epfiles = epfiles;
+	return 0;
+}
+
+
+static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
+{
+	struct ffs_epfile *epfile = epfiles;
+
+	ENTER();
+
+	for (; count; --count, ++epfile) {
+		BUG_ON(mutex_is_locked(&epfile->mutex) ||
+		       waitqueue_active(&epfile->wait));
+		if (epfile->dentry) {
+			d_delete(epfile->dentry);
+			dput(epfile->dentry);
+			epfile->dentry = NULL;
+		}
+	}
+
+	kfree(epfiles);
+}
+
+
+static int functionfs_add(struct usb_composite_dev *cdev,
+			  struct usb_configuration *c,
+			  struct ffs_data *ffs)
+{
+	struct ffs_function *func;
+	int ret;
+
+	ENTER();
+
+	func = kzalloc(sizeof *func, GFP_KERNEL);
+	if (unlikely(!func))
+		return -ENOMEM;
+
+	func->function.name    = "Function FS Gadget";
+	func->function.strings = ffs->stringtabs;
+
+	func->function.bind    = ffs_func_bind;
+	func->function.unbind  = ffs_func_unbind;
+	func->function.set_alt = ffs_func_set_alt;
+	/*func->function.get_alt = ffs_func_get_alt;*/
+	func->function.disable = ffs_func_disable;
+	func->function.setup   = ffs_func_setup;
+	func->function.suspend = ffs_func_suspend;
+	func->function.resume  = ffs_func_resume;
+
+	func->conf   = c;
+	func->gadget = cdev->gadget;
+	func->ffs = ffs;
+	ffs_data_get(ffs);
+
+	ret = usb_add_function(c, &func->function);
+	if (unlikely(ret))
+		ffs_func_free(func);
+
+	return ret;
+}
+
+static void ffs_func_free(struct ffs_function *func)
+{
+	ENTER();
+
+	ffs_data_put(func->ffs);
+
+	kfree(func->eps);
+	/* eps and interfaces_nums are allocated in the same chunk so
+	 * only one free is required.  Descriptors are also allocated
+	 * in the same chunk. */
+
+	kfree(func);
+}
+
+
+static void ffs_func_eps_disable(struct ffs_function *func)
+{
+	struct ffs_ep *ep         = func->eps;
+	struct ffs_epfile *epfile = func->ffs->epfiles;
+	unsigned count            = func->ffs->eps_count;
+	unsigned long flags;
+
+	spin_lock_irqsave(&func->ffs->eps_lock, flags);
+	do {
+		/* pending requests get nuked */
+		if (likely(ep->ep))
+			usb_ep_disable(ep->ep);
+		epfile->ep = NULL;
+
+		++ep;
+		++epfile;
+	} while (--count);
+	spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
+}
+
+static int ffs_func_eps_enable(struct ffs_function *func)
+{
+	struct ffs_data *ffs      = func->ffs;
+	struct ffs_ep *ep         = func->eps;
+	struct ffs_epfile *epfile = ffs->epfiles;
+	unsigned count            = ffs->eps_count;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&func->ffs->eps_lock, flags);
+	do {
+		struct usb_endpoint_descriptor *ds;
+		ds = ep->descs[ep->descs[1] ? 1 : 0];
+
+		ep->ep->driver_data = ep;
+		ret = usb_ep_enable(ep->ep, ds);
+		if (likely(!ret)) {
+			epfile->ep = ep;
+			epfile->in = usb_endpoint_dir_in(ds);
+			epfile->isoc = usb_endpoint_xfer_isoc(ds);
+		} else {
+			break;
+		}
+
+		wake_up(&epfile->wait);
+
+		++ep;
+		++epfile;
+	} while (--count);
+	spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
+
+	return ret;
+}
+
+
+/* Parsing and building descriptors and strings *****************************/
+
+
+/* This validates if data pointed by data is a valid USB descriptor as
+ * well as record how many interfaces, endpoints and strings are
+ * required by given configuration.  Returns address afther the
+ * descriptor or NULL if data is invalid. */
+
+enum ffs_entity_type {
+	FFS_DESCRIPTOR, FFS_INTERFACE, FFS_STRING, FFS_ENDPOINT
+};
+
+typedef int (*ffs_entity_callback)(enum ffs_entity_type entity,
+				   u8 *valuep,
+				   struct usb_descriptor_header *desc,
+				   void *priv);
+
+static int __must_check ffs_do_desc(char *data, unsigned len,
+				    ffs_entity_callback entity, void *priv)
+{
+	struct usb_descriptor_header *_ds = (void *)data;
+	u8 length;
+	int ret;
+
+	ENTER();
+
+	/* At least two bytes are required: length and type */
+	if (len < 2)
+		return -EINVAL;
+
+	/* If we have at least as many bytes as the descriptor takes? */
+	length = _ds->bLength;
+	if (len < length)
+		return -EINVAL;
+
+#define __entity_check_INTERFACE(val)  1
+#define __entity_check_STRING(val)     (val)
+#define __entity_check_ENDPOINT(val)   ((val) & USB_ENDPOINT_NUMBER_MASK)
+#define __entity(type, val) do {					\
+		FVDBG("entity " #type "(%02x)", (val));			\
+		if (unlikely(!__entity_check_ ##type(val)))		\
+			return -EINVAL;					\
+		ret = entity(FFS_ ##type, &val, _ds, priv);		\
+		if (unlikely(ret < 0)) {				\
+			FDBG("entity " #type "(%02x); ret = %d",	\
+			     (val), ret);				\
+			return ret;					\
+		}							\
+	} while (0)
+
+	/* Parse descriptor depending on type. */
+	switch (_ds->bDescriptorType) {
+	case USB_DT_DEVICE:
+	case USB_DT_CONFIG:
+	case USB_DT_STRING:
+	case USB_DT_DEVICE_QUALIFIER:
+		/* function can't have any of those */
+		return -EINVAL;
+
+	case USB_DT_INTERFACE: {
+		struct usb_interface_descriptor *ds = (void *)_ds;
+		FVDBG("interface descriptor");
+		if (length != sizeof *ds)
+			return -EINVAL;
+
+		__entity(INTERFACE, ds->bInterfaceNumber);
+		if (ds->iInterface)
+			__entity(STRING, ds->iInterface);
+	}
+		break;
+
+	case USB_DT_ENDPOINT: {
+		struct usb_endpoint_descriptor *ds = (void *)_ds;
+		FVDBG("endpoint descriptor");
+		if (length != USB_DT_ENDPOINT_SIZE &&
+		    length != USB_DT_ENDPOINT_AUDIO_SIZE)
+			return -EINVAL;
+		if (!(ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK))
+			return -EINVAL;
+		__entity(ENDPOINT, ds->bEndpointAddress);
+	}
+		break;
+
+	case USB_DT_OTHER_SPEED_CONFIG:
+		/* TODO */
+		return -EINVAL;
+
+	case USB_DT_INTERFACE_POWER:
+		/* TODO */
+		return -EINVAL;
+
+	case USB_DT_OTG:
+		if (length != sizeof(struct usb_otg_descriptor))
+			return -EINVAL;
+		break;
+
+	case USB_DT_DEBUG:
+		/* TODO */
+		return -EINVAL;
+
+	case USB_DT_INTERFACE_ASSOCIATION: {
+		struct usb_interface_assoc_descriptor *ds = (void *)_ds;
+		FVDBG("interface association descriptor");
+		if (length != sizeof *ds)
+			return -EINVAL;
+		if (ds->iFunction)
+			__entity(STRING, ds->iFunction);
+	}
+		break;
+
+	case USB_DT_SECURITY:
+		/* TODO */
+		return -EINVAL;
+
+	case USB_DT_KEY:
+		/* TODO */
+		return -EINVAL;
+
+	case USB_DT_ENCRYPTION_TYPE:
+		/* TODO */
+		return -EINVAL;
+
+	case USB_DT_BOS:
+		/* TODO */
+		return -EINVAL;
+
+	case USB_DT_DEVICE_CAPABILITY:
+		/* TODO */
+		return -EINVAL;
+
+	case USB_DT_WIRELESS_ENDPOINT_COMP:
+		/* TODO */
+		return -EINVAL;
+
+	case USB_DT_WIRE_ADAPTER:
+		/* TODO */
+		return -EINVAL;
+
+	case USB_DT_RPIPE:
+		/* TODO */
+		return -EINVAL;
+
+	case USB_DT_CS_RADIO_CONTROL:
+		/* TODO */
+		return -EINVAL;
+
+	default:
+		/* We should never be here */
+		return -EINVAL;
+	}
+
+#undef __entity
+#undef __entity_check_DESCRIPTOR
+#undef __entity_check_INTERFACE
+#undef __entity_check_STRING
+#undef __entity_check_ENDPOINT
+
+	return length;
+}
+
+
+static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
+				     ffs_entity_callback entity, void *priv)
+{
+	const unsigned _len = len;
+	long num = 0;
+
+	ENTER();
+
+	for (;;) {
+		int ret;
+
+		if (num == count)
+			data = NULL;
+
+		/* Record "descriptor" entitny */
+		ret = entity(FFS_DESCRIPTOR, (u8 *)num, (void *)data, priv);
+		if (unlikely(ret < 0)) {
+			FDBG("entity DESCRIPTOR(%02x); ret = %d", num, ret);
+			return ret;
+		}
+
+		if (!data)
+			return _len - len;
+
+		ret = ffs_do_desc(data, len, entity, priv);
+		if (unlikely(ret < 0)) {
+			FDBG("%s returns %d", __func__, ret);
+			return ret;
+		}
+
+		len -= ret;
+		data += ret;
+		++num;
+	}
+}
+
+
+static int __ffs_data_do_entity(enum ffs_entity_type type,
+				u8 *valuep, struct usb_descriptor_header *desc,
+				void *priv)
+{
+	struct ffs_data *ffs = priv;
+
+	ENTER();
+
+	switch (type) {
+	case FFS_DESCRIPTOR:
+		break;
+
+	case FFS_INTERFACE:
+		/* Interfaces are indexed from zero so if we
+		 * encountered interface "n" then there are at least
+		 * "n+1" interfaces. */
+		if (*valuep >= ffs->interfaces_count)
+			ffs->interfaces_count = *valuep + 1;
+		break;
+
+	case FFS_STRING:
+		/* Strings are indexed from 1 (0 is magic ;) reserved
+		 * for languages list or some such) */
+		if (*valuep > ffs->strings_count)
+			ffs->strings_count = *valuep;
+		break;
+
+	case FFS_ENDPOINT:
+		/* Endpoints are indexed from 1 as well. */
+		if ((*valuep & USB_ENDPOINT_NUMBER_MASK) > ffs->eps_count)
+			ffs->eps_count = (*valuep & USB_ENDPOINT_NUMBER_MASK);
+		break;
+	}
+
+	return 0;
+}
+
+
+static int __ffs_data_got_descs(struct ffs_data *ffs,
+				char *const _data, size_t len)
+{
+	unsigned fs_count, hs_count;
+	int fs_len, ret = -EINVAL;
+	char *data = _data;
+
+	ENTER();
+
+	if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_DESCRIPTORS_MAGIC ||
+		     get_unaligned_le32(data + 4) != len))
+		goto error;
+	fs_count = get_unaligned_le32(data +  8);
+	hs_count = get_unaligned_le32(data + 12);
+
+	if (!fs_count && !hs_count)
+		goto einval;
+
+	data += 16;
+	len  -= 16;
+
+	if (likely(fs_count)) {
+		fs_len = ffs_do_descs(fs_count, data, len,
+				      __ffs_data_do_entity, ffs);
+		if (unlikely(fs_len < 0)) {
+			ret = fs_len;
+			goto error;
+		}
+
+		data += fs_len;
+		len  -= fs_len;
+	} else {
+		fs_len = 0;
+	}
+
+	if (likely(hs_count)) {
+		ret = ffs_do_descs(hs_count, data, len,
+				   __ffs_data_do_entity, ffs);
+		if (unlikely(ret < 0))
+			goto error;
+	} else {
+		ret = 0;
+	}
+
+	if (unlikely(len != ret))
+		goto einval;
+
+	ffs->raw_fs_descs_length = fs_len;
+	ffs->raw_descs_length    = fs_len + ret;
+	ffs->raw_descs           = _data;
+	ffs->fs_descs_count      = fs_count;
+	ffs->hs_descs_count      = hs_count;
+
+	return 0;
+
+einval:
+	ret = -EINVAL;
+error:
+	kfree(_data);
+	return ret;
+}
+
+
+
+static int __ffs_data_got_strings(struct ffs_data *ffs,
+				  char *const _data, size_t len)
+{
+	u32 str_count, needed_count, lang_count;
+	struct usb_gadget_strings **stringtabs, *t;
+	struct usb_string *strings, *s;
+	const char *data = _data;
+
+	ENTER();
+
+	if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_STRINGS_MAGIC ||
+		     get_unaligned_le32(data + 4) != len))
+		goto error;
+	str_count  = get_unaligned_le32(data + 8);
+	lang_count = get_unaligned_le32(data + 12);
+
+	/* if one is zero the other must be zero */
+	if (unlikely(!str_count != !lang_count))
+		goto error;
+
+	/* Do we have at least as many strings as descriptors need? */
+	needed_count = ffs->strings_count;
+	if (unlikely(str_count < needed_count))
+		goto error;
+
+	/* If we don't need any strings just return and free all
+	 * memory */
+	if (!needed_count) {
+		kfree(_data);
+		return 0;
+	}
+
+	/* Allocate */
+	{
+		/* Allocate everything in one chunk so there's less
+		 * maintanance. */
+		struct {
+			struct usb_gadget_strings *stringtabs[lang_count + 1];
+			struct usb_gadget_strings stringtab[lang_count];
+			struct usb_string strings[lang_count*(needed_count+1)];
+		} *d;
+		unsigned i = 0;
+
+		d = kmalloc(sizeof *d, GFP_KERNEL);
+		if (unlikely(!d)) {
+			kfree(_data);
+			return -ENOMEM;
+		}
+
+		stringtabs = d->stringtabs;
+		t = d->stringtab;
+		i = lang_count;
+		do {
+			*stringtabs++ = t++;
+		} while (--i);
+		*stringtabs = NULL;
+
+		stringtabs = d->stringtabs;
+		t = d->stringtab;
+		s = d->strings;
+		strings = s;
+	}
+
+	/* For each language */
+	data += 16;
+	len -= 16;
+
+	do { /* lang_count > 0 so we can use do-while */
+		unsigned needed = needed_count;
+
+		if (unlikely(len < 3))
+			goto error_free;
+		t->language = get_unaligned_le16(data);
+		t->strings  = s;
+		++t;
+
+		data += 2;
+		len -= 2;
+
+		/* For each string */
+		do { /* str_count > 0 so we can use do-while */
+			size_t length = strnlen(data, len);
+
+			if (unlikely(length == len))
+				goto error_free;
+
+			/* user may provide more strings then we need,
+			 * if that's the case we simply ingore the
+			 * rest */
+			if (likely(needed)) {
+				/* s->id will be set while adding
+				 * function to configuration so for
+				 * now just leave garbage here. */
+				s->s = data;
+				--needed;
+				++s;
+			}
+
+			data += length + 1;
+			len -= length + 1;
+		} while (--str_count);
+
+		s->id = 0;   /* terminator */
+		s->s = NULL;
+		++s;
+
+	} while (--lang_count);
+
+	/* Some garbage left? */
+	if (unlikely(len))
+		goto error_free;
+
+	/* Done! */
+	ffs->stringtabs = stringtabs;
+	ffs->raw_strings = _data;
+
+	return 0;
+
+error_free:
+	kfree(stringtabs);
+error:
+	kfree(_data);
+	return -EINVAL;
+}
+
+
+
+
+/* Events handling and management *******************************************/
+
+static void __ffs_event_add(struct ffs_data *ffs,
+			    enum usb_functionfs_event_type type)
+{
+	enum usb_functionfs_event_type rem_type1 = type;
+	enum usb_functionfs_event_type rem_type2 = type;
+	int neg = 0;
+
+	/* Abort any unhandled setup */
+	if (ffs->setup_state == FFS_SETUP_PENDING)
+		ffs->setup_state = FFS_SETUP_CANCELED;
+
+	switch (type) {
+	case FUNCTIONFS_RESUME:
+		rem_type2 = FUNCTIONFS_SUSPEND;
+		/* FALL THGOUTH */
+	case FUNCTIONFS_SUSPEND:
+	case FUNCTIONFS_SETUP:
+		/* discard all similar events */
+		break;
+
+	case FUNCTIONFS_BIND:
+	case FUNCTIONFS_UNBIND:
+	case FUNCTIONFS_DISABLE:
+	case FUNCTIONFS_ENABLE:
+		/* discard everything other then power management. */
+		rem_type1 = FUNCTIONFS_SUSPEND;
+		rem_type2 = FUNCTIONFS_RESUME;
+		neg = 1;
+		break;
+
+	default:
+		BUG();
+	}
+
+	{
+		u8 *ev  = ffs->ev.types, *out = ev;
+		unsigned n = ffs->ev.count;
+		for (; n; --n, ++ev)
+			if ((*ev == rem_type1 || *ev == rem_type2) == neg)
+				*out++ = *ev;
+		ffs->ev.count = out - ffs->ev.types;
+	}
+
+	ffs->ev.types[ffs->ev.count++] = type;
+	wake_up_locked(&ffs->ev.waitq);
+}
+
+static void ffs_event_add(struct ffs_data *ffs,
+			  enum usb_functionfs_event_type type)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&ffs->ev.waitq.lock, flags);
+	__ffs_event_add(ffs, type);
+	spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags);
+}
+
+
+/* Bind/unbind USB function hooks *******************************************/
+
+static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
+				    struct usb_descriptor_header *desc,
+				    void *priv)
+{
+	struct usb_endpoint_descriptor *ds = (void *)desc;
+	struct ffs_function *func = priv;
+	struct ffs_ep *ffs_ep;
+
+	/* If hs_descriptors is not NULL then we are reading hs
+	 * descriptors now */
+	const int isHS = func->function.hs_descriptors != NULL;
+	unsigned idx;
+
+	if (type != FFS_DESCRIPTOR)
+		return 0;
+
+	if (isHS)
+		func->function.hs_descriptors[(long)valuep] = desc;
+	else
+		func->function.descriptors[(long)valuep]    = desc;
+
+	if (!desc || desc->bDescriptorType != USB_DT_ENDPOINT)
+		return 0;
+
+	idx = (ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) - 1;
+	ffs_ep = func->eps + idx;
+
+	if (unlikely(ffs_ep->descs[isHS]))
+		return -EINVAL;
+	ffs_ep->descs[isHS] = ds;
+
+	ffs_dump_mem(": Original  ep desc", ds, ds->bLength);
+	if (ffs_ep->ep) {
+		ds->bEndpointAddress = ffs_ep->descs[0]->bEndpointAddress;
+		if (!ds->wMaxPacketSize)
+			ds->wMaxPacketSize = ffs_ep->descs[0]->wMaxPacketSize;
+	} else {
+		struct usb_request *req;
+		struct usb_ep *ep;
+
+		FVDBG("autoconfig");
+		ep = usb_ep_autoconfig(func->gadget, ds);
+		if (unlikely(!ep))
+			return -ENOTSUPP;
+		ep->driver_data = func->eps + idx;;
+
+		req = usb_ep_alloc_request(ep, GFP_KERNEL);
+		if (unlikely(!req))
+			return -ENOMEM;
+
+		ffs_ep->ep  = ep;
+		ffs_ep->req = req;
+		func->eps_revmap[ds->bEndpointAddress &
+				 USB_ENDPOINT_NUMBER_MASK] = idx + 1;
+	}
+	ffs_dump_mem(": Rewritten ep desc", ds, ds->bLength);
+
+	return 0;
+}
+
+
+static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep,
+				   struct usb_descriptor_header *desc,
+				   void *priv)
+{
+	struct ffs_function *func = priv;
+	unsigned idx;
+	u8 newValue;
+
+	switch (type) {
+	default:
+	case FFS_DESCRIPTOR:
+		/* Handled in previous pass by __ffs_func_bind_do_descs() */
+		return 0;
+
+	case FFS_INTERFACE:
+		idx = *valuep;
+		if (func->interfaces_nums[idx] < 0) {
+			int id = usb_interface_id(func->conf, &func->function);
+			if (unlikely(id < 0))
+				return id;
+			func->interfaces_nums[idx] = id;
+		}
+		newValue = func->interfaces_nums[idx];
+		break;
+
+	case FFS_STRING:
+		/* String' IDs are allocated when fsf_data is bound to cdev */
+		newValue = func->ffs->stringtabs[0]->strings[*valuep - 1].id;
+		break;
+
+	case FFS_ENDPOINT:
+		/* USB_DT_ENDPOINT are handled in
+		 * __ffs_func_bind_do_descs(). */
+		if (desc->bDescriptorType == USB_DT_ENDPOINT)
+			return 0;
+
+		idx = (*valuep & USB_ENDPOINT_NUMBER_MASK) - 1;
+		if (unlikely(!func->eps[idx].ep))
+			return -EINVAL;
+
+		{
+			struct usb_endpoint_descriptor **descs;
+			descs = func->eps[idx].descs;
+			newValue = descs[descs[0] ? 0 : 1]->bEndpointAddress;
+		}
+		break;
+	}
+
+	FVDBG("%02x -> %02x", *valuep, newValue);
+	*valuep = newValue;
+	return 0;
+}
+
+static int ffs_func_bind(struct usb_configuration *c,
+			 struct usb_function *f)
+{
+	struct ffs_function *func = ffs_func_from_usb(f);
+	struct ffs_data *ffs = func->ffs;
+
+	const int full = !!func->ffs->fs_descs_count;
+	const int high = gadget_is_dualspeed(func->gadget) &&
+		func->ffs->hs_descs_count;
+
+	int ret;
+
+	/* Make it a single chunk, less management later on */
+	struct {
+		struct ffs_ep eps[ffs->eps_count];
+		struct usb_descriptor_header
+			*fs_descs[full ? ffs->fs_descs_count + 1 : 0];
+		struct usb_descriptor_header
+			*hs_descs[high ? ffs->hs_descs_count + 1 : 0];
+		short inums[ffs->interfaces_count];
+		char raw_descs[high ? ffs->raw_descs_length
+				    : ffs->raw_fs_descs_length];
+	} *data;
+
+	ENTER();
+
+	/* Only high speed but not supported by gadget? */
+	if (unlikely(!(full | high)))
+		return -ENOTSUPP;
+
+	/* Allocate */
+	data = kmalloc(sizeof *data, GFP_KERNEL);
+	if (unlikely(!data))
+		return -ENOMEM;
+
+	/* Zero */
+	memset(data->eps, 0, sizeof data->eps);
+	memcpy(data->raw_descs, ffs->raw_descs + 16, sizeof data->raw_descs);
+	memset(data->inums, 0xff, sizeof data->inums);
+	for (ret = ffs->eps_count; ret; --ret)
+		data->eps[ret].num = -1;
+
+	/* Save pointers */
+	func->eps             = data->eps;
+	func->interfaces_nums = data->inums;
+
+	/* Go throught all the endpoint descriptors and allocate
+	 * endpoints first, so that later we can rewrite the endpoint
+	 * numbers without worying that it may be described later on. */
+	if (likely(full)) {
+		func->function.descriptors = data->fs_descs;
+		ret = ffs_do_descs(ffs->fs_descs_count,
+				   data->raw_descs,
+				   sizeof data->raw_descs,
+				   __ffs_func_bind_do_descs, func);
+		if (unlikely(ret < 0))
+			goto error;
+	} else {
+		ret = 0;
+	}
+
+	if (likely(high)) {
+		func->function.hs_descriptors = data->hs_descs;
+		ret = ffs_do_descs(ffs->hs_descs_count,
+				   data->raw_descs + ret,
+				   (sizeof data->raw_descs) - ret,
+				   __ffs_func_bind_do_descs, func);
+	}
+
+	/* Now handle interface numbers allocation and interface and
+	 * enpoint numbers rewritting.  We can do that in one go
+	 * now. */
+	ret = ffs_do_descs(ffs->fs_descs_count +
+			   (high ? ffs->hs_descs_count : 0),
+			   data->raw_descs, sizeof data->raw_descs,
+			   __ffs_func_bind_do_nums, func);
+	if (unlikely(ret < 0))
+		goto error;
+
+	/* And we're done */
+	ffs_event_add(ffs, FUNCTIONFS_BIND);
+	return 0;
+
+error:
+	/* XXX Do we need to release all claimed endpoints here? */
+	return ret;
+}
+
+
+/* Other USB function hooks *************************************************/
+
+static void ffs_func_unbind(struct usb_configuration *c,
+			    struct usb_function *f)
+{
+	struct ffs_function *func = ffs_func_from_usb(f);
+	struct ffs_data *ffs = func->ffs;
+
+	ENTER();
+
+	if (ffs->func == func) {
+		ffs_func_eps_disable(func);
+		ffs->func = NULL;
+	}
+
+	ffs_event_add(ffs, FUNCTIONFS_UNBIND);
+
+	ffs_func_free(func);
+}
+
+
+static int ffs_func_set_alt(struct usb_function *f,
+			    unsigned interface, unsigned alt)
+{
+	struct ffs_function *func = ffs_func_from_usb(f);
+	struct ffs_data *ffs = func->ffs;
+	int ret = 0, intf;
+
+	if (alt != (unsigned)-1) {
+		intf = ffs_func_revmap_intf(func, interface);
+		if (unlikely(intf < 0))
+			return intf;
+	}
+
+	if (ffs->func)
+		ffs_func_eps_disable(ffs->func);
+
+	if (ffs->state != FFS_ACTIVE)
+		return -ENODEV;
+
+	if (alt == (unsigned)-1) {
+		ffs->func = NULL;
+		ffs_event_add(ffs, FUNCTIONFS_DISABLE);
+		return 0;
+	}
+
+	ffs->func = func;
+	ret = ffs_func_eps_enable(func);
+	if (likely(ret >= 0))
+		ffs_event_add(ffs, FUNCTIONFS_ENABLE);
+	return ret;
+}
+
+static void ffs_func_disable(struct usb_function *f)
+{
+	ffs_func_set_alt(f, 0, (unsigned)-1);
+}
+
+static int ffs_func_setup(struct usb_function *f,
+			  const struct usb_ctrlrequest *creq)
+{
+	struct ffs_function *func = ffs_func_from_usb(f);
+	struct ffs_data *ffs = func->ffs;
+	unsigned long flags;
+	int ret;
+
+	ENTER();
+
+	FVDBG("creq->bRequestType = %02x", creq->bRequestType);
+	FVDBG("creq->bRequest     = %02x", creq->bRequest);
+	FVDBG("creq->wValue       = %04x", le16_to_cpu(creq->wValue));
+	FVDBG("creq->wIndex       = %04x", le16_to_cpu(creq->wIndex));
+	FVDBG("creq->wLength      = %04x", le16_to_cpu(creq->wLength));
+
+	/* Most requests directed to interface go throught here
+	 * (notable exceptions are set/get interface) so we need to
+	 * handle them.  All other either handled by composite or
+	 * passed to usb_configuration->setup() (if one is set).  No
+	 * matter, we will handle requests directed to endpoint here
+	 * as well (as it's straightforward) but what to do with any
+	 * other request? */
+
+	if (ffs->state != FFS_ACTIVE)
+		return -ENODEV;
+
+	switch (creq->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_INTERFACE:
+		ret = ffs_func_revmap_intf(func, le16_to_cpu(creq->wIndex));
+		if (0) /* FALL THROUGHT but skip next instruction */
+	case USB_RECIP_ENDPOINT:
+			ret = ffs_func_revmap_ep(func,
+						 le16_to_cpu(creq->wIndex));
+		if (unlikely(ret < 0))
+			return ret;
+		break;
+
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	spin_lock_irqsave(&ffs->ev.waitq.lock, flags);
+	ffs->ev.setup = *creq;
+	ffs->ev.setup.wIndex = cpu_to_le16(ret);
+	__ffs_event_add(ffs, FUNCTIONFS_SETUP);
+	spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags);
+
+	return 0;
+}
+
+static void ffs_func_suspend(struct usb_function *f)
+{
+	ENTER();
+	ffs_event_add(ffs_func_from_usb(f)->ffs, FUNCTIONFS_SUSPEND);
+}
+
+static void ffs_func_resume(struct usb_function *f)
+{
+	ENTER();
+	ffs_event_add(ffs_func_from_usb(f)->ffs, FUNCTIONFS_RESUME);
+}
+
+
+
+/* Enpoint and interface numbers reverse mapping ****************************/
+
+static int ffs_func_revmap_ep(struct ffs_function *func, u8 num)
+{
+	num = func->eps_revmap[num & USB_ENDPOINT_NUMBER_MASK];
+	return num ? num : -EDOM;
+}
+
+static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf)
+{
+	short *nums = func->interfaces_nums;
+	unsigned count = func->ffs->interfaces_count;
+
+	for (; count; --count, ++nums) {
+		if (*nums >= 0 && *nums == intf)
+			return nums - func->interfaces_nums;
+	}
+
+	return -EDOM;
+}
+
+
+/* Misc helper functions ****************************************************/
+
+static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock)
+{
+	return nonblock
+		? likely(mutex_trylock(mutex)) ? 0 : -EAGAIN
+		: mutex_lock_interruptible(mutex);
+}
+
+
+static char *ffs_prepare_buffer(const char * __user buf, size_t len)
+{
+	char *data;
+
+	if (unlikely(!len))
+		return NULL;
+
+	data = kmalloc(len, GFP_KERNEL);
+	if (unlikely(!data))
+		return ERR_PTR(-ENOMEM);
+
+	if (likely(buf != NULL) && unlikely(__copy_from_user(data, buf, len))) {
+		kfree(data);
+		return ERR_PTR(-EFAULT);
+	}
+
+	if (likely(buf)) {
+		FVDBG("Buffer from user space:");
+		ffs_dump_mem("", data, len);
+	}
+
+	return data;
+}
diff --git a/include/linux/usb/functionfs.h b/include/linux/usb/functionfs.h
new file mode 100644
index 0000000..a34a2a0
--- /dev/null
+++ b/include/linux/usb/functionfs.h
@@ -0,0 +1,199 @@
+#ifndef __LINUX_FUNCTIONFS_H__
+#define __LINUX_FUNCTIONFS_H__ 1
+
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#include <linux/usb/ch9.h>
+
+
+enum {
+	FUNCTIONFS_DESCRIPTORS_MAGIC = 1,
+	FUNCTIONFS_STRINGS_MAGIC     = 2
+};
+
+
+#ifndef __KERNEL__
+
+/* Descriptor of an non-audio endpoint */
+struct usb_endpoint_descriptor_no_audio {
+	__u8  bLength;
+	__u8  bDescriptorType;
+
+	__u8  bEndpointAddress;
+	__u8  bmAttributes;
+	__le16 wMaxPacketSize;
+	__u8  bInterval;
+} __attribute__((packed));
+
+
+/*
+ * All numbers must be in little endian order.
+ */
+
+struct usb_functionfs_descs_head {
+	__le32 magic;
+	__le32 length;
+	__le32 fs_count;
+	__le32 hs_count;
+} __attribute__((packed));
+
+/*
+ * Descriptors format:
+ *
+ * | off | name      | type         | description                          |
+ * |-----+-----------+--------------+--------------------------------------|
+ * |   0 | magic     | LE32         | FUNCTIONFS_{FS,HS}_DESCRIPTORS_MAGIC |
+ * |   4 | lenght    | LE32         | length of the whole data chunk       |
+ * |   8 | fs_count  | LE32         | number of full-speed descriptors     |
+ * |  12 | hs_count  | LE32         | number of high-speed descriptors     |
+ * |  16 | fs_descrs | Descriptor[] | list of full-speed descriptors       |
+ * |     | hs_descrs | Descriptor[] | list of high-speed descriptors       |
+ *
+ * descs are just valid USB descriptors and have the following format:
+ *
+ * | off | name            | type | description              |
+ * |-----+-----------------+------+--------------------------|
+ * |   0 | bLength         | U8   | length of the descriptor |
+ * |   1 | bDescriptorType | U8   | descriptor type          |
+ * |   2 | payload         |      | descriptor's payload     |
+ */
+
+struct usb_functionfs_strings_head {
+	__le32 magic;
+	__le32 length;
+	__le32 str_count;
+	__le32 lang_count;
+} __attribute__((packed));
+
+/*
+ * Strings format:
+ *
+ * | off | name       | type                  | description                |
+ * |-----+------------+-----------------------+----------------------------|
+ * |   0 | magic      | LE32                  | FUNCTIONFS_STRINGS_MAGIC   |
+ * |   4 | length     | LE32                  | length of the data chunk   |
+ * |   8 | str_count  | LE32                  | number of strings          |
+ * |  12 | lang_count | LE32                  | number of languages        |
+ * |  16 | stringtab  | StringTab[lang_count] | table of strings per lang  |
+ *
+ * For each language there is one stringtab entry (ie. there are lang_count
+ * stringtab entires).  Each StringTab has following format:
+ *
+ * | off | name    | type              | description                        |
+ * |-----+---------+-------------------+------------------------------------|
+ * |   0 | lang    | LE16              | language code                      |
+ * |   2 | strings | String[str_count] | array of strings in given language |
+ *
+ * For each string ther is one strings entry (ie. there are str_count
+ * string entries).  Each String is a NUL terminated string encoded in
+ * UTF-8.
+ */
+
+#endif
+
+
+/*
+ * Events are delivered on the ep0 file descriptor, when the user mode driver
+ * reads from this file descriptor after writing the descriptors.  Don't
+ * stop polling this descriptor.
+ */
+
+enum usb_functionfs_event_type {
+	FUNCTIONFS_BIND,
+	FUNCTIONFS_UNBIND,
+
+	FUNCTIONFS_ENABLE,
+	FUNCTIONFS_DISABLE,
+
+	FUNCTIONFS_SETUP,
+
+	FUNCTIONFS_SUSPEND,
+	FUNCTIONFS_RESUME
+};
+
+/* NOTE:  this structure must stay the same size and layout on
+ * both 32-bit and 64-bit kernels.
+ */
+struct usb_functionfs_event {
+	union {
+		/* SETUP: packet; DATA phase i/o precedes next event
+		 *(setup.bmRequestType & USB_DIR_IN) flags direction */
+		struct usb_ctrlrequest	setup;
+	} __attribute__((packed)) u;
+
+	/* enum usb_functionfs_event_type */
+	__u8				type;
+	__u8				_pad[3];
+} __attribute__((packed));
+
+
+/* Endpoint ioctls */
+/* The same as in gadgetfs */
+
+/* IN transfers may be reported to the gadget driver as complete
+ *	when the fifo is loaded, before the host reads the data;
+ * OUT transfers may be reported to the host's "client" driver as
+ *	complete when they're sitting in the FIFO unread.
+ * THIS returns how many bytes are "unclaimed" in the endpoint fifo
+ * (needed for precise fault handling, when the hardware allows it)
+ */
+#define	FUNCTIONFS_FIFO_STATUS	_IO('g', 1)
+
+/* discards any unclaimed data in the fifo. */
+#define	FUNCTIONFS_FIFO_FLUSH	_IO('g', 2)
+
+/* resets endpoint halt+toggle; used to implement set_interface.
+ * some hardware (like pxa2xx) can't support this.
+ */
+#define	FUNCTIONFS_CLEAR_HALT	_IO('g', 3)
+
+/* Specific for functionfs */
+
+/*
+ * Returns reverse mapping of an interface.  Called on EP0.  If there
+ * is no such interface returns -EDOM.  If function is not active
+ * returns -ENODEV.
+ */
+#define	FUNCTIONFS_INTERFACE_REVMAP	_IO('g', 128)
+
+/*
+ * Returns real bEndpointAddress of an endpoint.  If function is not
+ * active returns -ENODEV.
+ */
+#define	FUNCTIONFS_ENDPOINT_REVMAP	_IO('g', 129)
+
+
+#ifdef __KERNEL__
+
+struct ffs_data;
+struct usb_composite_dev;
+struct usb_configuration;
+
+
+static int  functionfs_init(void) __attribute__((warn_unused_result));
+static void functionfs_cleanup(void);
+
+static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev)
+	__attribute__((warn_unused_result, nonnull));
+static void functionfs_unbind(struct ffs_data *ffs)
+	__attribute__((nonnull));
+
+static int functionfs_add(struct usb_composite_dev *cdev,
+			  struct usb_configuration *c,
+			  struct ffs_data *ffs)
+	__attribute__((warn_unused_result, nonnull));
+
+
+static int functionfs_ready_callback(struct ffs_data *ffs)
+	__attribute__((warn_unused_result, nonnull));
+static void functionfs_closed_callback(struct ffs_data *ffs)
+	__attribute__((nonnull));
+static int functionfs_check_dev_callback(const char *dev_name)
+	__attribute__((warn_unused_result, nonnull));
+
+
+#endif
+
+#endif
-- 
1.7.0


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [PATCH 4/8] USB: Ethernet: allow optional removal of __init and __init_data tags
  2010-04-07 13:41     ` [PATCH 3/8] USB: f_fs: the FunctionFS driver Michal Nazarewicz
@ 2010-04-07 13:41       ` Michal Nazarewicz
  2010-04-07 13:41         ` [PATCH 5/8] USB: g_ffs: the FunctionFS gadget driver Michal Nazarewicz
  2010-04-07 17:11       ` [PATCH 3/8] USB: f_fs: the FunctionFS driver Michał Nazarewicz
  1 sibling, 1 reply; 47+ messages in thread
From: Michal Nazarewicz @ 2010-04-07 13:41 UTC (permalink / raw)
  To: linux-usb
  Cc: Peter Korsgaard, Rupesh Gujare, linux-kernel, David Brownell,
	Kyungmin Park, Marek Szyprowski, Michal Nazarewicz

Use the __usb_init and __usb_initdata tags in Ethernet
composite function related files to allow disabling the init
section micro-optimisation in drivers that require this.

Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com>
Cc: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
---
 drivers/usb/gadget/f_acm.c   |   34 ++++++++++++++++++----------------
 drivers/usb/gadget/f_ecm.c   |   33 +++++++++++++++++----------------
 drivers/usb/gadget/f_rndis.c |   34 ++++++++++++++++++----------------
 drivers/usb/gadget/u_ether.c |    6 ++++--
 4 files changed, 57 insertions(+), 50 deletions(-)

diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c
index 400e1eb..449296c 100644
--- a/drivers/usb/gadget/f_acm.c
+++ b/drivers/usb/gadget/f_acm.c
@@ -116,7 +116,8 @@ acm_iad_descriptor = {
 };
 
 
-static struct usb_interface_descriptor acm_control_interface_desc __initdata = {
+static struct usb_interface_descriptor
+acm_control_interface_desc __usb_initdata = {
 	.bLength =		USB_DT_INTERFACE_SIZE,
 	.bDescriptorType =	USB_DT_INTERFACE,
 	/* .bInterfaceNumber = DYNAMIC */
@@ -127,7 +128,8 @@ static struct usb_interface_descriptor acm_control_interface_desc __initdata = {
 	/* .iInterface = DYNAMIC */
 };
 
-static struct usb_interface_descriptor acm_data_interface_desc __initdata = {
+static struct usb_interface_descriptor
+acm_data_interface_desc __usb_initdata = {
 	.bLength =		USB_DT_INTERFACE_SIZE,
 	.bDescriptorType =	USB_DT_INTERFACE,
 	/* .bInterfaceNumber = DYNAMIC */
@@ -138,7 +140,7 @@ static struct usb_interface_descriptor acm_data_interface_desc __initdata = {
 	/* .iInterface = DYNAMIC */
 };
 
-static struct usb_cdc_header_desc acm_header_desc __initdata = {
+static struct usb_cdc_header_desc acm_header_desc __usb_initdata = {
 	.bLength =		sizeof(acm_header_desc),
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
@@ -146,7 +148,7 @@ static struct usb_cdc_header_desc acm_header_desc __initdata = {
 };
 
 static struct usb_cdc_call_mgmt_descriptor
-acm_call_mgmt_descriptor __initdata = {
+acm_call_mgmt_descriptor __usb_initdata = {
 	.bLength =		sizeof(acm_call_mgmt_descriptor),
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubType =	USB_CDC_CALL_MANAGEMENT_TYPE,
@@ -154,14 +156,14 @@ acm_call_mgmt_descriptor __initdata = {
 	/* .bDataInterface = DYNAMIC */
 };
 
-static struct usb_cdc_acm_descriptor acm_descriptor __initdata = {
+static struct usb_cdc_acm_descriptor acm_descriptor __usb_initdata = {
 	.bLength =		sizeof(acm_descriptor),
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubType =	USB_CDC_ACM_TYPE,
 	.bmCapabilities =	USB_CDC_CAP_LINE,
 };
 
-static struct usb_cdc_union_desc acm_union_desc __initdata = {
+static struct usb_cdc_union_desc acm_union_desc __usb_initdata = {
 	.bLength =		sizeof(acm_union_desc),
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
@@ -171,7 +173,7 @@ static struct usb_cdc_union_desc acm_union_desc __initdata = {
 
 /* full speed support: */
 
-static struct usb_endpoint_descriptor acm_fs_notify_desc __initdata = {
+static struct usb_endpoint_descriptor acm_fs_notify_desc __usb_initdata = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 	.bEndpointAddress =	USB_DIR_IN,
@@ -180,21 +182,21 @@ static struct usb_endpoint_descriptor acm_fs_notify_desc __initdata = {
 	.bInterval =		1 << GS_LOG2_NOTIFY_INTERVAL,
 };
 
-static struct usb_endpoint_descriptor acm_fs_in_desc __initdata = {
+static struct usb_endpoint_descriptor acm_fs_in_desc __usb_initdata = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 	.bEndpointAddress =	USB_DIR_IN,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 };
 
-static struct usb_endpoint_descriptor acm_fs_out_desc __initdata = {
+static struct usb_endpoint_descriptor acm_fs_out_desc __usb_initdata = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 	.bEndpointAddress =	USB_DIR_OUT,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 };
 
-static struct usb_descriptor_header *acm_fs_function[] __initdata = {
+static struct usb_descriptor_header *acm_fs_function[] __usb_initdata = {
 	(struct usb_descriptor_header *) &acm_iad_descriptor,
 	(struct usb_descriptor_header *) &acm_control_interface_desc,
 	(struct usb_descriptor_header *) &acm_header_desc,
@@ -210,7 +212,7 @@ static struct usb_descriptor_header *acm_fs_function[] __initdata = {
 
 /* high speed support: */
 
-static struct usb_endpoint_descriptor acm_hs_notify_desc __initdata = {
+static struct usb_endpoint_descriptor acm_hs_notify_desc __usb_initdata = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 	.bEndpointAddress =	USB_DIR_IN,
@@ -219,21 +221,21 @@ static struct usb_endpoint_descriptor acm_hs_notify_desc __initdata = {
 	.bInterval =		GS_LOG2_NOTIFY_INTERVAL+4,
 };
 
-static struct usb_endpoint_descriptor acm_hs_in_desc __initdata = {
+static struct usb_endpoint_descriptor acm_hs_in_desc __usb_initdata = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 	.wMaxPacketSize =	cpu_to_le16(512),
 };
 
-static struct usb_endpoint_descriptor acm_hs_out_desc __initdata = {
+static struct usb_endpoint_descriptor acm_hs_out_desc __usb_initdata = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 	.wMaxPacketSize =	cpu_to_le16(512),
 };
 
-static struct usb_descriptor_header *acm_hs_function[] __initdata = {
+static struct usb_descriptor_header *acm_hs_function[] __usb_initdata = {
 	(struct usb_descriptor_header *) &acm_iad_descriptor,
 	(struct usb_descriptor_header *) &acm_control_interface_desc,
 	(struct usb_descriptor_header *) &acm_header_desc,
@@ -571,7 +573,7 @@ static int acm_send_break(struct gserial *port, int duration)
 /*-------------------------------------------------------------------------*/
 
 /* ACM function driver setup/binding */
-static int __init
+static int __usb_init
 acm_bind(struct usb_configuration *c, struct usb_function *f)
 {
 	struct usb_composite_dev *cdev = c->cdev;
@@ -719,7 +721,7 @@ static inline bool can_support_cdc(struct usb_configuration *c)
  * handle all the ones it binds.  Caller is also responsible
  * for calling @gserial_cleanup() before module unload.
  */
-int __init acm_bind_config(struct usb_configuration *c, u8 port_num)
+int __usb_init acm_bind_config(struct usb_configuration *c, u8 port_num)
 {
 	struct f_acm	*acm;
 	int		status;
diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c
index 4e59532..c880d45 100644
--- a/drivers/usb/gadget/f_ecm.c
+++ b/drivers/usb/gadget/f_ecm.c
@@ -113,7 +113,7 @@ static inline unsigned ecm_bitrate(struct usb_gadget *g)
 
 /* interface descriptor: */
 
-static struct usb_interface_descriptor ecm_control_intf __initdata = {
+static struct usb_interface_descriptor ecm_control_intf __usb_initdata = {
 	.bLength =		sizeof ecm_control_intf,
 	.bDescriptorType =	USB_DT_INTERFACE,
 
@@ -126,7 +126,7 @@ static struct usb_interface_descriptor ecm_control_intf __initdata = {
 	/* .iInterface = DYNAMIC */
 };
 
-static struct usb_cdc_header_desc ecm_header_desc __initdata = {
+static struct usb_cdc_header_desc ecm_header_desc __usb_initdata = {
 	.bLength =		sizeof ecm_header_desc,
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
@@ -134,7 +134,7 @@ static struct usb_cdc_header_desc ecm_header_desc __initdata = {
 	.bcdCDC =		cpu_to_le16(0x0110),
 };
 
-static struct usb_cdc_union_desc ecm_union_desc __initdata = {
+static struct usb_cdc_union_desc ecm_union_desc __usb_initdata = {
 	.bLength =		sizeof(ecm_union_desc),
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
@@ -142,7 +142,7 @@ static struct usb_cdc_union_desc ecm_union_desc __initdata = {
 	/* .bSlaveInterface0 =	DYNAMIC */
 };
 
-static struct usb_cdc_ether_desc ecm_desc __initdata = {
+static struct usb_cdc_ether_desc ecm_desc __usb_initdata = {
 	.bLength =		sizeof ecm_desc,
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubType =	USB_CDC_ETHERNET_TYPE,
@@ -157,7 +157,7 @@ static struct usb_cdc_ether_desc ecm_desc __initdata = {
 
 /* the default data interface has no endpoints ... */
 
-static struct usb_interface_descriptor ecm_data_nop_intf __initdata = {
+static struct usb_interface_descriptor ecm_data_nop_intf __usb_initdata = {
 	.bLength =		sizeof ecm_data_nop_intf,
 	.bDescriptorType =	USB_DT_INTERFACE,
 
@@ -172,7 +172,7 @@ static struct usb_interface_descriptor ecm_data_nop_intf __initdata = {
 
 /* ... but the "real" data interface has two bulk endpoints */
 
-static struct usb_interface_descriptor ecm_data_intf __initdata = {
+static struct usb_interface_descriptor ecm_data_intf __usb_initdata = {
 	.bLength =		sizeof ecm_data_intf,
 	.bDescriptorType =	USB_DT_INTERFACE,
 
@@ -187,7 +187,7 @@ static struct usb_interface_descriptor ecm_data_intf __initdata = {
 
 /* full speed support: */
 
-static struct usb_endpoint_descriptor fs_ecm_notify_desc __initdata = {
+static struct usb_endpoint_descriptor fs_ecm_notify_desc __usb_initdata = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
@@ -197,7 +197,7 @@ static struct usb_endpoint_descriptor fs_ecm_notify_desc __initdata = {
 	.bInterval =		1 << LOG2_STATUS_INTERVAL_MSEC,
 };
 
-static struct usb_endpoint_descriptor fs_ecm_in_desc __initdata = {
+static struct usb_endpoint_descriptor fs_ecm_in_desc __usb_initdata = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
@@ -205,7 +205,7 @@ static struct usb_endpoint_descriptor fs_ecm_in_desc __initdata = {
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 };
 
-static struct usb_endpoint_descriptor fs_ecm_out_desc __initdata = {
+static struct usb_endpoint_descriptor fs_ecm_out_desc __usb_initdata = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
@@ -213,7 +213,7 @@ static struct usb_endpoint_descriptor fs_ecm_out_desc __initdata = {
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 };
 
-static struct usb_descriptor_header *ecm_fs_function[] __initdata = {
+static struct usb_descriptor_header *ecm_fs_function[] __usb_initdata = {
 	/* CDC ECM control descriptors */
 	(struct usb_descriptor_header *) &ecm_control_intf,
 	(struct usb_descriptor_header *) &ecm_header_desc,
@@ -231,7 +231,7 @@ static struct usb_descriptor_header *ecm_fs_function[] __initdata = {
 
 /* high speed support: */
 
-static struct usb_endpoint_descriptor hs_ecm_notify_desc __initdata = {
+static struct usb_endpoint_descriptor hs_ecm_notify_desc __usb_initdata = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
@@ -240,7 +240,7 @@ static struct usb_endpoint_descriptor hs_ecm_notify_desc __initdata = {
 	.wMaxPacketSize =	cpu_to_le16(ECM_STATUS_BYTECOUNT),
 	.bInterval =		LOG2_STATUS_INTERVAL_MSEC + 4,
 };
-static struct usb_endpoint_descriptor hs_ecm_in_desc __initdata = {
+static struct usb_endpoint_descriptor hs_ecm_in_desc __usb_initdata = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
@@ -249,7 +249,7 @@ static struct usb_endpoint_descriptor hs_ecm_in_desc __initdata = {
 	.wMaxPacketSize =	cpu_to_le16(512),
 };
 
-static struct usb_endpoint_descriptor hs_ecm_out_desc __initdata = {
+static struct usb_endpoint_descriptor hs_ecm_out_desc __usb_initdata = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
@@ -258,7 +258,7 @@ static struct usb_endpoint_descriptor hs_ecm_out_desc __initdata = {
 	.wMaxPacketSize =	cpu_to_le16(512),
 };
 
-static struct usb_descriptor_header *ecm_hs_function[] __initdata = {
+static struct usb_descriptor_header *ecm_hs_function[] __usb_initdata = {
 	/* CDC ECM control descriptors */
 	(struct usb_descriptor_header *) &ecm_control_intf,
 	(struct usb_descriptor_header *) &ecm_header_desc,
@@ -597,7 +597,7 @@ static void ecm_close(struct gether *geth)
 
 /* ethernet function driver setup/binding */
 
-static int __init
+static int __usb_init
 ecm_bind(struct usb_configuration *c, struct usb_function *f)
 {
 	struct usb_composite_dev *cdev = c->cdev;
@@ -763,7 +763,8 @@ ecm_unbind(struct usb_configuration *c, struct usb_function *f)
  * Caller must have called @gether_setup().  Caller is also responsible
  * for calling @gether_cleanup() before module unload.
  */
-int __init ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
+int __usb_init
+ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
 {
 	struct f_ecm	*ecm;
 	int		status;
diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c
index 56b0221..35e8744 100644
--- a/drivers/usb/gadget/f_rndis.c
+++ b/drivers/usb/gadget/f_rndis.c
@@ -122,7 +122,7 @@ static unsigned int bitrate(struct usb_gadget *g)
 
 /* interface descriptor: */
 
-static struct usb_interface_descriptor rndis_control_intf __initdata = {
+static struct usb_interface_descriptor rndis_control_intf __usb_initdata = {
 	.bLength =		sizeof rndis_control_intf,
 	.bDescriptorType =	USB_DT_INTERFACE,
 
@@ -135,7 +135,7 @@ static struct usb_interface_descriptor rndis_control_intf __initdata = {
 	/* .iInterface = DYNAMIC */
 };
 
-static struct usb_cdc_header_desc header_desc __initdata = {
+static struct usb_cdc_header_desc header_desc __usb_initdata = {
 	.bLength =		sizeof header_desc,
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
@@ -143,7 +143,8 @@ static struct usb_cdc_header_desc header_desc __initdata = {
 	.bcdCDC =		cpu_to_le16(0x0110),
 };
 
-static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor __initdata = {
+static struct usb_cdc_call_mgmt_descriptor
+call_mgmt_descriptor __usb_initdata = {
 	.bLength =		sizeof call_mgmt_descriptor,
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubType =	USB_CDC_CALL_MANAGEMENT_TYPE,
@@ -152,7 +153,7 @@ static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor __initdata = {
 	.bDataInterface =	0x01,
 };
 
-static struct usb_cdc_acm_descriptor rndis_acm_descriptor __initdata = {
+static struct usb_cdc_acm_descriptor rndis_acm_descriptor __usb_initdata = {
 	.bLength =		sizeof rndis_acm_descriptor,
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubType =	USB_CDC_ACM_TYPE,
@@ -160,7 +161,7 @@ static struct usb_cdc_acm_descriptor rndis_acm_descriptor __initdata = {
 	.bmCapabilities =	0x00,
 };
 
-static struct usb_cdc_union_desc rndis_union_desc __initdata = {
+static struct usb_cdc_union_desc rndis_union_desc __usb_initdata = {
 	.bLength =		sizeof(rndis_union_desc),
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
@@ -170,7 +171,7 @@ static struct usb_cdc_union_desc rndis_union_desc __initdata = {
 
 /* the data interface has two bulk endpoints */
 
-static struct usb_interface_descriptor rndis_data_intf __initdata = {
+static struct usb_interface_descriptor rndis_data_intf __usb_initdata = {
 	.bLength =		sizeof rndis_data_intf,
 	.bDescriptorType =	USB_DT_INTERFACE,
 
@@ -198,7 +199,7 @@ rndis_iad_descriptor = {
 
 /* full speed support: */
 
-static struct usb_endpoint_descriptor fs_notify_desc __initdata = {
+static struct usb_endpoint_descriptor fs_notify_desc __usb_initdata = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
@@ -208,7 +209,7 @@ static struct usb_endpoint_descriptor fs_notify_desc __initdata = {
 	.bInterval =		1 << LOG2_STATUS_INTERVAL_MSEC,
 };
 
-static struct usb_endpoint_descriptor fs_in_desc __initdata = {
+static struct usb_endpoint_descriptor fs_in_desc __usb_initdata = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
@@ -216,7 +217,7 @@ static struct usb_endpoint_descriptor fs_in_desc __initdata = {
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 };
 
-static struct usb_endpoint_descriptor fs_out_desc __initdata = {
+static struct usb_endpoint_descriptor fs_out_desc __usb_initdata = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
@@ -224,7 +225,7 @@ static struct usb_endpoint_descriptor fs_out_desc __initdata = {
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 };
 
-static struct usb_descriptor_header *eth_fs_function[] __initdata = {
+static struct usb_descriptor_header *eth_fs_function[] __usb_initdata = {
 	(struct usb_descriptor_header *) &rndis_iad_descriptor,
 	/* control interface matches ACM, not Ethernet */
 	(struct usb_descriptor_header *) &rndis_control_intf,
@@ -242,7 +243,7 @@ static struct usb_descriptor_header *eth_fs_function[] __initdata = {
 
 /* high speed support: */
 
-static struct usb_endpoint_descriptor hs_notify_desc __initdata = {
+static struct usb_endpoint_descriptor hs_notify_desc __usb_initdata = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
@@ -251,7 +252,7 @@ static struct usb_endpoint_descriptor hs_notify_desc __initdata = {
 	.wMaxPacketSize =	cpu_to_le16(STATUS_BYTECOUNT),
 	.bInterval =		LOG2_STATUS_INTERVAL_MSEC + 4,
 };
-static struct usb_endpoint_descriptor hs_in_desc __initdata = {
+static struct usb_endpoint_descriptor hs_in_desc __usb_initdata = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
@@ -260,7 +261,7 @@ static struct usb_endpoint_descriptor hs_in_desc __initdata = {
 	.wMaxPacketSize =	cpu_to_le16(512),
 };
 
-static struct usb_endpoint_descriptor hs_out_desc __initdata = {
+static struct usb_endpoint_descriptor hs_out_desc __usb_initdata = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
@@ -269,7 +270,7 @@ static struct usb_endpoint_descriptor hs_out_desc __initdata = {
 	.wMaxPacketSize =	cpu_to_le16(512),
 };
 
-static struct usb_descriptor_header *eth_hs_function[] __initdata = {
+static struct usb_descriptor_header *eth_hs_function[] __usb_initdata = {
 	(struct usb_descriptor_header *) &rndis_iad_descriptor,
 	/* control interface matches ACM, not Ethernet */
 	(struct usb_descriptor_header *) &rndis_control_intf,
@@ -594,7 +595,7 @@ static void rndis_close(struct gether *geth)
 
 /* ethernet function driver setup/binding */
 
-static int __init
+static int __usb_init
 rndis_bind(struct usb_configuration *c, struct usb_function *f)
 {
 	struct usb_composite_dev *cdev = c->cdev;
@@ -786,7 +787,8 @@ static inline bool can_support_rndis(struct usb_configuration *c)
  * Caller must have called @gether_setup().  Caller is also responsible
  * for calling @gether_cleanup() before module unload.
  */
-int __init rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
+int __usb_init
+rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
 {
 	struct f_rndis	*rndis;
 	int		status;
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
index 07f4178..2d46412 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -31,6 +31,8 @@
 
 #include "u_ether.h"
 
+#include "usb-init-exit.h"
+
 
 /*
  * This component encapsulates the Ethernet link glue needed to provide
@@ -715,7 +717,7 @@ static u8 __init nibble(unsigned char c)
 	return 0;
 }
 
-static int __init get_ether_addr(const char *str, u8 *dev_addr)
+static int __usb_init get_ether_addr(const char *str, u8 *dev_addr)
 {
 	if (str) {
 		unsigned	i;
@@ -764,7 +766,7 @@ static struct device_type gadget_type = {
  *
  * Returns negative errno, or zero on success
  */
-int __init gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN])
+int __usb_init gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN])
 {
 	struct eth_dev		*dev;
 	struct net_device	*net;
-- 
1.7.0


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [PATCH 5/8] USB: g_ffs: the FunctionFS gadget driver
  2010-04-07 13:41       ` [PATCH 4/8] USB: Ethernet: allow optional removal of __init and __init_data tags Michal Nazarewicz
@ 2010-04-07 13:41         ` Michal Nazarewicz
  2010-04-07 13:41           ` [PATCH 6/8] USB: ffs-test: FunctionFS testing program Michal Nazarewicz
  0 siblings, 1 reply; 47+ messages in thread
From: Michal Nazarewicz @ 2010-04-07 13:41 UTC (permalink / raw)
  To: linux-usb
  Cc: Peter Korsgaard, Rupesh Gujare, linux-kernel, David Brownell,
	Kyungmin Park, Marek Szyprowski, Michal Nazarewicz

The Function Filesystem (FunctioFS) lets one create USB
composite functions in user space in the same way as GadgetFS
lets one create USB gadgets in user space.  This allows
creation of composite gadgets such that some of the functions
are implemented in kernel space (for instance Ethernet, serial
or mass storage) and other are implemented in user space.

Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com>
Cc: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
---
 drivers/usb/gadget/Kconfig  |   21 +++-
 drivers/usb/gadget/Makefile |    2 +
 drivers/usb/gadget/g_ffs.c  |  322 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 344 insertions(+), 1 deletions(-)
 create mode 100644 drivers/usb/gadget/g_ffs.c

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 11a3e0f..e71a730 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -710,6 +710,26 @@ config USB_GADGETFS
 	  Say "y" to link the driver statically, or "m" to build a
 	  dynamically linked module called "gadgetfs".
 
+config USB_FUNCTIONFS
+	tristate "Function Filesystem (EXPERIMENTAL)"
+	depends on EXPERIMENTAL
+	help
+	  The Function Filesystem (FunctioFS) lets one create USB
+	  composite functions in user space in the same way as GadgetFS
+	  lets one create USB gadgets in user space.  This allows creation
+	  of composite gadgets such that some of the functions are
+	  implemented in kernel space (for instance Ethernet, serial or
+	  mass storage) and other are implemented in user space.
+
+	  Say "y" to link the driver statically, or "m" to build
+	  a dynamically linked module called "g_ffs".
+
+config USB_FUNCTIONFS_ETH
+	bool "Include Ethernet funcien"
+	depends on USB_FUNCTIONFS
+	help
+	  Include an Ethernet funcion in the Funcion Filesystem.
+
 config USB_FILE_STORAGE
 	tristate "File-backed Storage Gadget"
 	depends on BLOCK
@@ -863,7 +883,6 @@ config USB_G_MULTI_CDC
 
 	  If unsure, say "y".
 
-
 # put drivers that need isochronous transfer support (for audio
 # or video class gadget drivers), or specific hardware, here.
 
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 43b51da..6d9d638 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -49,6 +49,8 @@ obj-$(CONFIG_USB_ZERO)		+= g_zero.o
 obj-$(CONFIG_USB_AUDIO)		+= g_audio.o
 obj-$(CONFIG_USB_ETH)		+= g_ether.o
 obj-$(CONFIG_USB_GADGETFS)	+= gadgetfs.o
+obj-$(CONFIG_USB_FUNCTIONFS)	+= g_ffs.o
+obj-$(CONFIG_USB_ETH_FUNCTIONFS)	+= g_eth_ffs.o
 obj-$(CONFIG_USB_FILE_STORAGE)	+= g_file_storage.o
 obj-$(CONFIG_USB_MASS_STORAGE)	+= g_mass_storage.o
 obj-$(CONFIG_USB_G_SERIAL)	+= g_serial.o
diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c
new file mode 100644
index 0000000..6033f5d
--- /dev/null
+++ b/drivers/usb/gadget/g_ffs.c
@@ -0,0 +1,322 @@
+#include <linux/module.h>
+#include <linux/utsname.h>
+
+
+/*
+ * kbuild is not very cooperative with respect to linking separately
+ * compiled library objects into one module.  So for now we won't use
+ * separate compilation ... ensuring init/exit sections work to shrink
+ * the runtime footprint, and giving us at least some parts of what
+ * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
+ */
+
+/* Do not mark any of the USB and composite initialization functions
+ * as __init, we use it after user space writes descriptors which is
+ * when init segments are removed. */
+#define USB_NO_INIT_SEGMENT 1
+/* Do not mark composite_unbind() and usb_composite_unregister() as
+ * __exit, we may need it even if we are not unloaded. */
+#define USB_NO_EXIT_SEGMENT 1
+
+#include "composite.c"
+#include "usbstring.c"
+#include "config.c"
+#include "epautoconf.c"
+
+#ifdef CONFIG_USB_FUNCTIONFS_ETH
+/* #  if defined USB_ETH_RNDIS */
+/* #    undef USB_ETH_RNDIS */
+/* #  endif */
+/* #  ifdef CONFIG_USB_ETH_FUNCTIONFS_RNDIS */
+/* #    define USB_ETH_RNDIS y */
+/* #  endif */
+#  include "u_ether.h"
+
+#  include "f_ecm.c"
+#  include "f_subset.c"
+/* #  ifdef USB_ETH_RNDIS */
+/* #    include "f_rndis.c" */
+/* #    include "rndis.c" */
+/* #  endif */
+#  include "u_ether.c"
+
+static u8 gfs_hostaddr[ETH_ALEN];
+#endif
+
+#include "f_fs.c"
+
+
+#define DRIVER_NAME	"g_ffs"
+#define DRIVER_DESC	"USB Function Filesystem"
+#define DRIVER_VERSION	"24 Aug 2004"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Michal Nazarewicz");
+MODULE_LICENSE("GPL");
+
+
+static unsigned short gfs_vendor_id    = 0x0525;	/* XXX NetChip */
+static unsigned short gfs_product_id   = 0xa4ac;	/* XXX */
+
+static struct usb_device_descriptor gfs_dev_desc = {
+	.bLength		= sizeof gfs_dev_desc,
+	.bDescriptorType	= USB_DT_DEVICE,
+
+	.bcdUSB			= cpu_to_le16(0x0200),
+	.bDeviceClass		= USB_CLASS_PER_INTERFACE,
+
+	/* Vendor and product id can be overridden by module parameters.  */
+	/* .idVendor		= cpu_to_le16(gfs_vendor_id), */
+	/* .idProduct		= cpu_to_le16(gfs_product_id), */
+	/* .bcdDevice		= f(hardware) */
+	/* .iManufacturer	= DYNAMIC */
+	/* .iProduct		= DYNAMIC */
+	/* NO SERIAL NUMBER */
+	.bNumConfigurations	= 1,
+};
+
+#define GFS_MODULE_PARAM_DESC(name, field) \
+	MODULE_PARM_DESC(name, "Value of the " #field " field of the device descriptor sent to the host.  Takes effect only prior to the user-space driver registering to the FunctionFS.")
+
+module_param_named(usb_class,    gfs_dev_desc.bDeviceClass,    byte,   0644);
+GFS_MODULE_PARAM_DESC(usb_class, bDeviceClass);
+module_param_named(usb_subclass, gfs_dev_desc.bDeviceSubClass, byte,   0644);
+GFS_MODULE_PARAM_DESC(usb_subclass, bDeviceSubClass);
+module_param_named(usb_protocol, gfs_dev_desc.bDeviceProtocol, byte,   0644);
+GFS_MODULE_PARAM_DESC(usb_protocol, bDeviceProtocol);
+module_param_named(usb_vendor,   gfs_vendor_id,                ushort, 0644);
+GFS_MODULE_PARAM_DESC(usb_vendor, idVendor);
+module_param_named(usb_product,  gfs_product_id,               ushort, 0644);
+GFS_MODULE_PARAM_DESC(usb_product, idProduct);
+
+
+
+static const struct usb_descriptor_header *gfs_otg_desc[] = {
+	(const struct usb_descriptor_header *)
+	&(const struct usb_otg_descriptor) {
+		.bLength		= sizeof(struct usb_otg_descriptor),
+		.bDescriptorType	= USB_DT_OTG,
+
+		/* REVISIT SRP-only hardware is possible, although
+		 * it would not be called "OTG" ... */
+		.bmAttributes		= USB_OTG_SRP | USB_OTG_HNP,
+	},
+
+	NULL
+};
+
+/* string IDs are assigned dynamically */
+
+enum {
+	GFS_STRING_MANUFACTURER_IDX,
+	GFS_STRING_PRODUCT_IDX,
+	GFS_STRING_CONFIGURATION_IDX
+};
+
+static       char gfs_manufacturer[50];
+static const char gfs_driver_desc[] = DRIVER_DESC;
+static const char gfs_short_name[]  = DRIVER_NAME;
+
+static struct usb_string gfs_strings[] = {
+	[GFS_STRING_MANUFACTURER_IDX].s = gfs_manufacturer,
+	[GFS_STRING_PRODUCT_IDX].s = gfs_driver_desc,
+	[GFS_STRING_CONFIGURATION_IDX].s = "Self Powered",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings *gfs_dev_strings[] = {
+	&(struct usb_gadget_strings) {
+		.language	= 0x0409,	/* en-us */
+		.strings	= gfs_strings,
+	},
+	NULL,
+};
+
+
+static int gfs_bind(struct usb_composite_dev *cdev);
+static int gfs_unbind(struct usb_composite_dev *cdev);
+static int gfs_do_config(struct usb_configuration *c);
+
+
+static struct usb_composite_driver gfs_driver = {
+	.name		= gfs_short_name,
+	.dev		= &gfs_dev_desc,
+	.strings	= gfs_dev_strings,
+	.bind		= gfs_bind,
+	.unbind		= gfs_unbind,
+};
+
+static struct usb_configuration gfs_config_driver = {
+	.label			= "FunctionFS",
+	.bind			= gfs_do_config,
+	.bConfigurationValue	= 1,
+	/* .iConfiguration	= DYNAMIC */
+	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER,
+};
+
+
+static struct ffs_data *gfs_ffs_data;
+static unsigned long gfs_registered;
+
+
+static int __init gfs_init(void)
+{
+	ENTER();
+
+	return functionfs_init();
+}
+module_init(gfs_init);
+
+static void __exit gfs_exit(void)
+{
+	ENTER();
+
+	if (test_and_clear_bit(0, &gfs_registered))
+		usb_composite_unregister(&gfs_driver);
+
+	functionfs_cleanup();
+}
+module_exit(gfs_exit);
+
+
+static int functionfs_ready_callback(struct ffs_data *ffs)
+{
+	int ret;
+
+	ENTER();
+
+	if (WARN_ON(test_and_set_bit(0, &gfs_registered)))
+		return -EBUSY;
+
+	gfs_ffs_data = ffs;
+	ret = usb_composite_register(&gfs_driver);
+	if (unlikely(ret < 0))
+		clear_bit(0, &gfs_registered);
+	return ret;
+}
+
+static void functionfs_closed_callback(struct ffs_data *ffs)
+{
+	ENTER();
+
+	if (test_and_clear_bit(0, &gfs_registered))
+		usb_composite_unregister(&gfs_driver);
+}
+
+
+static int functionfs_check_dev_callback(const char *dev_name)
+{
+	return 0;
+}
+
+
+
+static int gfs_bind(struct usb_composite_dev *cdev)
+{
+	int ret;
+
+	ENTER();
+
+	if (WARN_ON(!gfs_ffs_data))
+		return -ENODEV;
+
+#ifdef CONFIG_USB_FUNCTIONFS_ETH
+	ret = gether_setup(cdev->gadget, gfs_hostaddr);
+	if (unlikely(ret < 0))
+		goto error_quick;
+#endif
+
+	gfs_dev_desc.idVendor  = cpu_to_le16(gfs_vendor_id);
+	gfs_dev_desc.idProduct = cpu_to_le16(gfs_product_id);
+
+	snprintf(gfs_manufacturer, sizeof gfs_manufacturer, "%s %s with %s",
+		 init_utsname()->sysname, init_utsname()->release,
+		 cdev->gadget->name);
+	ret = usb_string_id(cdev);
+	if (unlikely(ret < 0))
+		goto error;
+	gfs_strings[GFS_STRING_MANUFACTURER_IDX].id = ret;
+	gfs_dev_desc.iManufacturer = ret;
+
+	ret = usb_string_id(cdev);
+	if (unlikely(ret < 0))
+		goto error;
+	gfs_strings[GFS_STRING_PRODUCT_IDX].id = ret;
+	gfs_dev_desc.iProduct = ret;
+
+	ret = usb_string_id(cdev);
+	if (unlikely(ret < 0))
+		goto error;
+	gfs_strings[GFS_STRING_CONFIGURATION_IDX].id = ret;
+	gfs_config_driver.iConfiguration = ret;
+
+	ret = functionfs_bind(gfs_ffs_data, cdev);
+	if (unlikely(ret < 0))
+		goto error;
+
+	ret = usb_add_config(cdev, &gfs_config_driver);
+	if (unlikely(ret < 0))
+		goto error_unbind;
+
+	return 0;
+
+error_unbind:
+	functionfs_unbind(gfs_ffs_data);
+error:
+#ifdef CONFIG_USB_FUNCTIONFS_ETH
+	gether_cleanup();
+error_quick:
+#endif
+	gfs_ffs_data = NULL;
+	return ret;
+}
+
+static int gfs_unbind(struct usb_composite_dev *cdev)
+{
+	ENTER();
+
+#ifdef CONFIG_USB_FUNCTIONFS_ETH
+	gether_cleanup();
+#endif
+
+	if (!WARN_ON(!gfs_ffs_data)) {
+		functionfs_unbind(gfs_ffs_data);
+		gfs_ffs_data = NULL;
+	}
+
+	return 0;
+}
+
+
+static int gfs_do_config(struct usb_configuration *c)
+{
+	int ret;
+
+	ENTER();
+
+	if (WARN_ON(!gfs_ffs_data))
+		return -ENODEV;
+
+	if (gadget_is_otg(c->cdev->gadget)) {
+		c->descriptors = gfs_otg_desc;
+		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+	}
+
+#ifdef CONFIG_USB_FUNCTIONFS_ETH
+	if (can_support_ecm(c->cdev->gadget)) {
+		FINFO("ecm_bind_config");
+		ret = ecm_bind_config(c, gfs_hostaddr);
+	} else {
+		FINFO("geth_bind_config");
+		ret = geth_bind_config(c, gfs_hostaddr);
+	}
+	if (unlikely(ret < 0))
+		return ret;
+#endif
+
+	ret = functionfs_add(c->cdev, c, gfs_ffs_data);
+	if (unlikely(ret < 0))
+		return ret;
+
+	return 0;
+}
-- 
1.7.0


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [PATCH 6/8] USB: ffs-test: FunctionFS testing program
  2010-04-07 13:41         ` [PATCH 5/8] USB: g_ffs: the FunctionFS gadget driver Michal Nazarewicz
@ 2010-04-07 13:41           ` Michal Nazarewicz
  2010-04-07 13:41             ` [PATCH 7/8] USB: testusb: imported David Brownell's USB testing application Michal Nazarewicz
  0 siblings, 1 reply; 47+ messages in thread
From: Michal Nazarewicz @ 2010-04-07 13:41 UTC (permalink / raw)
  To: linux-usb
  Cc: Peter Korsgaard, Rupesh Gujare, linux-kernel, David Brownell,
	Kyungmin Park, Marek Szyprowski, Michal Nazarewicz

This adds an example user-space FunctionFS driver which
implements a source/sink interface used for testing.

Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com>
Cc: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
---
 tools/usb/ffs-test.c |  554 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 554 insertions(+), 0 deletions(-)
 create mode 100644 tools/usb/ffs-test.c

diff --git a/tools/usb/ffs-test.c b/tools/usb/ffs-test.c
new file mode 100644
index 0000000..bbe2e3a
--- /dev/null
+++ b/tools/usb/ffs-test.c
@@ -0,0 +1,554 @@
+/*
+ * ffs-test.c.c -- user mode filesystem api for usb composite function
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *                    Author: Michal Nazarewicz <m.nazarewicz@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* $(CROSS_COMPILE)cc -Wall -Wextra -g -o ffs-test ffs-test.c -lpthread */
+
+
+#define _BSD_SOURCE /* for endian.h */
+
+#include <endian.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <linux/usb/functionfs.h>
+
+
+/******************** Little Endian Handling ********************************/
+
+#define cpu_to_le16(x)  htole16(x)
+#define cpu_to_le32(x)  htole32(x)
+#define le32_to_cpu(x)  le32toh(x)
+#define le16_to_cpu(x)  le16toh(x)
+
+static inline __u16 get_unaligned_le16(const void *_ptr)
+{
+	const __u8 *ptr = _ptr;
+	return ptr[0] | (ptr[1] << 8);
+}
+
+static inline __u32 get_unaligned_le32(const void *_ptr)
+{
+	const __u8 *ptr = _ptr;
+	return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
+}
+
+static inline void put_unaligned_le16(__u16 val, void *_ptr)
+{
+	__u8 *ptr = _ptr;
+	*ptr++ = val;
+	*ptr++ = val >> 8;
+}
+
+static inline void put_unaligned_le32(__u32 val, void *_ptr)
+{
+	__u8 *ptr = _ptr;
+	*ptr++ = val;
+	*ptr++ = val >>  8;
+	*ptr++ = val >> 16;
+	*ptr++ = val >> 24;
+}
+
+
+/******************** Messages and Errors ***********************************/
+
+static const char argv0[] = "ffs-test";
+
+static unsigned verbosity = 7;
+
+static void _msg(unsigned level, const char *fmt, ...)
+{
+	if (level < 2)
+		level = 2;
+	else if (level > 7)
+		level = 7;
+
+	if (level <= verbosity) {
+		static const char levels[8][6] = {
+			[2] = "crit:",
+			[3] = "err: ",
+			[4] = "warn:",
+			[5] = "note:",
+			[6] = "info:",
+			[7] = "dbg: "
+		};
+
+		int _errno = errno;
+		va_list ap;
+
+		fprintf(stderr, "%s: %s ", argv0, levels[level]);
+		va_start(ap, fmt);
+		vfprintf(stderr, fmt, ap);
+		va_end(ap);
+
+		if (fmt[strlen(fmt) - 1] != '\n') {
+			char buffer[128];
+			strerror_r(_errno, buffer, sizeof buffer);
+			fprintf(stderr, ": (-%d) %s\n", _errno, buffer);
+		}
+
+		fflush(stderr);
+	}
+}
+
+#define die(...)  (_msg(2, __VA_ARGS__), exit(1))
+#define err(...)   _msg(3, __VA_ARGS__)
+#define warn(...)  _msg(4, __VA_ARGS__)
+#define note(...)  _msg(5, __VA_ARGS__)
+#define info(...)  _msg(6, __VA_ARGS__)
+#define debug(...) _msg(7, __VA_ARGS__)
+
+#define die_on(cond, ...) do { \
+	if (cond) \
+		die(__VA_ARGS__); \
+	} while (0)
+
+
+/******************** Descriptors and Strings *******************************/
+
+static const struct {
+	struct usb_functionfs_descs_head header;
+	struct {
+		struct usb_interface_descriptor intf;
+		struct usb_endpoint_descriptor_no_audio sink;
+		struct usb_endpoint_descriptor_no_audio source;
+	} __attribute__((packed)) fs_descs, hs_descs;
+} __attribute__((packed)) descriptors = {
+	.header = {
+		.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC),
+		.length = cpu_to_le32(sizeof descriptors),
+		.fs_count = 3,
+		.hs_count = 3,
+	},
+	.fs_descs = {
+		.intf = {
+			.bLength = sizeof descriptors.fs_descs.intf,
+			.bDescriptorType = USB_DT_INTERFACE,
+			.bNumEndpoints = 2,
+			.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+			.iInterface = 1,
+		},
+		.sink = {
+			.bLength = sizeof descriptors.fs_descs.sink,
+			.bDescriptorType = USB_DT_ENDPOINT,
+			.bEndpointAddress = 1 | USB_DIR_IN,
+			.bmAttributes = USB_ENDPOINT_XFER_BULK,
+			/* .wMaxPacketSize = autoconfiguration (kernel) */
+		},
+		.source = {
+			.bLength = sizeof descriptors.fs_descs.source,
+			.bDescriptorType = USB_DT_ENDPOINT,
+			.bEndpointAddress = 2 | USB_DIR_OUT,
+			.bmAttributes = USB_ENDPOINT_XFER_BULK,
+			/* .wMaxPacketSize = autoconfiguration (kernel) */
+		},
+	},
+	.hs_descs = {
+		.intf = {
+			.bLength = sizeof descriptors.fs_descs.intf,
+			.bDescriptorType = USB_DT_INTERFACE,
+			.bNumEndpoints = 2,
+			.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+			.iInterface = 1,
+		},
+		.sink = {
+			.bLength = sizeof descriptors.hs_descs.sink,
+			.bDescriptorType = USB_DT_ENDPOINT,
+			.bEndpointAddress = 1 | USB_DIR_IN,
+			.bmAttributes = USB_ENDPOINT_XFER_BULK,
+			.wMaxPacketSize = cpu_to_le16(512),
+		},
+		.source = {
+			.bLength = sizeof descriptors.hs_descs.source,
+			.bDescriptorType = USB_DT_ENDPOINT,
+			.bEndpointAddress = 2 | USB_DIR_OUT,
+			.bmAttributes = USB_ENDPOINT_XFER_BULK,
+			.wMaxPacketSize = cpu_to_le16(512),
+			.bInterval = 1, /* NAK every 1 uframe */
+		},
+	},
+};
+
+
+#define STR_INTERFACE_ "Source/Sink"
+
+static const struct {
+	struct usb_functionfs_strings_head header;
+	struct {
+		__le16 code;
+		const char str1[sizeof STR_INTERFACE_];
+	} __attribute__((packed)) lang0;
+} __attribute__((packed)) strings = {
+	.header = {
+		.magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
+		.length = cpu_to_le32(sizeof strings),
+		.str_count = cpu_to_le32(1),
+		.lang_count = cpu_to_le32(1),
+	},
+	.lang0 = {
+		cpu_to_le16(0x0409), /* en-us */
+		STR_INTERFACE_,
+	},
+};
+
+#define STR_INTERFACE strings.lang0.str1
+
+
+/******************** Files and Threads Handling ****************************/
+
+struct thread;
+
+static ssize_t read_wrap(struct thread *t, void *buf, size_t nbytes);
+static ssize_t write_wrap(struct thread *t, const void *buf, size_t nbytes);
+static ssize_t ep0_consume(struct thread *t, const void *buf, size_t nbytes);
+static ssize_t fill_in_buf(struct thread *t, void *buf, size_t nbytes);
+static ssize_t empty_out_buf(struct thread *t, const void *buf, size_t nbytes);
+
+
+static struct thread {
+	const char *const filename;
+	size_t buf_size;
+
+	ssize_t (*in)(struct thread *, void *, size_t);
+	const char *const in_name;
+
+	ssize_t (*out)(struct thread *, const void *, size_t);
+	const char *const out_name;
+
+	int fd;
+	pthread_t id;
+	void *buf;
+	ssize_t status;
+} threads[] = {
+	{
+		"ep0", 4 * sizeof(struct usb_functionfs_event),
+		read_wrap, NULL,
+		ep0_consume, "<consume>",
+		0, 0, NULL, 0
+	},
+	{
+		"ep1", 8 * 1024,
+		fill_in_buf, "<in>",
+		write_wrap, NULL,
+		0, 0, NULL, 0
+	},
+	{
+		"ep2", 8 * 1024,
+		read_wrap, NULL,
+		empty_out_buf, "<out>",
+		0, 0, NULL, 0
+	},
+};
+
+
+static void init_thread(struct thread *t)
+{
+	t->buf = malloc(t->buf_size);
+	die_on(!t->buf, "malloc");
+
+	t->fd = open(t->filename, O_RDWR);
+	die_on(t->fd < 0, "%s", t->filename);
+}
+
+static void cleanup_thread(void *arg)
+{
+	struct thread *t = arg;
+	int ret, fd;
+
+	fd = t->fd;
+	if (t->fd < 0)
+		return;
+	t->fd = -1;
+
+	/* test the FIFO ioctls (non-ep0 code paths) */
+	if (t != threads) {
+		ret = ioctl(fd, FUNCTIONFS_FIFO_STATUS);
+		if (ret < 0) {
+			/* ENODEV reported after disconnect */
+			if (errno != ENODEV)
+				err("%s: get fifo status", t->filename);
+		} else if (ret) {
+			warn("%s: unclaimed = %d\n", t->filename, ret);
+			if (ioctl(fd, FUNCTIONFS_FIFO_FLUSH) < 0)
+				err("%s: fifo flush", t->filename);
+		}
+	}
+
+	if (close(fd) < 0)
+		err("%s: close", t->filename);
+
+	free(t->buf);
+	t->buf = NULL;
+}
+
+static void *start_thread_helper(void *arg)
+{
+	const char *name, *op, *in_name, *out_name;
+	struct thread *t = arg;
+	ssize_t ret;
+
+	info("%s: starts\n", t->filename);
+	in_name = t->in_name ? t->in_name : t->filename;
+	out_name = t->out_name ? t->out_name : t->filename;
+
+	pthread_cleanup_push(cleanup_thread, arg);
+
+	for (;;) {
+		pthread_testcancel();
+
+		ret = t->in(t, t->buf, t->buf_size);
+		if (ret > 0) {
+			ret = t->out(t, t->buf, t->buf_size);
+			name = out_name;
+			op = "write";
+		} else {
+			name = in_name;
+			op = "read";
+		}
+
+		if (ret > 0) {
+			/* nop */
+		} else if (!ret) {
+			debug("%s: %s: EOF", name, op);
+			break;
+		} else if (errno == EINTR || errno == EAGAIN) {
+			debug("%s: %s", name, op);
+		} else {
+			warn("%s: %s", name, op);
+			break;
+		}
+	}
+
+	pthread_cleanup_pop(1);
+
+	t->status = ret;
+	info("%s: ends\n", t->filename);
+	return NULL;
+}
+
+static void start_thread(struct thread *t)
+{
+	debug("%s: starting\n", t->filename);
+
+	die_on(pthread_create(&t->id, NULL, start_thread_helper, t) < 0,
+	       "pthread_create(%s)", t->filename);
+}
+
+static void join_thread(struct thread *t)
+{
+	int ret = pthread_join(t->id, NULL);
+
+	if (ret < 0)
+		err("%s: joining thread", t->filename);
+	else
+		debug("%s: joined\n", t->filename);
+}
+
+
+static ssize_t read_wrap(struct thread *t, void *buf, size_t nbytes)
+{
+	return read(t->fd, buf, nbytes);
+}
+
+static ssize_t write_wrap(struct thread *t, const void *buf, size_t nbytes)
+{
+	return write(t->fd, buf, nbytes);
+}
+
+
+/******************** Empty/Fill buffer routines ****************************/
+
+/* 0 -- stream of zeros, 1 -- i % 63, 2 -- pipe */
+enum pattern { PAT_ZERO, PAT_SEQ, PAT_PIPE };
+static enum pattern pattern;
+
+static ssize_t
+fill_in_buf(struct thread *ignore, void *buf, size_t nbytes)
+{
+	size_t i;
+	__u8 *p;
+
+	(void)ignore;
+
+	switch (pattern) {
+	case PAT_ZERO:
+		memset(buf, 0, nbytes);
+		break;
+
+	case PAT_SEQ:
+		for (p = buf, i = 0; i < nbytes; ++i, ++p)
+			*p = i % 63;
+		break;
+
+	case PAT_PIPE:
+		return fread(buf, 1, nbytes, stdin);
+	}
+
+	return nbytes;
+}
+
+static ssize_t
+empty_out_buf(struct thread *ignore, const void *buf, size_t nbytes)
+{
+	const __u8 *p;
+	__u8 expected;
+	ssize_t ret;
+	size_t len;
+
+	(void)ignore;
+
+	switch (pattern) {
+	case PAT_ZERO:
+		expected = 0;
+		for (p = buf, len = 0; len < nbytes; ++p, ++len)
+			if (*p)
+				goto invalid;
+		break;
+
+	case PAT_SEQ:
+		for (p = buf, len = 0; len < nbytes; ++p, ++len)
+			if (*p != len % 63) {
+				expected = len % 63;
+				goto invalid;
+			}
+		break;
+
+	case PAT_PIPE:
+		ret = fwrite(buf, nbytes, 1, stdout);
+		if (ret > 0)
+			fflush(stdout);
+		break;
+
+invalid:
+		err("bad OUT byte %zd, expected %02x got %02x\n",
+		    len, expected, *p);
+		for (p = buf, len = 0; len < nbytes; ++p, ++len) {
+			if (0 == (len % 32))
+				fprintf(stderr, "%4d:", len);
+			fprintf(stderr, " %02x", *p);
+			if (31 == (len % 32))
+				fprintf(stderr, "\n");
+		}
+		fflush(stderr);
+		errno = EILSEQ;
+		return -1;
+	}
+
+	return len;
+}
+
+
+/******************** Endpoints routines ************************************/
+
+static void handle_setup(const struct usb_ctrlrequest *setup)
+{
+	printf("bRequestType = %d\n", setup->bRequestType);
+	printf("bRequest     = %d\n", setup->bRequest);
+	printf("wValue       = %d\n", le16_to_cpu(setup->wValue));
+	printf("wIndex       = %d\n", le16_to_cpu(setup->wIndex));
+	printf("wLength      = %d\n", le16_to_cpu(setup->wLength));
+}
+
+static ssize_t
+ep0_consume(struct thread *ignore, const void *buf, size_t nbytes)
+{
+	static const char *const names[] = {
+		[FUNCTIONFS_BIND] = "BIND",
+		[FUNCTIONFS_UNBIND] = "UNBIND",
+		[FUNCTIONFS_ENABLE] = "ENABLE",
+		[FUNCTIONFS_DISABLE] = "DISABLE",
+		[FUNCTIONFS_SETUP] = "SETUP",
+		[FUNCTIONFS_SUSPEND] = "SUSPEND",
+		[FUNCTIONFS_RESUME] = "RESUME",
+	};
+
+	const struct usb_functionfs_event *event = buf;
+	size_t n;
+
+	(void)ignore;
+
+	for (n = nbytes / sizeof *event; n; --n, ++event)
+		switch (event->type) {
+		case FUNCTIONFS_BIND:
+		case FUNCTIONFS_UNBIND:
+		case FUNCTIONFS_ENABLE:
+		case FUNCTIONFS_DISABLE:
+		case FUNCTIONFS_SETUP:
+		case FUNCTIONFS_SUSPEND:
+		case FUNCTIONFS_RESUME:
+			printf("Event %s\n", names[event->type]);
+			if (event->type == FUNCTIONFS_SETUP)
+				handle_setup(&event->u.setup);
+			break;
+
+		default:
+			printf("Event %03u (unknown)\n", event->type);
+		}
+
+	return nbytes;
+}
+
+static void ep0_init(struct thread *t)
+{
+	ssize_t ret;
+
+	info("%s: writing descriptors\n", t->filename);
+	ret = write(t->fd, &descriptors, sizeof descriptors);
+	die_on(ret < 0, "%s: write: descriptors", t->filename);
+
+	info("%s: writing strings\n", t->filename);
+	ret = write(t->fd, &strings, sizeof strings);
+	die_on(ret < 0, "%s: write: strings", t->filename);
+}
+
+
+/******************** Main **************************************************/
+
+int main(void)
+{
+	unsigned i;
+
+	/* XXX TODO: Argument parsing missing */
+
+	init_thread(threads);
+	ep0_init(threads);
+
+	for (i = 1; i < sizeof threads / sizeof *threads; ++i)
+		init_thread(threads + i);
+
+	for (i = 1; i < sizeof threads / sizeof *threads; ++i)
+		start_thread(threads + i);
+
+	start_thread_helper(threads);
+
+	for (i = 1; i < sizeof threads / sizeof *threads; ++i)
+		join_thread(threads + i);
+
+	return 0;
+}
-- 
1.7.0


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [PATCH 7/8] USB: testusb: imported David Brownell's USB testing application
  2010-04-07 13:41           ` [PATCH 6/8] USB: ffs-test: FunctionFS testing program Michal Nazarewicz
@ 2010-04-07 13:41             ` Michal Nazarewicz
  2010-04-07 13:41               ` [PATCH 8/8] USB: testusb: testusb compatibility with FunctionFS gadget Michal Nazarewicz
                                 ` (3 more replies)
  0 siblings, 4 replies; 47+ messages in thread
From: Michal Nazarewicz @ 2010-04-07 13:41 UTC (permalink / raw)
  To: linux-usb
  Cc: Peter Korsgaard, Rupesh Gujare, linux-kernel, David Brownell,
	Kyungmin Park, Marek Szyprowski, Michal Nazarewicz

The testusb application can be used to test various USB gadget
that implment the source/sink intrerface.

Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com>
Cc: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
---
 tools/usb/testusb.c |  427 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 427 insertions(+), 0 deletions(-)
 create mode 100644 tools/usb/testusb.c

diff --git a/tools/usb/testusb.c b/tools/usb/testusb.c
new file mode 100644
index 0000000..28e25ca
--- /dev/null
+++ b/tools/usb/testusb.c
@@ -0,0 +1,427 @@
+/* $(CROSS_COMPILE)cc -Wall -g -lpthread -o testusb testusb.c */
+
+/*
+ * Copyright (c) 2002 by David Brownell
+ * 
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ftw.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <sys/ioctl.h>
+#include <linux/usbdevice_fs.h>
+
+/*-------------------------------------------------------------------------*/
+
+#define	TEST_CASES	30
+
+// FIXME make these public somewhere; usbdevfs.h?
+
+struct usbtest_param {
+	// inputs
+	unsigned		test_num;	/* 0..(TEST_CASES-1) */
+	unsigned		iterations;
+	unsigned		length;
+	unsigned		vary;
+	unsigned		sglen;
+
+	// outputs
+	struct timeval		duration;
+};
+#define USBTEST_REQUEST	_IOWR('U', 100, struct usbtest_param)
+
+/*-------------------------------------------------------------------------*/
+
+/* #include <linux/usb_ch9.h> */
+
+struct usb_device_descriptor {
+	__u8  bLength;
+	__u8  bDescriptorType;
+	__u16 bcdUSB;
+	__u8  bDeviceClass;
+	__u8  bDeviceSubClass;
+	__u8  bDeviceProtocol;
+	__u8  bMaxPacketSize0;
+	__u16 idVendor;
+	__u16 idProduct;
+	__u16 bcdDevice;
+	__u8  iManufacturer;
+	__u8  iProduct;
+	__u8  iSerialNumber;
+	__u8  bNumConfigurations;
+} __attribute__ ((packed));
+
+enum usb_device_speed {
+	USB_SPEED_UNKNOWN = 0,			/* enumerating */
+	USB_SPEED_LOW, USB_SPEED_FULL,		/* usb 1.1 */
+	USB_SPEED_HIGH				/* usb 2.0 */
+};
+
+/*-------------------------------------------------------------------------*/
+
+static char *speed (enum usb_device_speed s)
+{
+	switch (s) {
+	case USB_SPEED_UNKNOWN:	return "unknown";
+	case USB_SPEED_LOW:	return "low";
+	case USB_SPEED_FULL:	return "full";
+	case USB_SPEED_HIGH:	return "high";
+	default:		return "??";
+	}
+}
+
+struct testdev {
+	struct testdev		*next;
+	char			*name;
+	pthread_t		thread;
+	enum usb_device_speed	speed;
+	unsigned		ifnum : 8;
+	unsigned		forever : 1;
+	int			test;
+
+	struct usbtest_param	param;
+};
+static struct testdev		*testdevs;
+
+static int is_testdev (struct usb_device_descriptor *dev)
+{
+	/* FX2 with (tweaked) bulksrc firmware */
+	if (dev->idVendor == 0x0547 && dev->idProduct == 0x1002)
+		return 1;
+
+	/*----------------------------------------------------*/
+
+	/* devices that start up using the EZ-USB default device and
+	 * which we can use after loading simple firmware.  hotplug
+	 * can fxload it, and then run this test driver.
+	 *
+	 * we return false positives in two cases:
+	 * - the device has a "real" driver (maybe usb-serial) that
+	 *   renumerates.  the device should vanish quickly.
+	 * - the device doesn't have the test firmware installed.
+	 */
+
+	/* generic EZ-USB FX controller */
+	if (dev->idVendor == 0x0547 && dev->idProduct == 0x2235)
+		return 1;
+
+	/* generic EZ-USB FX2 controller */
+	if (dev->idVendor == 0x04b4 && dev->idProduct == 0x8613)
+		return 1;
+
+	/* CY3671 development board with EZ-USB FX */
+	if (dev->idVendor == 0x0547 && dev->idProduct == 0x0080)
+		return 1;
+
+	/* Keyspan 19Qi uses an21xx (original EZ-USB) */
+	if (dev->idVendor == 0x06cd && dev->idProduct == 0x010b)
+		return 1;
+
+	/*----------------------------------------------------*/
+
+	/* "gadget zero", Linux-USB test software */
+	if (dev->idVendor == 0x0525 && dev->idProduct == 0xa4a0)
+		return 1;
+
+	/* user mode subset of that */
+	if (dev->idVendor == 0x0525 && dev->idProduct == 0xa4a4)
+		return 1;
+
+	/* iso version of usermode code */
+	if (dev->idVendor == 0x0525 && dev->idProduct == 0xa4a3)
+		return 1;
+
+	/* some GPL'd test firmware uses these IDs */
+
+	if (dev->idVendor == 0xfff0 && dev->idProduct == 0xfff0)
+		return 1;
+
+	/*----------------------------------------------------*/
+
+	/* iBOT2 high speed webcam */
+	if (dev->idVendor == 0x0b62 && dev->idProduct == 0x0059)
+		return 1;
+
+	return 0;
+}
+
+static int find_testdev (const char *name, const struct stat *sb, int flag)
+{
+	int				fd;
+	struct usb_device_descriptor	dev;
+
+	if (flag != FTW_F)
+		return 0;
+	/* ignore /proc/bus/usb/{devices,drivers} */
+	if (strrchr (name, '/')[1] == 'd')
+		return 0;
+
+	if ((fd = open (name, O_RDONLY)) < 0) {
+		perror ("can't open dev file r/o");
+		return 0;
+	}
+	if (read (fd, &dev, sizeof dev) != sizeof dev)
+		fputs ("short devfile read!\n", stderr);
+	else if (is_testdev (&dev)) {
+		struct testdev		*entry;
+
+		if ((entry = calloc (1, sizeof *entry)) == 0) {
+			fputs ("no mem!\n", stderr);
+			goto done;
+		}
+		entry->name = strdup (name);
+		if (!entry->name) {
+			free (entry);
+			goto done;
+		}
+
+		// FIXME better to look at each interface and ask if it's
+		// bound to 'usbtest', rather than assume interface 0
+		entry->ifnum = 0;
+
+		// FIXME ask usbfs what speed; update USBDEVFS_CONNECTINFO
+		// so it tells about high speed etc
+
+		fprintf (stderr, "%s speed\t%s\n",
+				speed (entry->speed), entry->name);
+
+		entry->next = testdevs;
+		testdevs = entry;
+	}
+
+done:
+	close (fd);
+	return 0;
+}
+
+static int
+usbdev_ioctl (int fd, int ifno, unsigned request, void *param)
+{
+	struct usbdevfs_ioctl	wrapper;
+
+	wrapper.ifno = ifno;
+	wrapper.ioctl_code = request;
+	wrapper.data = param;
+
+	return ioctl (fd, USBDEVFS_IOCTL, &wrapper);
+}
+
+static void *handle_testdev (void *arg)
+{
+	struct testdev		*dev = arg;
+	int			fd, i;
+	int			status;
+
+	if ((fd = open (dev->name, O_RDWR)) < 0) {
+		perror ("can't open dev file r/w");
+		return 0;
+	}
+
+restart:
+	for (i = 0; i < TEST_CASES; i++) {
+		if (dev->test != -1 && dev->test != i)
+			continue;
+		dev->param.test_num = i;
+
+		status = usbdev_ioctl (fd, dev->ifnum,
+				USBTEST_REQUEST, &dev->param);
+		if (status < 0 && errno == EOPNOTSUPP)
+			continue;
+
+		/* FIXME need a "syslog it" option for background testing */
+
+		/* NOTE: each thread emits complete lines; no fragments! */
+		if (status < 0) {
+			char	buf [80];
+			int	err = errno;
+
+			if (strerror_r (errno, buf, sizeof buf)) {
+				snprintf (buf, sizeof buf, "error %d", err);
+				errno = err;
+			}
+			printf ("%s test %d --> %d (%s)\n",
+				dev->name, i, errno, buf);
+		} else
+			printf ("%s test %d, %4d.%.06d secs\n", dev->name, i,
+				(int) dev->param.duration.tv_sec,
+				(int) dev->param.duration.tv_usec);
+
+		fflush (stdout);
+	}
+	if (dev->forever)
+		goto restart;
+
+	close (fd);
+	return arg;
+}
+
+int main (int argc, char **argv)
+{
+	int			c;
+	struct testdev		*entry;
+	char			*device;
+	int			all = 0, forever = 0, not = 0;
+	int			test = -1 /* all */;
+	struct usbtest_param	param;
+
+	/* pick defaults that works with all speeds, without short packets.
+	 *
+	 * Best per-frame data rates:
+	 *     high speed, bulk       512 * 13 * 8 = 53248
+	 *                 interrupt 1024 *  3 * 8 = 24576
+	 *     full speed, bulk/intr   64 * 19     =  1216
+	 *                 interrupt   64 *  1     =    64
+	 *      low speed, interrupt    8 *  1     =     8
+	 */
+	param.iterations = 1000;
+	param.length = 512;
+	param.vary = 512;
+	param.sglen = 32;
+
+	/* for easy use when hotplugging */
+	device = getenv ("DEVICE");
+
+	while ((c = getopt (argc, argv, "D:ac:g:hns:t:v:")) != EOF)
+	switch (c) {
+	case 'D':	/* device, if only one */
+		device = optarg;
+		continue;
+	case 'a':	/* use all devices */
+		device = 0;
+		all = 1;
+		continue;
+	case 'c':	/* count iterations */
+		param.iterations = atoi (optarg);
+		if (param.iterations < 0)
+			goto usage;
+		continue;
+	case 'g':	/* scatter/gather entries */
+		param.sglen = atoi (optarg);
+		if (param.sglen < 0)
+			goto usage;
+		continue;
+	case 'l':	/* loop forever */
+		forever = 1;
+		continue;
+	case 'n':	/* no test running! */
+		not = 1;
+		continue;
+	case 's':	/* size of packet */
+		param.length = atoi (optarg);
+		if (param.length < 0)
+			goto usage;
+		continue;
+	case 't':	/* run just one test */
+		test = atoi (optarg);
+		if (test < 0)
+			goto usage;
+		continue;
+	case 'v':	/* vary packet size by ... */
+		param.vary = atoi (optarg);
+		if (param.vary < 0)
+			goto usage;
+		continue;
+	case '?':
+	case 'h':
+	default:
+usage:
+		fprintf (stderr, "usage: %s [-an] [-D dev]\n"
+			"\t[-c iterations]  [-t testnum]\n"
+			"\t[-s packetsize] [-g sglen] [-v vary]\n",
+			argv [0]);
+		return 1;
+	}
+	if (optind != argc)
+		goto usage;
+	if (!all && !device) {
+		fprintf (stderr, "must specify '-a' or '-D dev', "
+			"or DEVICE=/proc/bus/usb/BBB/DDD in env\n");
+		goto usage;
+	}
+
+	if ((c = open ("/proc/bus/usb/devices", O_RDONLY)) < 0) {
+		fputs ("usbfs files are missing\n", stderr);
+		return -1;
+	}
+
+	/* collect and list the test devices */
+	if (ftw ("/proc/bus/usb", find_testdev, 3) != 0) {
+		fputs ("ftw failed; is usbfs missing?\n", stderr);
+		return -1;
+	}
+
+	/* quit, run single test, or create test threads */
+	if (!testdevs && !device) {
+		fputs ("no test devices recognized\n", stderr);
+		return -1;
+	}
+	if (not)
+		return 0;
+	if (testdevs && testdevs->next == 0 && !device)
+		device = testdevs->name;
+	for (entry = testdevs; entry; entry = entry->next) {
+		int	status;
+
+		entry->param = param;
+		entry->forever = forever;
+		entry->test = test;
+
+		if (device) {
+			if (strcmp (entry->name, device))
+				continue;
+			return handle_testdev (entry) != entry;
+		}
+		status = pthread_create (&entry->thread, 0, handle_testdev, entry);
+		if (status) {
+			perror ("pthread_create");
+			continue;
+		}
+	}
+	if (device) {
+		struct testdev		dev;
+
+		/* kernel can recognize test devices we don't */
+		fprintf (stderr, "%s: %s may see only control tests\n",
+				argv [0], device);
+
+		memset (&dev, 0, sizeof dev);
+		dev.name = device;
+		dev.param = param;
+		dev.forever = forever;
+		dev.test = test;
+		return handle_testdev (&dev) != &dev;
+	}
+
+	/* wait for tests to complete */
+	for (entry = testdevs; entry; entry = entry->next) {
+		void	*retval;
+
+		if (pthread_join (entry->thread, &retval))
+			perror ("pthread_join");
+		/* testing errors discarded! */
+	}
+
+	return 0;
+}
-- 
1.7.0


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [PATCH 8/8] USB: testusb: testusb compatibility with FunctionFS gadget
  2010-04-07 13:41             ` [PATCH 7/8] USB: testusb: imported David Brownell's USB testing application Michal Nazarewicz
@ 2010-04-07 13:41               ` Michal Nazarewicz
  2010-04-08  0:30                 ` Greg KH
  2010-04-08  0:29               ` [PATCH 7/8] USB: testusb: imported David Brownell's USB testing application Greg KH
                                 ` (2 subsequent siblings)
  3 siblings, 1 reply; 47+ messages in thread
From: Michal Nazarewicz @ 2010-04-07 13:41 UTC (permalink / raw)
  To: linux-usb
  Cc: Peter Korsgaard, Rupesh Gujare, linux-kernel, David Brownell,
	Kyungmin Park, Marek Szyprowski, Michal Nazarewicz

The FunctionFS gadget may provide the source/sink interface
not as the first interface (with id == 0) but some different
interface hence a code to find the interface number is
required.

(Note that you will still configure the gadget to report
idProduct == 0xa4a4 (an "echo 0xa4a4
>/sys/module/g_ffs/parameters/usb_product" should suffice) or
configure host to handle 0x0525:0xa4ac devices using the
usbtest driver.)

Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com>
Cc: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
---
 tools/usb/testusb.c |  176 ++++++++++++++++++++++++++++++++++-----------------
 1 files changed, 119 insertions(+), 57 deletions(-)

diff --git a/tools/usb/testusb.c b/tools/usb/testusb.c
index 28e25ca..beaeea3 100644
--- a/tools/usb/testusb.c
+++ b/tools/usb/testusb.c
@@ -56,6 +56,13 @@ struct usbtest_param {
 
 /* #include <linux/usb_ch9.h> */
 
+#define USB_DT_DEVICE			0x01
+#define USB_DT_INTERFACE		0x04
+
+#define USB_CLASS_PER_INTERFACE		0	/* for DeviceClass */
+#define USB_CLASS_VENDOR_SPEC		0xff
+
+
 struct usb_device_descriptor {
 	__u8  bLength;
 	__u8  bDescriptorType;
@@ -73,6 +80,19 @@ struct usb_device_descriptor {
 	__u8  bNumConfigurations;
 } __attribute__ ((packed));
 
+struct usb_interface_descriptor {
+	__u8  bLength;
+	__u8  bDescriptorType;
+
+	__u8  bInterfaceNumber;
+	__u8  bAlternateSetting;
+	__u8  bNumEndpoints;
+	__u8  bInterfaceClass;
+	__u8  bInterfaceSubClass;
+	__u8  bInterfaceProtocol;
+	__u8  iInterface;
+} __attribute__ ((packed));
+
 enum usb_device_speed {
 	USB_SPEED_UNKNOWN = 0,			/* enumerating */
 	USB_SPEED_LOW, USB_SPEED_FULL,		/* usb 1.1 */
@@ -105,11 +125,42 @@ struct testdev {
 };
 static struct testdev		*testdevs;
 
-static int is_testdev (struct usb_device_descriptor *dev)
+static int testdev_ffs_ifnum(FILE *fd)
+{
+	union {
+		char buf[255];
+		struct usb_interface_descriptor intf;
+	} u;
+
+	for (;;) {
+		if (fread(u.buf, 1, 1, fd) != 1)
+			return -1;
+		if (fread(u.buf + 1, (unsigned char)u.buf[0] - 1, 1, fd) != 1)
+			return -1;
+
+		if (u.intf.bLength == sizeof u.intf
+		 && u.intf.bDescriptorType == USB_DT_INTERFACE
+		 && u.intf.bNumEndpoints == 2
+		 && u.intf.bInterfaceClass == USB_CLASS_VENDOR_SPEC
+		 && u.intf.bInterfaceSubClass == 0
+		 && u.intf.bInterfaceProtocol == 0)
+			return (unsigned char)u.intf.bInterfaceNumber;
+	}
+}
+
+static int testdev_ifnum(FILE *fd)
 {
+	struct usb_device_descriptor dev;
+
+	if (fread(&dev, sizeof dev, 1, fd) != 1)
+		return -1;
+
+	if (dev.bLength != sizeof dev || dev.bDescriptorType != USB_DT_DEVICE)
+		return -1;
+
 	/* FX2 with (tweaked) bulksrc firmware */
-	if (dev->idVendor == 0x0547 && dev->idProduct == 0x1002)
-		return 1;
+	if (dev.idVendor == 0x0547 && dev.idProduct == 0x1002)
+		return 0;
 
 	/*----------------------------------------------------*/
 
@@ -124,95 +175,106 @@ static int is_testdev (struct usb_device_descriptor *dev)
 	 */
 
 	/* generic EZ-USB FX controller */
-	if (dev->idVendor == 0x0547 && dev->idProduct == 0x2235)
-		return 1;
+	if (dev.idVendor == 0x0547 && dev.idProduct == 0x2235)
+		return 0;
 
 	/* generic EZ-USB FX2 controller */
-	if (dev->idVendor == 0x04b4 && dev->idProduct == 0x8613)
-		return 1;
+	if (dev.idVendor == 0x04b4 && dev.idProduct == 0x8613)
+		return 0;
 
 	/* CY3671 development board with EZ-USB FX */
-	if (dev->idVendor == 0x0547 && dev->idProduct == 0x0080)
-		return 1;
+	if (dev.idVendor == 0x0547 && dev.idProduct == 0x0080)
+		return 0;
 
 	/* Keyspan 19Qi uses an21xx (original EZ-USB) */
-	if (dev->idVendor == 0x06cd && dev->idProduct == 0x010b)
-		return 1;
+	if (dev.idVendor == 0x06cd && dev.idProduct == 0x010b)
+		return 0;
 
 	/*----------------------------------------------------*/
 
 	/* "gadget zero", Linux-USB test software */
-	if (dev->idVendor == 0x0525 && dev->idProduct == 0xa4a0)
-		return 1;
+	if (dev.idVendor == 0x0525 && dev.idProduct == 0xa4a0)
+		return 0;
 
 	/* user mode subset of that */
-	if (dev->idVendor == 0x0525 && dev->idProduct == 0xa4a4)
-		return 1;
+	if (dev.idVendor == 0x0525 && dev.idProduct == 0xa4a4)
+		return testdev_ffs_ifnum(fd);
+		/* return 0; */
 
 	/* iso version of usermode code */
-	if (dev->idVendor == 0x0525 && dev->idProduct == 0xa4a3)
-		return 1;
+	if (dev.idVendor == 0x0525 && dev.idProduct == 0xa4a3)
+		return 0;
 
 	/* some GPL'd test firmware uses these IDs */
 
-	if (dev->idVendor == 0xfff0 && dev->idProduct == 0xfff0)
-		return 1;
+	if (dev.idVendor == 0xfff0 && dev.idProduct == 0xfff0)
+		return 0;
 
 	/*----------------------------------------------------*/
 
 	/* iBOT2 high speed webcam */
-	if (dev->idVendor == 0x0b62 && dev->idProduct == 0x0059)
-		return 1;
+	if (dev.idVendor == 0x0b62 && dev.idProduct == 0x0059)
+		return 0;
 
-	return 0;
+	/*----------------------------------------------------*/
+
+	/* the FunctionFS gadget can have the source/sink interface
+	 * anywhere.  We look for an interface descriptor that match
+	 * what we expect.  We ignore configuratiens thou. */
+
+	if (dev.idVendor == 0x0525 && dev.idProduct == 0xa4ac
+	 && (dev.bDeviceClass == USB_CLASS_PER_INTERFACE
+	  || dev.bDeviceClass == USB_CLASS_VENDOR_SPEC))
+		return testdev_ffs_ifnum(fd);
+
+	return -1;
 }
 
-static int find_testdev (const char *name, const struct stat *sb, int flag)
+static int find_testdev(const char *name, const struct stat *sb, int flag)
 {
-	int				fd;
-	struct usb_device_descriptor	dev;
+	FILE				*fd;
+	int				ifnum;
+	struct testdev			*entry;
 
 	if (flag != FTW_F)
 		return 0;
 	/* ignore /proc/bus/usb/{devices,drivers} */
-	if (strrchr (name, '/')[1] == 'd')
+	if (strrchr(name, '/')[1] == 'd')
 		return 0;
 
-	if ((fd = open (name, O_RDONLY)) < 0) {
-		perror ("can't open dev file r/o");
+	fd = fopen(name, "rb");
+	if (!fd) {
+		perror(name);
 		return 0;
 	}
-	if (read (fd, &dev, sizeof dev) != sizeof dev)
-		fputs ("short devfile read!\n", stderr);
-	else if (is_testdev (&dev)) {
-		struct testdev		*entry;
-
-		if ((entry = calloc (1, sizeof *entry)) == 0) {
-			fputs ("no mem!\n", stderr);
-			goto done;
-		}
-		entry->name = strdup (name);
-		if (!entry->name) {
-			free (entry);
-			goto done;
-		}
-
-		// FIXME better to look at each interface and ask if it's
-		// bound to 'usbtest', rather than assume interface 0
-		entry->ifnum = 0;
 
-		// FIXME ask usbfs what speed; update USBDEVFS_CONNECTINFO
-		// so it tells about high speed etc
+	ifnum = testdev_ifnum(fd);
+	fclose(fd);
+	if (ifnum < 0)
+		return 0;
 
-		fprintf (stderr, "%s speed\t%s\n",
-				speed (entry->speed), entry->name);
+	entry = calloc(1, sizeof *entry);
+	if (!entry)
+		goto nomem;
 
-		entry->next = testdevs;
-		testdevs = entry;
+	entry->name = strdup(name);
+	if (!entry->name) {
+		free(entry);
+nomem:
+		perror("malloc");
+		return 0;
 	}
 
-done:
-	close (fd);
+	entry->ifnum = ifnum;
+
+	/* FIXME ask usbfs what speed; update USBDEVFS_CONNECTINFO so
+	 * it tells about high speed etc */
+
+	fprintf(stderr, "%s speed\t%s\t%u\n",
+		speed(entry->speed), entry->name, entry->ifnum);
+
+	entry->next = testdevs;
+	testdevs = entry;
 	return 0;
 }
 
-- 
1.7.0


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* Re: [PATCH 1/8] USB: composite: allow optional removal of __init and __exit tags
  2010-04-07 13:41 ` [PATCH 1/8] USB: composite: allow optional removal of __init and __exit tags Michal Nazarewicz
  2010-04-07 13:41   ` [PATCH 2/8] sched: __wake_up_locked() exported Michal Nazarewicz
@ 2010-04-07 15:28   ` Greg KH
  2010-04-07 15:39     ` Michał Nazarewicz
  1 sibling, 1 reply; 47+ messages in thread
From: Greg KH @ 2010-04-07 15:28 UTC (permalink / raw)
  To: Michal Nazarewicz
  Cc: linux-usb, Peter Korsgaard, Rupesh Gujare, linux-kernel,
	David Brownell, Kyungmin Park, Marek Szyprowski

On Wed, Apr 07, 2010 at 03:41:28PM +0200, Michal Nazarewicz wrote:
> The composite framework has been written using __init and __exit tags
> to mark init and exit functions as such.  This works with most of the
> composite gadgets however some may need to call init/exit functions
> during normal operations.  One example is mass storage gadget which
> needs to call exit functions.
> 
> This patch allows gadgets to define USB_NO_INIT_SEGMENT or
> USB_NO_EXIT_SEGMENT to remove the __init and __exit declarations
> from composite framework.

Ick ick ick.

How about we just drop the __init and __exit tags completly and then
we don't have to propagate this mess any further?

How much memory do they really save anyway?

I don't like this at all...

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 2/8] sched: __wake_up_locked() exported
  2010-04-07 13:41   ` [PATCH 2/8] sched: __wake_up_locked() exported Michal Nazarewicz
  2010-04-07 13:41     ` [PATCH 3/8] USB: f_fs: the FunctionFS driver Michal Nazarewicz
@ 2010-04-07 15:29     ` Greg KH
  2010-04-07 17:11       ` Michał Nazarewicz
  1 sibling, 1 reply; 47+ messages in thread
From: Greg KH @ 2010-04-07 15:29 UTC (permalink / raw)
  To: Michal Nazarewicz
  Cc: linux-usb, Peter Korsgaard, Rupesh Gujare, linux-kernel,
	David Brownell, Kyungmin Park, Marek Szyprowski

On Wed, Apr 07, 2010 at 03:41:29PM +0200, Michal Nazarewicz wrote:
> The __wake_up_locked() function has been exported in case modules need it.

What module needs it?

Why is it needed?

Why is it needed now, and was not previously?

Are you sure that you really need it?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 1/8] USB: composite: allow optional removal of __init and __exit tags
  2010-04-07 15:28   ` [PATCH 1/8] USB: composite: allow optional removal of __init and __exit tags Greg KH
@ 2010-04-07 15:39     ` Michał Nazarewicz
  2010-04-08  0:26       ` Greg KH
  0 siblings, 1 reply; 47+ messages in thread
From: Michał Nazarewicz @ 2010-04-07 15:39 UTC (permalink / raw)
  To: Greg KH
  Cc: linux-usb, Peter Korsgaard, Rupesh Gujare, linux-kernel,
	David Brownell, Kyungmin Park, Marek Szyprowski

> On Wed, Apr 07, 2010 at 03:41:28PM +0200, Michal Nazarewicz wrote:
>> The composite framework has been written using __init and __exit tags
>> to mark init and exit functions as such.  This works with most of the
>> composite gadgets however some may need to call init/exit functions
>> during normal operations.  One example is mass storage gadget which
>> needs to call exit functions.
>>
>> This patch allows gadgets to define USB_NO_INIT_SEGMENT or
>> USB_NO_EXIT_SEGMENT to remove the __init and __exit declarations
>> from composite framework.

On Wed, 07 Apr 2010 17:28:52 +0200, Greg KH <greg@kroah.com> wrote:
> How about we just drop the __init and __exit tags completly and then
> we don't have to propagate this mess any further?

Personally, I don't care, ;) so if you think it's better to drop it all
together I can change it in a future patch.

-- 
Best regards,                                           _     _
  .o. | Liege of Serenely Enlightened Majesty of       o' \,=./ `o
  ..o | Computer Science,  Michał "mina86" Nazarewicz     (o o)
  ooo +---[mina86@mina86.com]---[mina86@jabber.org]---ooO--(_)--Ooo--

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 2/8] sched: __wake_up_locked() exported
  2010-04-07 15:29     ` [PATCH 2/8] sched: __wake_up_locked() exported Greg KH
@ 2010-04-07 17:11       ` Michał Nazarewicz
  2010-04-08  0:28         ` Greg KH
  0 siblings, 1 reply; 47+ messages in thread
From: Michał Nazarewicz @ 2010-04-07 17:11 UTC (permalink / raw)
  To: Greg KH
  Cc: linux-usb, Peter Korsgaard, Rupesh Gujare, linux-kernel,
	David Brownell, Kyungmin Park, Marek Szyprowski

> On Wed, Apr 07, 2010 at 03:41:29PM +0200, Michal Nazarewicz wrote:
>> The __wake_up_locked() function has been exported in case modules need it.

On Wed, 07 Apr 2010 17:29:22 +0200, Greg KH <greg@kroah.com> wrote:
> What module needs it?

FunctionFS (f_fs) uses it (thus FunctionFS Gadget (g_ffs) uses it).

> Why is it needed?

The FunctionFS uses wait_queue_head_t's spinlock to protect a data
structure (a queue) used by FunctionFS.

In an earlier version of the code there was another spinlock for
the queue alone thus when waiting for an event the following had
to be used:

#v+
spin_lock(&queue.lock);
while (queue.empty) {
	spin_unlock(&queue.lock);
	wait_event(&queue.wait_queue, !queue.empty);
	spin_lock(&queue.lock);
}
...
spin_unlock(&queue.lock);
#v-

I disliked this code very much and at first hoped that there's some
"wait_event_holding_lock()" macro which would define the loop shown
above (similar to user-space condition variables; see
pthread_cond_wait(3) <http://linux.die.net/man/3/pthread_cond_wait>).

What makes matter worse is that wait_event() calls prepare_to_wait()
which locks the wait_queue_head_t's spinlock so in the end we have
unlock one spinlock prior to locking another in situation where one
spinlock would suffice.

In searching for a better solution I stumbled across fs/timerfd.c which
used the wait_queue_head_t's spinlock to lock it's own structures:

* http://lxr.linux.no/#linux+v2.6.33/fs/timerfd.c#L39
* http://lxr.linux.no/#linux+v2.6.33/fs/timerfd.c#L106

So, in the end, I decided to use the same approach in FunctionFS and
hence by using wait_queue_head_t's spinlock I removed one (unneeded)
spinlock and decreased number of spin_lock and spin_unlock operations,
ie. to something like (simplified code):

#v+
spin_lock(&queue.wait_queue.lock);
my_wait_event_locked(&queue.wait_queue, !queue.empty);
...
spin_unlock(&queue.lock);
#v-

where my_wait_event_locked() is a function that does what wait_event()
does but assumes the wait_queue_head_t's lock is held when entering
and leaves it held when exiting.

> Are you sure that you really need it?

I could live without it but I strongly believe the code is somehow
cleaner and more optimised when __wake_up_locked() is used.


To be more concrete I attach the actual code (parts not important in current
discussion have been stripped).  The most important parts are
__ffs_ep0_read_wait_for_events() function, the FFS_NO_SETUP case in
ffs_ep0_read() and ffs_event_add().

#v+
/****************************** Waiting ******************************/
static int __ffs_ep0_read_wait_for_events(struct ffs_data *ffs)
{
	/* We are holding ffs->ev.waitq.lock */
	DEFINE_WAIT(wait);
	int ret = 0;

	wait.flags |= WQ_FLAG_EXCLUSIVE;
	__add_wait_queue_tail(&ffs->ev.waitq, &wait);

	do {
		set_current_state(TASK_INTERRUPTIBLE);
		if (signal_pending(current)) {
			ret = -ERESTARTSYS;
			break;
		}

		spin_unlock_irq(&ffs->ev.waitq.lock);
		schedule();
		spin_lock_irq(&ffs->ev.waitq.lock);
	} while (!ffs->ev.count);

	__remove_wait_queue(&ffs->ev.waitq, &wait);
	__set_current_state(TASK_RUNNING);
	return ret;
}

/****************************** Popping ******************************/
static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
				     size_t n)
{
	/* We are holding ffs->ev.waitq.lock and ffs->mutex and we need
	 * to release them. */

	struct usb_functionfs_event events[n];
	unsigned i = 0;
	do {
		events[i].type = ffs->ev.types[i];
		/* ... */
	} while (++i < n);
	ffs->ev.count = 0;

	spin_unlock_irq(&ffs->ev.waitq.lock);
	mutex_unlock(&ffs->mutex);
	return unlikely(__copy_to_user(buf, events, sizeof events))
		? -EFAULT : sizeof events;
}

/****************************** Entry from user space ******************************/
static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
			    size_t len, loff_t *ptr)
{
	struct ffs_data *ffs = file->private_data;
	size_t n;
	char *data;
	int ret;

	/* Acquire mutex */
	ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
	if (unlikely(ret < 0))
		return ret;

	spin_lock_irq(&ffs->ev.waitq.lock);
	switch (FFS_SETUP_STATE(ffs)) {
	case FFS_SETUP_CANCELED:
		ret = -EIDRM;
		break;

	case FFS_NO_SETUP:
		n = len / sizeof(struct usb_functionfs_event);
		if (unlikely(!n)) {
			ret = -EINVAL;
			break;
		}

		if ((file->f_flags & O_NONBLOCK) && !ffs->ev.count) {
			ret = -EAGAIN;
			break;
		}

		if (!ffs->ev.count &&
		    unlikely(__ffs_ep0_read_wait_for_events(ffs))) {
			ret = -EINTR;
			break;
		}

		return __ffs_ep0_read_events(ffs, buf,
					     min(n, (size_t)ffs->ev.count));


	case FFS_SETUP_PENDING:
		if (ffs->ev.setup.bRequestType & USB_DIR_IN) {
			ret = __ffs_ep0_stall(ffs);
			break;
		}

		len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength));

		spin_unlock_irq(&ffs->ev.waitq.lock);

		data = ffs_prepare_buffer(NULL, len); /* basically kmalloc() */
		if (unlikely(IS_ERR(data))) {
			ret = PTR_ERR(data);
			goto done_mutex;
		}

		spin_lock_irq(&ffs->ev.waitq.lock);

		/* the state of setup could have changed from
		 * FFS_SETUP_PENDING to FFS_SETUP_CANCELED so we need
		 * to check for that. */
		if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) {
			kfree(data);
			ret = -EIDRM;
			break;
		}

		/* unlocks spinlock */
		ret = __ffs_ep0_queue_wait(ffs, data, len);
		if (likely(ret > 0) && unlikely(__copy_to_user(buf, data, len)))
			ret = -EFAULT;
		kfree(data);
		goto done_mutex;
	}
	spin_unlock_irq(&ffs->ev.waitq.lock);
done_mutex:
	mutex_unlock(&ffs->mutex);
	return ret;
}

/****************************** Adding ******************************/
static void __ffs_event_add(struct ffs_data *ffs,
			    enum usb_functionfs_event_type type)
{
	/* Abort any unhandled setup */
	if (ffs->setup_state == FFS_SETUP_PENDING)
		ffs->setup_state = FFS_SETUP_CANCELED;

         /* ... some events may get nuked here */

	ffs->ev.types[ffs->ev.count++] = type;
	wake_up_locked(&ffs->ev.waitq);
}

static void ffs_event_add(struct ffs_data *ffs,
			  enum usb_functionfs_event_type type)
{
	unsigned long flags;
	spin_lock_irqsave(&ffs->ev.waitq.lock, flags);
	__ffs_event_add(ffs, type);
	spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags);
}
#v-

-- 
Best regards,                                           _     _
  .o. | Liege of Serenely Enlightened Majesty of       o' \,=./ `o
  ..o | Computer Science,  Michał "mina86" Nazarewicz     (o o)
  ooo +---[mina86@mina86.com]---[mina86@jabber.org]---ooO--(_)--Ooo--

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 3/8] USB: f_fs: the FunctionFS driver
  2010-04-07 13:41     ` [PATCH 3/8] USB: f_fs: the FunctionFS driver Michal Nazarewicz
  2010-04-07 13:41       ` [PATCH 4/8] USB: Ethernet: allow optional removal of __init and __init_data tags Michal Nazarewicz
@ 2010-04-07 17:11       ` Michał Nazarewicz
  1 sibling, 0 replies; 47+ messages in thread
From: Michał Nazarewicz @ 2010-04-07 17:11 UTC (permalink / raw)
  To: Michal Nazarewicz, linux-usb
  Cc: Peter Korsgaard, Rupesh Gujare, linux-kernel, David Brownell,
	Kyungmin Park, Marek Szyprowski

On Wed, 07 Apr 2010 15:41:30 +0200, Michal Nazarewicz <m.nazarewicz@samsung.com> wrote:
> +static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
> +				     size_t n)
> +{
> +	/* We are holding ffs->ev.waitq.lock and ffs->mutex and we need
> +	 * to release them. */
> +
> +	struct usb_functionfs_event events[n];
> +	unsigned i = 0;
> +
> +	memset(events, 0, sizeof events);
> +
> +	do {
> +		events[i].type = ffs->ev.types[i];
> +		if (events[i].type == FUNCTIONFS_SETUP) {
> +			events[i].u.setup = ffs->ev.setup;
> +			ffs->setup_state = FFS_SETUP_PENDING;
> +		}
> +	} while (++i < n);
> +
> +	ffs->ev.count = 0;

I've just noticed.  This line should instead read:

+	if (n < ffs->ev.count) {
+		ffs->ev.count -= n;
+		memmove(ffs->ev.types, ffs->ev.types + n,
+			ffs->ev.count * sizeof *ffs->ev.types);
+	} else {
+		ffs->ev.count = 0;
+	}

This is a minor bug which should be rather hard to observe (and only
if user-space reads() less then 4 * sizeof(struct usb_functionfs_event)
bytes when reading from "ep0") so posted code is good for testing.
(I'll post a fixed version later on with possibly other fixes and
changes.)

> +
> +	spin_unlock_irq(&ffs->ev.waitq.lock);
> +	mutex_unlock(&ffs->mutex);
> +
> +	return unlikely(__copy_to_user(buf, events, sizeof events))
> +		? -EFAULT : sizeof events;
> +}

-- 
Best regards,                                           _     _
  .o. | Liege of Serenely Enlightened Majesty of       o' \,=./ `o
  ..o | Computer Science,  Michał "mina86" Nazarewicz     (o o)
  ooo +---[mina86@mina86.com]---[mina86@jabber.org]---ooO--(_)--Ooo--

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 1/8] USB: composite: allow optional removal of __init and __exit tags
  2010-04-07 15:39     ` Michał Nazarewicz
@ 2010-04-08  0:26       ` Greg KH
  0 siblings, 0 replies; 47+ messages in thread
From: Greg KH @ 2010-04-08  0:26 UTC (permalink / raw)
  To: Michał Nazarewicz
  Cc: linux-usb, Peter Korsgaard, Rupesh Gujare, linux-kernel,
	David Brownell, Kyungmin Park, Marek Szyprowski

On Wed, Apr 07, 2010 at 05:39:35PM +0200, Michał Nazarewicz wrote:
> >On Wed, Apr 07, 2010 at 03:41:28PM +0200, Michal Nazarewicz wrote:
> >>The composite framework has been written using __init and __exit tags
> >>to mark init and exit functions as such.  This works with most of the
> >>composite gadgets however some may need to call init/exit functions
> >>during normal operations.  One example is mass storage gadget which
> >>needs to call exit functions.
> >>
> >>This patch allows gadgets to define USB_NO_INIT_SEGMENT or
> >>USB_NO_EXIT_SEGMENT to remove the __init and __exit declarations
> >>from composite framework.
> 
> On Wed, 07 Apr 2010 17:28:52 +0200, Greg KH <greg@kroah.com> wrote:
> >How about we just drop the __init and __exit tags completly and then
> >we don't have to propagate this mess any further?
> 
> Personally, I don't care, ;) so if you think it's better to drop it all
> together I can change it in a future patch.

That would be great.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 2/8] sched: __wake_up_locked() exported
  2010-04-07 17:11       ` Michał Nazarewicz
@ 2010-04-08  0:28         ` Greg KH
  0 siblings, 0 replies; 47+ messages in thread
From: Greg KH @ 2010-04-08  0:28 UTC (permalink / raw)
  To: Michał Nazarewicz
  Cc: linux-usb, Peter Korsgaard, Rupesh Gujare, linux-kernel,
	David Brownell, Kyungmin Park, Marek Szyprowski

On Wed, Apr 07, 2010 at 07:11:05PM +0200, Michał Nazarewicz wrote:
> >On Wed, Apr 07, 2010 at 03:41:29PM +0200, Michal Nazarewicz wrote:
> >>The __wake_up_locked() function has been exported in case modules need it.
> 
> On Wed, 07 Apr 2010 17:29:22 +0200, Greg KH <greg@kroah.com> wrote:
> >What module needs it?
> 
> FunctionFS (f_fs) uses it (thus FunctionFS Gadget (g_ffs) uses it).
> 
> >Why is it needed?
> 
> The FunctionFS uses wait_queue_head_t's spinlock to protect a data
> structure (a queue) used by FunctionFS.
> 
> In an earlier version of the code there was another spinlock for
> the queue alone thus when waiting for an event the following had
> to be used:
> 
> #v+
> spin_lock(&queue.lock);
> while (queue.empty) {
> 	spin_unlock(&queue.lock);
> 	wait_event(&queue.wait_queue, !queue.empty);
> 	spin_lock(&queue.lock);
> }
> ...
> spin_unlock(&queue.lock);
> #v-
> 
> I disliked this code very much and at first hoped that there's some
> "wait_event_holding_lock()" macro which would define the loop shown
> above (similar to user-space condition variables; see
> pthread_cond_wait(3) <http://linux.die.net/man/3/pthread_cond_wait>).
> 
> What makes matter worse is that wait_event() calls prepare_to_wait()
> which locks the wait_queue_head_t's spinlock so in the end we have
> unlock one spinlock prior to locking another in situation where one
> spinlock would suffice.
> 
> In searching for a better solution I stumbled across fs/timerfd.c which
> used the wait_queue_head_t's spinlock to lock it's own structures:
> 
> * http://lxr.linux.no/#linux+v2.6.33/fs/timerfd.c#L39
> * http://lxr.linux.no/#linux+v2.6.33/fs/timerfd.c#L106
> 
> So, in the end, I decided to use the same approach in FunctionFS and
> hence by using wait_queue_head_t's spinlock I removed one (unneeded)
> spinlock and decreased number of spin_lock and spin_unlock operations,
> ie. to something like (simplified code):
> 
> #v+
> spin_lock(&queue.wait_queue.lock);
> my_wait_event_locked(&queue.wait_queue, !queue.empty);
> ...
> spin_unlock(&queue.lock);
> #v-
> 
> where my_wait_event_locked() is a function that does what wait_event()
> does but assumes the wait_queue_head_t's lock is held when entering
> and leaves it held when exiting.
> 
> >Are you sure that you really need it?
> 
> I could live without it but I strongly believe the code is somehow
> cleaner and more optimised when __wake_up_locked() is used.

Ok, thanks for the detailed description, that makes more sense.

Perhaps you should put this into the patch description next time :)

Oh, and maybe we should move this type of functionality into the core
kernel so that the two users don't have to open-code it both times?  If
there are 2 users, odds are someone else will want to also do the same
thing in the near future.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 7/8] USB: testusb: imported David Brownell's USB testing application
  2010-04-07 13:41             ` [PATCH 7/8] USB: testusb: imported David Brownell's USB testing application Michal Nazarewicz
  2010-04-07 13:41               ` [PATCH 8/8] USB: testusb: testusb compatibility with FunctionFS gadget Michal Nazarewicz
@ 2010-04-08  0:29               ` Greg KH
  2010-04-09 19:21                 ` [PATCHv2 7/8] USB: testusb: an " Michal Nazarewicz
  2010-04-14  9:30                 ` [PATCH 7/8] USB: testusb: imported David Brownell's " David Brownell
  2010-04-08  6:10               ` Heikki Krogerus
  2010-04-14  9:41               ` David Brownell
  3 siblings, 2 replies; 47+ messages in thread
From: Greg KH @ 2010-04-08  0:29 UTC (permalink / raw)
  To: Michal Nazarewicz
  Cc: linux-usb, Peter Korsgaard, Rupesh Gujare, linux-kernel,
	David Brownell, Kyungmin Park, Marek Szyprowski

On Wed, Apr 07, 2010 at 03:41:34PM +0200, Michal Nazarewicz wrote:
> The testusb application can be used to test various USB gadget
> that implment the source/sink intrerface.
> 
> Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com>
> Cc: Kyungmin Park <kyungmin.park@samsung.com>
> Cc: Marek Szyprowski <m.szyprowski@samsung.com>

You should put a:
	From: David Brownell <dbrownell@users.sourceforge.net>
as the first line of the body of this patch, so it properly shows up as
David's code.

I also would like to get an ack from David that he doesn't mind his code
moving into the kernel here (personally I think it's a good thing to be
here.)

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 8/8] USB: testusb: testusb compatibility with FunctionFS gadget
  2010-04-07 13:41               ` [PATCH 8/8] USB: testusb: testusb compatibility with FunctionFS gadget Michal Nazarewicz
@ 2010-04-08  0:30                 ` Greg KH
  0 siblings, 0 replies; 47+ messages in thread
From: Greg KH @ 2010-04-08  0:30 UTC (permalink / raw)
  To: Michal Nazarewicz
  Cc: linux-usb, Peter Korsgaard, Rupesh Gujare, linux-kernel,
	David Brownell, Kyungmin Park, Marek Szyprowski

On Wed, Apr 07, 2010 at 03:41:35PM +0200, Michal Nazarewicz wrote:
> The FunctionFS gadget may provide the source/sink interface
> not as the first interface (with id == 0) but some different
> interface hence a code to find the interface number is
> required.
> 
> (Note that you will still configure the gadget to report
> idProduct == 0xa4a4 (an "echo 0xa4a4
> >/sys/module/g_ffs/parameters/usb_product" should suffice) or
> configure host to handle 0x0525:0xa4ac devices using the
> usbtest driver.)
> 
> Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com>
> Cc: Kyungmin Park <kyungmin.park@samsung.com>
> Cc: Marek Szyprowski <m.szyprowski@samsung.com>

Ah, kernel code with a userspace test program.  If only all patches came
with stuff like this.

Very nice job, I like it a lot.  Care to take the minor comments so far,
and redo it?  I'll be glad to take the patches into my tree then.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 7/8] USB: testusb: imported David Brownell's USB testing application
  2010-04-07 13:41             ` [PATCH 7/8] USB: testusb: imported David Brownell's USB testing application Michal Nazarewicz
  2010-04-07 13:41               ` [PATCH 8/8] USB: testusb: testusb compatibility with FunctionFS gadget Michal Nazarewicz
  2010-04-08  0:29               ` [PATCH 7/8] USB: testusb: imported David Brownell's USB testing application Greg KH
@ 2010-04-08  6:10               ` Heikki Krogerus
  2010-04-08  6:18                 ` Greg KH
  2010-04-14  9:41               ` David Brownell
  3 siblings, 1 reply; 47+ messages in thread
From: Heikki Krogerus @ 2010-04-08  6:10 UTC (permalink / raw)
  To: ext Michal Nazarewicz
  Cc: linux-usb, Peter Korsgaard, Rupesh Gujare, linux-kernel,
	David Brownell, Kyungmin Park, Marek Szyprowski

Hi,

If this is going into kernel then..

On Wed, Apr 07, 2010 at 03:41:34PM +0200, ext Michal Nazarewicz wrote:

...

> +       if ((c = open ("/proc/bus/usb/devices", O_RDONLY)) < 0) {
> +               fputs ("usbfs files are missing\n", stderr);
> +               return -1;
> +       }

Is this needed?

> +
> +       /* collect and list the test devices */
> +       if (ftw ("/proc/bus/usb", find_testdev, 3) != 0) {

How about "/dev/bus/usb" instead? Some distributions do not enable
sysfs in their default kernels anymore.

> +               fputs ("ftw failed; is usbfs missing?\n", stderr);
> +               return -1;
> +       }

br,

-- 
heikki

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 7/8] USB: testusb: imported David Brownell's USB testing application
  2010-04-08  6:10               ` Heikki Krogerus
@ 2010-04-08  6:18                 ` Greg KH
  2010-04-08  6:34                   ` Heikki Krogerus
  0 siblings, 1 reply; 47+ messages in thread
From: Greg KH @ 2010-04-08  6:18 UTC (permalink / raw)
  To: Heikki Krogerus
  Cc: ext Michal Nazarewicz, linux-usb, Peter Korsgaard, Rupesh Gujare,
	linux-kernel, David Brownell, Kyungmin Park, Marek Szyprowski

On Thu, Apr 08, 2010 at 09:10:00AM +0300, Heikki Krogerus wrote:
> Hi,
> 
> If this is going into kernel then..
> 
> On Wed, Apr 07, 2010 at 03:41:34PM +0200, ext Michal Nazarewicz wrote:
> 
> ...
> 
> > +       if ((c = open ("/proc/bus/usb/devices", O_RDONLY)) < 0) {
> > +               fputs ("usbfs files are missing\n", stderr);
> > +               return -1;
> > +       }
> 
> Is this needed?
> 
> > +
> > +       /* collect and list the test devices */
> > +       if (ftw ("/proc/bus/usb", find_testdev, 3) != 0) {
> 
> How about "/dev/bus/usb" instead? Some distributions do not enable
> sysfs in their default kernels anymore.

You mean 'usbfs' not 'sysfs', right?

And yes, this probably should be fixed, patches are always welcome :)

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 7/8] USB: testusb: imported David Brownell's USB testing application
  2010-04-08  6:18                 ` Greg KH
@ 2010-04-08  6:34                   ` Heikki Krogerus
  0 siblings, 0 replies; 47+ messages in thread
From: Heikki Krogerus @ 2010-04-08  6:34 UTC (permalink / raw)
  To: ext Greg KH
  Cc: ext Michal Nazarewicz, linux-usb, Peter Korsgaard, Rupesh Gujare,
	linux-kernel, David Brownell, Kyungmin Park, Marek Szyprowski

On Thu, Apr 08, 2010 at 08:18:46AM +0200, ext Greg KH wrote:
> On Thu, Apr 08, 2010 at 09:10:00AM +0300, Heikki Krogerus wrote:
> > Hi,
> > 
> > If this is going into kernel then..
> > 
> > On Wed, Apr 07, 2010 at 03:41:34PM +0200, ext Michal Nazarewicz wrote:
> > 
> > ...
> > 
> > > +       if ((c = open ("/proc/bus/usb/devices", O_RDONLY)) < 0) {
> > > +               fputs ("usbfs files are missing\n", stderr);
> > > +               return -1;
> > > +       }
> > 
> > Is this needed?
> > 
> > > +
> > > +       /* collect and list the test devices */
> > > +       if (ftw ("/proc/bus/usb", find_testdev, 3) != 0) {
> > 
> > How about "/dev/bus/usb" instead? Some distributions do not enable
> > sysfs in their default kernels anymore.
> 
> You mean 'usbfs' not 'sysfs', right?

Yes, usbfs of cource :)

-- 
heikki

^ permalink raw reply	[flat|nested] 47+ messages in thread

* [PATCH 0/7] The FunctionFS composite function
@ 2010-04-09 19:21 Michal Nazarewicz
  2010-04-09 19:21 ` [PATCHv2 1/8] wait_event_interruptible_locked() interface Michal Nazarewicz
  0 siblings, 1 reply; 47+ messages in thread
From: Michal Nazarewicz @ 2010-04-09 19:21 UTC (permalink / raw)
  To: linux-usb
  Cc: Michal Nazarewicz, Michal Nazarewicz, Davide Libenzi, Greg KH,
	Kyungmin Park, Marek Szyprowski, Thomas Gleixner, linux-fsdevel,
	linux-kernel

Hello everyone,


*Changes from Previous Version*

This is the second version of the FunctionFS patchset.

1. A new wait_event_interruptible_locked() macro (in 6 variants) has
   been added and used in the f_fs.c instead of an open-coding it.
   This, again, was suggested by Greg.

   As a side effect of the above, also a patch for fs/timerfd.c is
   provided that uses the new facility (hence I'm ccing it to fsdevel
   and timerfd folks as well).

2. The __init/__exit tags from various USB related files have been
   removed instead of using __usb_* tags that can be switched.  This
   was requested by Greg.

3. A bug in f_fs.c in has been fixed (the one I've spotted and
   announced earlier on the list).  Also there were some other tiny
   changes to f_fs.c.

4. The testusb.c now looks for usbfs in both /dev/bus/usb and
   /proc/bus/usb.  It was suggested by Heikki Krogerus that some
   distributions don't provide usbfs in /proc.  Also -A argument has
   been added to let user supply the path.

Chosen differences as a diff are included at the end of the mail.


*Prologue*

Patches that follow implement the FunctionFS composite function that
is a conversion of GadgetFS to use the composite framework.  Possible
uses are:

1. what Peter Korsgaard has described[1]:

> I could certainly imagine doing something like that, E.G. reuse the
> existing g_serial functionality while implementing E.G. a HID device
> though gadgetfs.

2. or what Rupesh Gujare has described[2]:

> In my requirement, I want to have Media Transfer Protocol (MTP)
> (which uses user level usb driver (usb.c) & gadgetfs) along with
> modem functionality(f_acm.c).
>
> Currently either of them ie. f_acm (as part of composite driver
> along with ums) or gadgetfs (as independent module) can be loaded,
> which allows only one active USB function at a time.
>
> If gadgetfs can be made as a composite function then I can have both
> functionality at same time.


*The Patches*

The first patch creates a new wait_event_interruptible_locked()
interface for waiting on events.  While holding the wait queue's lock.
Refer to the commit message for usage.

The second patch makes use of the new interface in fs/timerfd.c file
replacing some 20 lines by a single macro call.

Next three patches implement the FunctionFS and a composite gadget
that uses FunctionFS and Ethernet function (with the later function
optionally disabled via Kconfig).

The last three patches provide a testing code for the FunctionFS.
My original intend was to provide those just for anyone who'd like to
test the code and not to include them in the Linux source tree but if
that seems appropriate then why not?


*How FunctionFS works*

(Copied from second patch commit message.)

>From kernel point of view it is just a composite function with some
unique behaviour.  It may be added to an USB configuration only after
the user space driver has registered by writing descriptors and
strings (the user space program has to provide the same information
that kernel level composite functions provide when they are added to
the configuration).

This in particular means that the composite initialisation functions
may not be in init section (ie. may not use the __init tag) hence the
first and fourth patch in the series.


>From user space point of view it is a file system which when
mounted provide an "ep0" file.  User space driver need to
write descriptors and strings to that file.  It does not need
to worry about endpoints, interfaces or strings numbers but
simply provide descriptors such as if the function was the
only one (endpoints and strings numbers starting from one and
interface numbers starting from core).  The FunctionFS changes
numbers of those as needed also handling situation when
numbers differ in different configurations.

When descriptors and strings are written "ep#" files appear
(one for each declared endpoint) which handle communication on
a single endpoint.  Again, FunctionFS takes care of the real
numbers and changing of the configuration (which means that
"ep1" file may be really mapped to (say) endpoint 3 (and when
configuration changes to (say) endpoint 2)).  "ep0" is used
for receiving events and handling setup requests.

When all files are closed the function disables itself.


*Testing*

The fifth patch implement a simple source/sink FunctionFS driver based
on similar driver for GadgetFS by David Brownell[3].  It registers
a dual-speed function with a single IN and single OUT endpoints.

The sixth and seventh patch provide a host-side testing code.  This is
what David Brownell has created a while back[4] with a simple fix to
make the tool detect the number of our source/sink interface.

Still, you will need to configure the gadget to report idProduct ==
0xa4a4 (an "echo 0xa4a4 >/sys/module/g_ffs/parameters/usb_product"
should suffice) or configure host to handle 0x0525:0xa4ac devices
using the usbtest driver.

Hence, the simplest way to run the test is to do the following:

* On target (machine that runs has the gadget) as root:
  $ echo 0xa4a4 >/sys/module/g_ffs/parameters/usb_product &&
  $ mkdir /dev/ffs &&
  $ mount -t functionfs ffs /dev/ffs &&
  $ cd /dev/ffs &&
  $ /path/to/ffs-test
* On host (as root):
  $ testusb -a

At this point I have to admit that communication on EP0 has not yet
been tested, so beware of bugs there.


*Request for Comments and Future Work*

Regarding presented version there are two aspects I'd like to discuss.

1. First of all, the current code uses similar approach GadgetFS
   used -- there is a single file ("ep0" in case of FunctionFS and
   named after the controller in case of GadgetFS) that is used to
   receive events from kernel and handle ep0 communication.

   I think it is not the best approach as it'd be simpler and cleaner
   if there were two files: one for receiving events and another for
   handling ep0 communication.

   What do you think? Should I keep the current version or change to
   code to use two files?

2. What still needs to be implemented is a mechanism allowing double
   buffering (and in effect transmission without pauses) and maybe
   single-thread user-space driver implementation.

   I'd like to ask what would be the best way to achieve this.
   GadgetFS implements asynchronous I/O -- is it still the best
   option?

3. The last thing I'd like to mention is that the FunctionFS is
   designed in such a way that with some more work it will be able
   to mount it several times so in the end a gadget could use several
   FunctionFS functions.

   The idea is that each FunctionFS instance is identified by the
   device name used when mounting.

   One can imagine a gadget that has an Ethernet, MTP and HID
   interfaces where the last two are implemented via FunctionFS.  On
   user space level it would look like this:

   $ modprobe g_foo
   $ mkdir /dev/ffs-mtp && mount -t functionfs mtp /dev/ffs-mtp
   $ ( cd /dev/ffs-mtp && mtp-daemon ) &
   $ mkdir /dev/ffs-hid && mount -t functionfs hid /dev/ffs-hid
   $ ( cd /dev/ffs-hid && hid-daemon ) &

   On kernel level the gadget would check ffs_data->dev_name to
   identify whether it's FunctionFS designed for MTP ("mtp") or HID
   ("hid").


____________________________________________________________
[1] http://article.gmane.org/gmane.linux.usb.general/23890
[2] http://article.gmane.org/gmane.linux.usb.general/23902
[3] http://www.linux-usb.org/gadget/usb.c
[4] http://www.linux-usb.org/usbtest/testusb.c


David Brownell (1):
  USB: testusb: an USB testing application

Michal Nazarewicz (7):
  wait_event_interruptible_locked() interface
  fs/timerfd.c: make use of wait_event_interruptible_locked_irq()
  USB: gadget: __init and __exit tags removed
  USB: f_fs: the FunctionFS driver
  USB: g_ffs: the FunctionFS gadget driver
  USB: ffs-test: FunctionFS testing program
  USB: testusb: testusb compatibility with FunctionFS gadget

 drivers/usb/gadget/Kconfig          |   21 +-
 drivers/usb/gadget/Makefile         |    2 +
 drivers/usb/gadget/composite.c      |   21 +-
 drivers/usb/gadget/config.c         |    4 +-
 drivers/usb/gadget/epautoconf.c     |   12 +-
 drivers/usb/gadget/f_acm.c          |   32 +-
 drivers/usb/gadget/f_ecm.c          |   33 +-
 drivers/usb/gadget/f_fs.c           | 2453 +++++++++++++++++++++++++++++++++++
 drivers/usb/gadget/f_mass_storage.c |    2 +-
 drivers/usb/gadget/f_rndis.c        |   34 +-
 drivers/usb/gadget/g_ffs.c          |  322 +++++
 drivers/usb/gadget/u_ether.c        |    4 +-
 fs/timerfd.c                        |   22 +-
 include/linux/usb/functionfs.h      |  199 +++
 include/linux/wait.h                |  231 ++++-
 kernel/sched.c                      |    1 +
 tools/usb/ffs-test.c                |  554 ++++++++
 tools/usb/testusb.c                 |  537 ++++++++
 18 files changed, 4392 insertions(+), 92 deletions(-)
 create mode 100644 drivers/usb/gadget/f_fs.c
 create mode 100644 drivers/usb/gadget/g_ffs.c
 create mode 100644 include/linux/usb/functionfs.h
 create mode 100644 tools/usb/ffs-test.c
 create mode 100644 tools/usb/testusb.c


*Diff with Previous Version*

diff --git a/include/linux/wait.h b/include/linux/wait.h
see first patch

diff --git a/fs/timerfd.c b/fs/timerfd.c
see second patch

diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c
diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c
diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c
diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c
diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c
diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c
basically: sed -e s/__usb_\(init\|exit\|init_or_exit\_/__cold/ \
               -e s/__usb_initdata//

diff --git a/drivers/usb/gadget/usb-init-exit.h b/drivers/usb/gadget/usb-init-exit.h
deleted file mode 100644

diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c
--- a/drivers/usb/gadget/f_fs.c
+++ b/drivers/usb/gadget/f_fs.c
@@ -336,7 +336,7 @@ ffs_sb_create_file(struct super_block *sb, const char *name, void *data,
 static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock)
 	__attribute__((warn_unused_result, nonnull));
 static char *ffs_prepare_buffer(const char * __user buf, size_t len)
-	__attribute__((warn_unused_result));
+	__attribute__((warn_unused_result, nonnull));
 
 
 /* Control file aka ep0 *****************************************************/
@@ -533,34 +533,6 @@ done_spin:
 
 
 
-static int __ffs_ep0_read_wait_for_events(struct ffs_data *ffs)
-{
-	/* We are holding ffs->ev.waitq.lock */
-
-	DEFINE_WAIT(wait);
-	int ret = 0;
-
-	wait.flags |= WQ_FLAG_EXCLUSIVE;
-	__add_wait_queue_tail(&ffs->ev.waitq, &wait);
-
-	do {
-		set_current_state(TASK_INTERRUPTIBLE);
-		if (signal_pending(current)) {
-			ret = -ERESTARTSYS;
-			break;
-		}
-
-		spin_unlock_irq(&ffs->ev.waitq.lock);
-		schedule();
-		spin_lock_irq(&ffs->ev.waitq.lock);
-	} while (!ffs->ev.count);
-
-	__remove_wait_queue(&ffs->ev.waitq, &wait);
-	__set_current_state(TASK_RUNNING);
-
-	return ret;
-}
-
 static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
 				     size_t n)
 {
@@ -580,7 +552,13 @@ static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
 		}
 	} while (++i < n);
 
-	ffs->ev.count = 0;
+	if (n < ffs->ev.count) {
+		ffs->ev.count -= n;
+		memmove(ffs->ev.types, ffs->ev.types + n,
+			ffs->ev.count * sizeof *ffs->ev.types);
+	} else {
+		ffs->ev.count = 0;
+	}
 
 	spin_unlock_irq(&ffs->ev.waitq.lock);
 	mutex_unlock(&ffs->mutex);
@@ -594,8 +572,8 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
 			    size_t len, loff_t *ptr)
 {
 	struct ffs_data *ffs = file->private_data;
+	char *data = NULL;
 	size_t n;
-	char *data;
 	int ret;
 
 	ENTER();
@@ -638,8 +616,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
 			break;
 		}
 
-		if (!ffs->ev.count &&
-		    unlikely(__ffs_ep0_read_wait_for_events(ffs))) {
+		if (unlikely(wait_event_interruptible_exclusive_locked_irq(ffs->ev.waitq, ffs->ev.count))) {
 			ret = -EINTR;
 			break;
 		}
@@ -658,17 +635,18 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
 
 		spin_unlock_irq(&ffs->ev.waitq.lock);
 
-		data = ffs_prepare_buffer(NULL, len);
-		if (unlikely(IS_ERR(data))) {
-			ret = PTR_ERR(data);
-			goto done_mutex;
+		if (likely(len)) {
+			data = kmalloc(len, GFP_KERNEL);
+			if (unlikely(!data)) {
+				ret = -ENOMEM;
+				goto done_mutex;
+			}
 		}
 
 		spin_lock_irq(&ffs->ev.waitq.lock);
 
 		/* See ffs_ep0_write() */
 		if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) {
-			kfree(data);
 			ret = -EIDRM;
 			break;
 		}
@@ -677,7 +655,6 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
 		ret = __ffs_ep0_queue_wait(ffs, data, len);
 		if (likely(ret > 0) && unlikely(__copy_to_user(buf, data, len)))
 			ret = -EFAULT;
-		kfree(data);
 		goto done_mutex;
 
 	default:
@@ -688,6 +665,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
 	spin_unlock_irq(&ffs->ev.waitq.lock);
 done_mutex:
 	mutex_unlock(&ffs->mutex);
+	kfree(data);
 	return ret;
 }
 
@@ -1768,7 +1746,7 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
 				     ffs_entity_callback entity, void *priv)
 {
 	const unsigned _len = len;
-	long num = 0;
+	unsigned long num = 0;
 
 	ENTER();
 
@@ -1781,7 +1759,7 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
 		/* Record "descriptor" entitny */
 		ret = entity(FFS_DESCRIPTOR, (u8 *)num, (void *)data, priv);
 		if (unlikely(ret < 0)) {
-			FDBG("entity DESCRIPTOR(%02x); ret = %d", num, ret);
+			FDBG("entity DESCRIPTOR(%02lx); ret = %d", num, ret);
 			return ret;
 		}
 
@@ -2461,15 +2439,13 @@ static char *ffs_prepare_buffer(const char * __user buf, size_t len)
 	if (unlikely(!data))
 		return ERR_PTR(-ENOMEM);
 
-	if (likely(buf != NULL) && unlikely(__copy_from_user(data, buf, len))) {
+	if (unlikely(__copy_from_user(data, buf, len))) {
 		kfree(data);
 		return ERR_PTR(-EFAULT);
 	}
 
-	if (likely(buf)) {
-		FVDBG("Buffer from user space:");
-		ffs_dump_mem("", data, len);
-	}
+	FVDBG("Buffer from user space:");
+	ffs_dump_mem("", data, len);
 
 	return data;
 }


diff --git a/tools/usb/testusb.c b/tools/usb/testusb.c
index beaeea3..0a1a5b5 100644
--- a/tools/usb/testusb.c
+++ b/tools/usb/testusb.c
@@ -1,8 +1,10 @@
-/* $(CROSS_COMPILE)cc -Wall -g -lpthread -o testusb testusb.c */
+/* $(CROSS_COMPILE)cc -Wall -Wextra -g -lpthread -o testusb testusb.c */
 
 /*
  * Copyright (c) 2002 by David Brownell
- * 
+ * Copyright (c) 2010 by Samsung Electronics
+ * Author: Michal Nazarewicz <m.nazarewicz@samsung.com>
+ *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  * Free Software Foundation; either version 2 of the License, or (at your
@@ -25,6 +27,7 @@
 #include <pthread.h>
 #include <unistd.h>
 #include <errno.h>
+#include <limits.h>
 
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -236,6 +239,8 @@ static int find_testdev(const char *name, const struct stat *sb, int flag)
 	int				ifnum;
 	struct testdev			*entry;
 
+	(void)sb; /* unused */
+
 	if (flag != FTW_F)
 		return 0;
 	/* ignore /proc/bus/usb/{devices,drivers} */
@@ -339,11 +344,51 @@ restart:
 	return arg;
 }
 
+static const char *usbfs_dir_find(void)
+{
+	static char usbfs_path_0[] = "/dev/usb/devices";
+	static char usbfs_path_1[] = "/proc/bus/usb/devices";
+
+	static char *const usbfs_paths[] = {
+		usbfs_path_0, usbfs_path_1
+	};
+
+	static char *const *
+		end = usbfs_paths + sizeof usbfs_paths / sizeof *usbfs_paths;
+
+	char *const *it = usbfs_paths;
+	do {
+		int fd = open(*it, O_RDONLY);
+		close(fd);
+		if (fd >= 0) {
+			strrchr(*it, '/')[0] = '\0';
+			return *it;
+		}
+	} while (++it != end);
+
+	return NULL;
+}
+
+static int parse_num(unsigned *num, const char *str)
+{
+	unsigned long val;
+	char *end;
+
+	errno = 0;
+	val = strtoul(str, &end, 0);
+	if (errno || *end || val > UINT_MAX)
+		return -1;
+	*num = val;
+	return 0;
+}
+
 int main (int argc, char **argv)
 {
+
 	int			c;
 	struct testdev		*entry;
 	char			*device;
+	const char		*usbfs_dir = NULL;
 	int			all = 0, forever = 0, not = 0;
 	int			test = -1 /* all */;
 	struct usbtest_param	param;
@@ -365,23 +410,24 @@ int main (int argc, char **argv)
 	/* for easy use when hotplugging */
 	device = getenv ("DEVICE");
 
-	while ((c = getopt (argc, argv, "D:ac:g:hns:t:v:")) != EOF)
+	while ((c = getopt (argc, argv, "D:aA:c:g:hns:t:v:")) != EOF)
 	switch (c) {
 	case 'D':	/* device, if only one */
 		device = optarg;
 		continue;
+	case 'A':	/* use all devices with specified usbfs dir */
+		usbfs_dir = optarg;
+		/* FALL THROUGH */
 	case 'a':	/* use all devices */
-		device = 0;
+		device = NULL;
 		all = 1;
 		continue;
 	case 'c':	/* count iterations */
-		param.iterations = atoi (optarg);
-		if (param.iterations < 0)
+		if (parse_num(&param.iterations, optarg))
 			goto usage;
 		continue;
 	case 'g':	/* scatter/gather entries */
-		param.sglen = atoi (optarg);
-		if (param.sglen < 0)
+		if (parse_num(&param.sglen, optarg))
 			goto usage;
 		continue;
 	case 'l':	/* loop forever */
@@ -391,8 +437,7 @@ int main (int argc, char **argv)
 		not = 1;
 		continue;
 	case 's':	/* size of packet */
-		param.length = atoi (optarg);
-		if (param.length < 0)
+		if (parse_num(&param.length, optarg))
 			goto usage;
 		continue;
 	case 't':	/* run just one test */
@@ -401,15 +446,14 @@ int main (int argc, char **argv)
 			goto usage;
 		continue;
 	case 'v':	/* vary packet size by ... */
-		param.vary = atoi (optarg);
-		if (param.vary < 0)
+		if (parse_num(&param.vary, optarg))
 			goto usage;
 		continue;
 	case '?':
 	case 'h':
 	default:
 usage:
-		fprintf (stderr, "usage: %s [-an] [-D dev]\n"
+		fprintf (stderr, "usage: %s [-n] [-D dev | -a | -A usbfs-dir]\n"
 			"\t[-c iterations]  [-t testnum]\n"
 			"\t[-s packetsize] [-g sglen] [-v vary]\n",
 			argv [0]);
@@ -423,13 +467,17 @@ usage:
 		goto usage;
 	}
 
-	if ((c = open ("/proc/bus/usb/devices", O_RDONLY)) < 0) {
-		fputs ("usbfs files are missing\n", stderr);
-		return -1;
+	/* Find usbfs mount point */
+	if (!usbfs_dir) {
+		usbfs_dir = usbfs_dir_find();
+		if (!usbfs_dir) {
+			fputs ("usbfs files are missing\n", stderr);
+			return -1;
+		}
 	}
 
 	/* collect and list the test devices */
-	if (ftw ("/proc/bus/usb", find_testdev, 3) != 0) {
+	if (ftw (usbfs_dir, find_testdev, 3) != 0) {
 		fputs ("ftw failed; is usbfs missing?\n", stderr);
 		return -1;
 	}

^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [PATCHv2 1/8] wait_event_interruptible_locked() interface
  2010-04-09 19:21 [PATCH 0/7] The FunctionFS composite function Michal Nazarewicz
@ 2010-04-09 19:21 ` Michal Nazarewicz
  2010-04-09 19:21   ` [PATCHv2 2/8] fs/timerfd.c: make use of wait_event_interruptible_locked_irq() Michal Nazarewicz
  2010-04-11 15:02     ` Thomas Gleixner
  0 siblings, 2 replies; 47+ messages in thread
From: Michal Nazarewicz @ 2010-04-09 19:21 UTC (permalink / raw)
  To: linux-usb
  Cc: Michal Nazarewicz, Michal Nazarewicz, Davide Libenzi, Greg KH,
	Kyungmin Park, Marek Szyprowski, Thomas Gleixner, linux-fsdevel,
	linux-kernel

New wait_event_interruptible{,_exclusive}_locked{,_irq,_irqsave}
macros added.  They work just like versions without _locked* suffix
but require the wait queue's lock to be held.  Also __wake_up_locked()
is now exported as to pair it with the above macros.

The use case of this new facility is when one uses wait queue's lock
to protect a data structure.  This may be advantageous if the
structure needs to be protected by a spinlock anyway.  In particular,
with additional spinlock the following code has to be used to wait for
an condition:

#v+
spin_lock(&data.lock);
...
for (ret = 0; !ret && !(condition); ) {
	spin_unlock(&data.lock);
	ret = wait_event_interruptible(data.wqh, (condition));
	spin_lock(&data.lock);
}
...
spin_unlock(&data.lock);
#v-

This looks bizarre plus wait_event_interruptible() locks the wait
queue's lock anyway so there is a unlock+lock sequence where it could
be avoided.

To avoid those problems and benefit from wait queue's lock, a code
similar to the following should be used:

#v+
/* Waiting */
spin_lock(&data.wqh.lock);
...
ret = wait_event_interruptible_locked(data.wqh, (condition));
...
spin_unlock(&data.wqh.lock);

/* Waiting exclusively */
spin_lock(&data.whq.lock);
...
ret = wait_event_interruptible_exclusive_locked(data.whq, (condition));
...
spin_unlock(&data.whq.lock);

/* Waking up */
spin_lock(&data.wqh.lock);
...
wake_up_locked(&data.wqh);
...
spin_unlock(&data.wqh.lock);
#v-

When spin_lock_irq() or spin_lock_irqsave() is used matching versions
of macros need to be used (*_locked_irq() or * _locked_irqsave()).

Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com>
Cc: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
---
 include/linux/wait.h |  231 +++++++++++++++++++++++++++++++++++++++++++++++++-
 kernel/sched.c       |    1 +
 2 files changed, 231 insertions(+), 1 deletions(-)

diff --git a/include/linux/wait.h b/include/linux/wait.h
index a48e16b..1f97306 100644
--- a/include/linux/wait.h
+++ b/include/linux/wait.h
@@ -362,6 +362,235 @@ do {									\
 	__ret;								\
 })
 
+
+#define __wait_event_interruptible_locked(wq, condition, ret, exclusive, lock, unlock, lock_args) \
+do {									\
+	DEFINE_WAIT(__wait);						\
+									\
+	if (exclusive)							\
+		__wait.flags |= WQ_FLAG_EXCLUSIVE;			\
+	else								\
+		__wait.flags &= ~WQ_FLAG_EXCLUSIVE;			\
+	__add_wait_queue_tail(&(wq), &__wait);				\
+									\
+	do {								\
+		set_current_state(TASK_INTERRUPTIBLE);			\
+		if (signal_pending(current)) {				\
+			ret = -ERESTARTSYS;				\
+			break;						\
+		}							\
+		spin_unlock ##unlock lock_args;				\
+		schedule();						\
+		spin_lock ##lock lock_args;				\
+	} while (!(condition));						\
+	__remove_wait_queue(&(wq), &__wait);				\
+	__set_current_state(TASK_RUNNING);				\
+} while (0)
+
+
+/**
+ * wait_event_interruptible_locked - sleep until a condition gets true
+ * @wq: the waitqueue to wait on
+ * @condition: a C expression for the event to wait for
+ *
+ * The process is put to sleep (TASK_INTERRUPTIBLE) until the
+ * @condition evaluates to true or a signal is received.
+ * The @condition is checked each time the waitqueue @wq is woken up.
+ *
+ * It must be called with wq.lock being held.  This spinlock is
+ * unlocked while sleeping but @condition testing is done while lock
+ * is held and when this macro exits the lock is held.
+ *
+ * The lock is locked/unlocked using spin_lock()/spin_unlock()
+ * functions which must match the way they are locked/unlocked outside
+ * of this macro.
+ *
+ * wake_up_locked() has to be called after changing any variable that could
+ * change the result of the wait condition.
+ *
+ * The function will return -ERESTARTSYS if it was interrupted by a
+ * signal and 0 if @condition evaluated to true.
+ */
+#define wait_event_interruptible_locked(wq, condition)			\
+({									\
+	int __ret = 0;							\
+	if (!(condition))						\
+		__wait_event_interruptible_locked(wq, condition, __ret, 0, , , (&(wq).lock)); \
+	__ret;								\
+})
+
+/**
+ * wait_event_interruptible_locked_irq - sleep until a condition gets true
+ * @wq: the waitqueue to wait on
+ * @condition: a C expression for the event to wait for
+ *
+ * The process is put to sleep (TASK_INTERRUPTIBLE) until the
+ * @condition evaluates to true or a signal is received.
+ * The @condition is checked each time the waitqueue @wq is woken up.
+ *
+ * It must be called with wq.lock being held.  This spinlock is
+ * unlocked while sleeping but @condition testing is done while lock
+ * is held and when this macro exits the lock is held.
+ *
+ * The lock is locked/unlocked using spin_lock_irq()/spin_unlock_irq()
+ * functions which must match the way they are locked/unlocked outside
+ * of this macro.
+ *
+ * wake_up_locked() has to be called after changing any variable that could
+ * change the result of the wait condition.
+ *
+ * The function will return -ERESTARTSYS if it was interrupted by a
+ * signal and 0 if @condition evaluated to true.
+ */
+#define wait_event_interruptible_locked_irq(wq, condition)		\
+({									\
+	int __ret = 0;							\
+	if (!(condition))						\
+		__wait_event_interruptible_locked(wq, condition, __ret, 0, _irq, _irq, (&(wq).lock)); \
+	__ret;								\
+})
+
+/**
+ * wait_event_interruptible_locked_irq - sleep until a condition gets true
+ * @wq: the waitqueue to wait on
+ * @condition: a C expression for the event to wait for
+ * @flags: variable to restore/save IRQ state from/to
+ *
+ * The process is put to sleep (TASK_INTERRUPTIBLE) until the
+ * @condition evaluates to true or a signal is received.
+ * The @condition is checked each time the waitqueue @wq is woken up.
+ *
+ * It must be called with wq.lock being held.  This spinlock is
+ * unlocked while sleeping but @condition testing is done while lock
+ * is held and when this macro exits the lock is held.
+ *
+ * The lock is locked/unlocked using
+ * spin_lock_irqsave()/spin_unlock_irqrestore() functions which must
+ * match the way they are locked/unlocked outside of this macro.
+ *
+ * wake_up_locked() has to be called after changing any variable that could
+ * change the result of the wait condition.
+ *
+ * The function will return -ERESTARTSYS if it was interrupted by a
+ * signal and 0 if @condition evaluated to true.
+ */
+#define wait_event_interruptible_locked_irqsave(wq, condition, flags)	\
+({									\
+	int __ret = 0;							\
+	if (!(condition))						\
+		__wait_event_interruptible_locked(wq, condition, __ret, 0, _irqsave, _irqrestore, (&(wq).lock, flags)); \
+	__ret;								\
+})
+
+
+/**
+ * wait_event_interruptible_exclusive_locked - sleep exclusively until a condition gets true
+ * @wq: the waitqueue to wait on
+ * @condition: a C expression for the event to wait for
+ *
+ * The process is put to sleep (TASK_INTERRUPTIBLE) until the
+ * @condition evaluates to true or a signal is received.
+ * The @condition is checked each time the waitqueue @wq is woken up.
+ *
+ * It must be called with wq.lock being held.  This spinlock is
+ * unlocked while sleeping but @condition testing is done while lock
+ * is held and when this macro exits the lock is held.
+ *
+ * The lock is locked/unlocked using spin_lock()/spin_unlock()
+ * functions which must match the way they are locked/unlocked outside
+ * of this macro.
+ *
+ * The process is put on the wait queue with an WQ_FLAG_EXCLUSIVE flag
+ * set thus when other process waits process on the list if this
+ * process is awaken further processes are not considered.
+ *
+ * wake_up_locked() has to be called after changing any variable that could
+ * change the result of the wait condition.
+ *
+ * The function will return -ERESTARTSYS if it was interrupted by a
+ * signal and 0 if @condition evaluated to true.
+ */
+#define wait_event_interruptible_exclusive_locked(wq, condition)	\
+({									\
+	int __ret = 0;							\
+	if (!(condition))						\
+		__wait_event_interruptible_locked(wq, condition, __ret, 1, , , (&(wq).lock)); \
+	__ret;								\
+})
+
+/**
+ * wait_event_interruptible_exclusive_locked_irq - sleep until a condition gets true
+ * @wq: the waitqueue to wait on
+ * @condition: a C expression for the event to wait for
+ *
+ * The process is put to sleep (TASK_INTERRUPTIBLE) until the
+ * @condition evaluates to true or a signal is received.
+ * The @condition is checked each time the waitqueue @wq is woken up.
+ *
+ * It must be called with wq.lock being held.  This spinlock is
+ * unlocked while sleeping but @condition testing is done while lock
+ * is held and when this macro exits the lock is held.
+ *
+ * The lock is locked/unlocked using spin_lock_irq()/spin_unlock_irq()
+ * functions which must match the way they are locked/unlocked outside
+ * of this macro.
+ *
+ * The process is put on the wait queue with an WQ_FLAG_EXCLUSIVE flag
+ * set thus when other process waits process on the list if this
+ * process is awaken further processes are not considered.
+ *
+ * wake_up_locked() has to be called after changing any variable that could
+ * change the result of the wait condition.
+ *
+ * The function will return -ERESTARTSYS if it was interrupted by a
+ * signal and 0 if @condition evaluated to true.
+ */
+#define wait_event_interruptible_exclusive_locked_irq(wq, condition)	\
+({									\
+	int __ret = 0;							\
+	if (!(condition))						\
+		__wait_event_interruptible_locked(wq, condition, __ret, 1, _irq, _irq, (&(wq).lock)); \
+	__ret;								\
+})
+
+/**
+ * wait_event_interruptible_locked_irq - sleep until a condition gets true
+ * @wq: the waitqueue to wait on
+ * @condition: a C expression for the event to wait for
+ * @flags: variable to restore/save IRQ state from/to
+ *
+ * The process is put to sleep (TASK_INTERRUPTIBLE) until the
+ * @condition evaluates to true or a signal is received.
+ * The @condition is checked each time the waitqueue @wq is woken up.
+ *
+ * It must be called with wq.lock being held.  This spinlock is
+ * unlocked while sleeping but @condition testing is done while lock
+ * is held and when this macro exits the lock is held.
+ *
+ * The lock is locked/unlocked using
+ * spin_lock_irqsave()/spin_unlock_irqrestore() functions which must
+ * match the way they are locked/unlocked outside of this macro.
+ *
+ * The process is put on the wait queue with an WQ_FLAG_EXCLUSIVE flag
+ * set thus when other process waits process on the list if this
+ * process is awaken further processes are not considered.
+ *
+ * wake_up_locked() has to be called after changing any variable that could
+ * change the result of the wait condition.
+ *
+ * The function will return -ERESTARTSYS if it was interrupted by a
+ * signal and 0 if @condition evaluated to true.
+ */
+#define wait_event_interruptible_exclusive_locked_irqsave(wq, condition, flags)	\
+({									\
+	int __ret = 0;							\
+	if (!(condition))						\
+		__wait_event_interruptible_locked(wq, condition, __ret, 1, _irqsave, _irqrestore, (&(wq).lock, flags)); \
+	__ret;								\
+})
+
+
+
 #define __wait_event_killable(wq, condition, ret)			\
 do {									\
 	DEFINE_WAIT(__wait);						\
@@ -517,7 +746,7 @@ static inline int wait_on_bit_lock(void *word, int bit,
 		return 0;
 	return out_of_line_wait_on_bit_lock(word, bit, action, mode);
 }
-	
+
 #endif /* __KERNEL__ */
 
 #endif
diff --git a/kernel/sched.c b/kernel/sched.c
index 6ca5490..226e9ec 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -3940,6 +3940,7 @@ void __wake_up_locked(wait_queue_head_t *q, unsigned int mode)
 {
 	__wake_up_common(q, mode, 1, 0, NULL);
 }
+EXPORT_SYMBOL_GPL(__wake_up_locked);
 
 void __wake_up_locked_key(wait_queue_head_t *q, unsigned int mode, void *key)
 {

^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [PATCHv2 2/8] fs/timerfd.c: make use of wait_event_interruptible_locked_irq()
  2010-04-09 19:21 ` [PATCHv2 1/8] wait_event_interruptible_locked() interface Michal Nazarewicz
@ 2010-04-09 19:21   ` Michal Nazarewicz
  2010-04-09 19:21     ` [PATCHv2 3/8] USB: gadget: __init and __exit tags removed Michal Nazarewicz
  2010-04-11 14:31     ` [PATCHv2 2/8] fs/timerfd.c: make use of wait_event_interruptible_locked_irq() Thomas Gleixner
  2010-04-11 15:02     ` Thomas Gleixner
  1 sibling, 2 replies; 47+ messages in thread
From: Michal Nazarewicz @ 2010-04-09 19:21 UTC (permalink / raw)
  To: linux-usb
  Cc: Michal Nazarewicz, Michal Nazarewicz, Davide Libenzi, Greg KH,
	Kyungmin Park, Marek Szyprowski, Thomas Gleixner, linux-fsdevel,
	linux-kernel

This patch modifies the fs/timerfd.c to use the newly created
wait_event_interruptible_locked_irq() macro.  This replaces an open
code implementation with a single macro call.

Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com>
Cc: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
---
 fs/timerfd.c |   22 ++--------------------
 1 files changed, 2 insertions(+), 20 deletions(-)

diff --git a/fs/timerfd.c b/fs/timerfd.c
index 1bfc95a..4d2c371 100644
--- a/fs/timerfd.c
+++ b/fs/timerfd.c
@@ -109,31 +109,13 @@ static ssize_t timerfd_read(struct file *file, char __user *buf, size_t count,
 	struct timerfd_ctx *ctx = file->private_data;
 	ssize_t res;
 	u64 ticks = 0;
-	DECLARE_WAITQUEUE(wait, current);
 
 	if (count < sizeof(ticks))
 		return -EINVAL;
 	spin_lock_irq(&ctx->wqh.lock);
 	res = -EAGAIN;
-	if (!ctx->ticks && !(file->f_flags & O_NONBLOCK)) {
-		__add_wait_queue(&ctx->wqh, &wait);
-		for (res = 0;;) {
-			set_current_state(TASK_INTERRUPTIBLE);
-			if (ctx->ticks) {
-				res = 0;
-				break;
-			}
-			if (signal_pending(current)) {
-				res = -ERESTARTSYS;
-				break;
-			}
-			spin_unlock_irq(&ctx->wqh.lock);
-			schedule();
-			spin_lock_irq(&ctx->wqh.lock);
-		}
-		__remove_wait_queue(&ctx->wqh, &wait);
-		__set_current_state(TASK_RUNNING);
-	}
+	if (!(file->f_flags & O_NONBLOCK))
+		wait_event_interruptible_locked_irq(ctx->wqh, ctx->ticks);
 	if (ctx->ticks) {
 		ticks = ctx->ticks;
 		if (ctx->expired && ctx->tintv.tv64) {
-- 
1.7.0


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [PATCHv2 3/8] USB: gadget: __init and __exit tags removed
  2010-04-09 19:21   ` [PATCHv2 2/8] fs/timerfd.c: make use of wait_event_interruptible_locked_irq() Michal Nazarewicz
@ 2010-04-09 19:21     ` Michal Nazarewicz
  2010-04-09 19:21       ` [PATCHv2 4/8] USB: f_fs: the FunctionFS driver Michal Nazarewicz
  2010-04-29 22:15       ` [PATCHv2 3/8] USB: gadget: __init and __exit tags removed Greg KH
  2010-04-11 14:31     ` [PATCHv2 2/8] fs/timerfd.c: make use of wait_event_interruptible_locked_irq() Thomas Gleixner
  1 sibling, 2 replies; 47+ messages in thread
From: Michal Nazarewicz @ 2010-04-09 19:21 UTC (permalink / raw)
  To: linux-usb
  Cc: Michal Nazarewicz, Michal Nazarewicz, Greg KH, Kyungmin Park,
	Marek Szyprowski, linux-kernel

__init, __initdata and __exit tags have have been removed from
various files to make it possible for gadgets that do not use
the __init/__exit tags to use those.

Files in question are related to:
* the core composite framework,
* the mass storage function (fixing a section mismatch) and
* ethernet driver (ACM, ECM, RNDIS).

Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com>
Cc: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
---
 drivers/usb/gadget/composite.c      |   21 ++++++++++-----------
 drivers/usb/gadget/config.c         |    4 ++--
 drivers/usb/gadget/epautoconf.c     |   12 ++++++------
 drivers/usb/gadget/f_acm.c          |   32 ++++++++++++++++----------------
 drivers/usb/gadget/f_ecm.c          |   33 +++++++++++++++++----------------
 drivers/usb/gadget/f_mass_storage.c |    2 +-
 drivers/usb/gadget/f_rndis.c        |   34 ++++++++++++++++++----------------
 drivers/usb/gadget/u_ether.c        |    4 ++--
 8 files changed, 72 insertions(+), 70 deletions(-)

diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 09289bb..ff155ca 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -85,7 +85,7 @@ MODULE_PARM_DESC(iSerialNumber, "SerialNumber string");
  * This function returns the value of the function's bind(), which is
  * zero for success else a negative errno value.
  */
-int __init usb_add_function(struct usb_configuration *config,
+int __cold usb_add_function(struct usb_configuration *config,
 		struct usb_function *function)
 {
 	int	value = -EINVAL;
@@ -215,7 +215,7 @@ int usb_function_activate(struct usb_function *function)
  * Returns the interface ID which was allocated; or -ENODEV if no
  * more interface IDs can be allocated.
  */
-int __init usb_interface_id(struct usb_configuration *config,
+int __cold usb_interface_id(struct usb_configuration *config,
 		struct usb_function *function)
 {
 	unsigned id = config->next_interface_id;
@@ -480,7 +480,7 @@ done:
  * assigns global resources including string IDs, and per-configuration
  * resources such as interface IDs and endpoints.
  */
-int __init usb_add_config(struct usb_composite_dev *cdev,
+int __cold usb_add_config(struct usb_composite_dev *cdev,
 		struct usb_configuration *config)
 {
 	int				status = -EINVAL;
@@ -677,7 +677,7 @@ static int get_string(struct usb_composite_dev *cdev,
  * ensure that for example different functions don't wrongly assign
  * different meanings to the same identifier.
  */
-int __init usb_string_id(struct usb_composite_dev *cdev)
+int __cold usb_string_id(struct usb_composite_dev *cdev)
 {
 	if (cdev->next_string_id < 254) {
 		/* string id 0 is reserved */
@@ -898,7 +898,7 @@ static void composite_disconnect(struct usb_gadget *gadget)
 
 /*-------------------------------------------------------------------------*/
 
-static void /* __init_or_exit */
+static void __cold
 composite_unbind(struct usb_gadget *gadget)
 {
 	struct usb_composite_dev	*cdev = get_gadget_data(gadget);
@@ -947,7 +947,7 @@ composite_unbind(struct usb_gadget *gadget)
 	composite = NULL;
 }
 
-static void __init
+static void __cold
 string_override_one(struct usb_gadget_strings *tab, u8 id, const char *s)
 {
 	struct usb_string		*str = tab->strings;
@@ -960,7 +960,7 @@ string_override_one(struct usb_gadget_strings *tab, u8 id, const char *s)
 	}
 }
 
-static void __init
+static void __cold
 string_override(struct usb_gadget_strings **tab, u8 id, const char *s)
 {
 	while (*tab) {
@@ -969,7 +969,7 @@ string_override(struct usb_gadget_strings **tab, u8 id, const char *s)
 	}
 }
 
-static int __init composite_bind(struct usb_gadget *gadget)
+static int __cold composite_bind(struct usb_gadget *gadget)
 {
 	struct usb_composite_dev	*cdev;
 	int				status = -ENOMEM;
@@ -1092,7 +1092,6 @@ static struct usb_gadget_driver composite_driver = {
 	.speed		= USB_SPEED_HIGH,
 
 	.bind		= composite_bind,
-	/* .unbind		= __exit_p(composite_unbind), */
 	.unbind		= composite_unbind,
 
 	.setup		= composite_setup,
@@ -1121,7 +1120,7 @@ static struct usb_gadget_driver composite_driver = {
  * while it was binding.  That would usually be done in order to wait for
  * some userspace participation.
  */
-int __init usb_composite_register(struct usb_composite_driver *driver)
+int __cold usb_composite_register(struct usb_composite_driver *driver)
 {
 	if (!driver || !driver->dev || !driver->bind || composite)
 		return -EINVAL;
@@ -1142,7 +1141,7 @@ int __init usb_composite_register(struct usb_composite_driver *driver)
  * This function is used to unregister drivers using the composite
  * driver framework.
  */
-void /* __exit */ usb_composite_unregister(struct usb_composite_driver *driver)
+void __cold usb_composite_unregister(struct usb_composite_driver *driver)
 {
 	if (composite != driver)
 		return;
diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c
index e1191b9..a256138 100644
--- a/drivers/usb/gadget/config.c
+++ b/drivers/usb/gadget/config.c
@@ -127,7 +127,7 @@ int usb_gadget_config_buf(
  * with identifiers (for interfaces, strings, endpoints, and more)
  * as needed by a given function instance.
  */
-struct usb_descriptor_header **__init
+struct usb_descriptor_header **__cold
 usb_copy_descriptors(struct usb_descriptor_header **src)
 {
 	struct usb_descriptor_header **tmp;
@@ -174,7 +174,7 @@ usb_copy_descriptors(struct usb_descriptor_header **src)
  * intended use is to help remembering the endpoint descriptor to use
  * when enabling a given endpoint.
  */
-struct usb_endpoint_descriptor *__init
+struct usb_endpoint_descriptor *__cold
 usb_find_endpoint(
 	struct usb_descriptor_header **src,
 	struct usb_descriptor_header **copy,
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index 3568de2..dceaa35 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -34,12 +34,12 @@
 
 
 /* we must assign addresses for configurable endpoints (like net2280) */
-static __initdata unsigned epnum;
+static unsigned epnum;
 
 // #define MANY_ENDPOINTS
 #ifdef MANY_ENDPOINTS
 /* more than 15 configurable endpoints */
-static __initdata unsigned in_epnum;
+static unsigned in_epnum;
 #endif
 
 
@@ -59,7 +59,7 @@ static __initdata unsigned in_epnum;
  * NOTE:  each endpoint is unidirectional, as specified by its USB
  * descriptor; and isn't specific to a configuration or altsetting.
  */
-static int __init
+static int __cold
 ep_matches (
 	struct usb_gadget		*gadget,
 	struct usb_ep			*ep,
@@ -187,7 +187,7 @@ ep_matches (
 	return 1;
 }
 
-static struct usb_ep * __init
+static struct usb_ep *__cold
 find_ep (struct usb_gadget *gadget, const char *name)
 {
 	struct usb_ep	*ep;
@@ -229,7 +229,7 @@ find_ep (struct usb_gadget *gadget, const char *name)
  *
  * On failure, this returns a null endpoint descriptor.
  */
-struct usb_ep * __init usb_ep_autoconfig (
+struct usb_ep *__cold usb_ep_autoconfig (
 	struct usb_gadget		*gadget,
 	struct usb_endpoint_descriptor	*desc
 )
@@ -304,7 +304,7 @@ struct usb_ep * __init usb_ep_autoconfig (
  * state such as ep->driver_data and the record of assigned endpoints
  * used by usb_ep_autoconfig().
  */
-void __init usb_ep_autoconfig_reset (struct usb_gadget *gadget)
+void __cold usb_ep_autoconfig_reset (struct usb_gadget *gadget)
 {
 	struct usb_ep	*ep;
 
diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c
index e49c732..a098ff3 100644
--- a/drivers/usb/gadget/f_acm.c
+++ b/drivers/usb/gadget/f_acm.c
@@ -115,7 +115,7 @@ acm_iad_descriptor = {
 };
 
 
-static struct usb_interface_descriptor acm_control_interface_desc __initdata = {
+static struct usb_interface_descriptor acm_control_interface_desc = {
 	.bLength =		USB_DT_INTERFACE_SIZE,
 	.bDescriptorType =	USB_DT_INTERFACE,
 	/* .bInterfaceNumber = DYNAMIC */
@@ -126,7 +126,7 @@ static struct usb_interface_descriptor acm_control_interface_desc __initdata = {
 	/* .iInterface = DYNAMIC */
 };
 
-static struct usb_interface_descriptor acm_data_interface_desc __initdata = {
+static struct usb_interface_descriptor acm_data_interface_desc = {
 	.bLength =		USB_DT_INTERFACE_SIZE,
 	.bDescriptorType =	USB_DT_INTERFACE,
 	/* .bInterfaceNumber = DYNAMIC */
@@ -137,7 +137,7 @@ static struct usb_interface_descriptor acm_data_interface_desc __initdata = {
 	/* .iInterface = DYNAMIC */
 };
 
-static struct usb_cdc_header_desc acm_header_desc __initdata = {
+static struct usb_cdc_header_desc acm_header_desc = {
 	.bLength =		sizeof(acm_header_desc),
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
@@ -145,7 +145,7 @@ static struct usb_cdc_header_desc acm_header_desc __initdata = {
 };
 
 static struct usb_cdc_call_mgmt_descriptor
-acm_call_mgmt_descriptor __initdata = {
+acm_call_mgmt_descriptor = {
 	.bLength =		sizeof(acm_call_mgmt_descriptor),
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubType =	USB_CDC_CALL_MANAGEMENT_TYPE,
@@ -153,14 +153,14 @@ acm_call_mgmt_descriptor __initdata = {
 	/* .bDataInterface = DYNAMIC */
 };
 
-static struct usb_cdc_acm_descriptor acm_descriptor __initdata = {
+static struct usb_cdc_acm_descriptor acm_descriptor = {
 	.bLength =		sizeof(acm_descriptor),
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubType =	USB_CDC_ACM_TYPE,
 	.bmCapabilities =	USB_CDC_CAP_LINE,
 };
 
-static struct usb_cdc_union_desc acm_union_desc __initdata = {
+static struct usb_cdc_union_desc acm_union_desc = {
 	.bLength =		sizeof(acm_union_desc),
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
@@ -170,7 +170,7 @@ static struct usb_cdc_union_desc acm_union_desc __initdata = {
 
 /* full speed support: */
 
-static struct usb_endpoint_descriptor acm_fs_notify_desc __initdata = {
+static struct usb_endpoint_descriptor acm_fs_notify_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 	.bEndpointAddress =	USB_DIR_IN,
@@ -179,21 +179,21 @@ static struct usb_endpoint_descriptor acm_fs_notify_desc __initdata = {
 	.bInterval =		1 << GS_LOG2_NOTIFY_INTERVAL,
 };
 
-static struct usb_endpoint_descriptor acm_fs_in_desc __initdata = {
+static struct usb_endpoint_descriptor acm_fs_in_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 	.bEndpointAddress =	USB_DIR_IN,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 };
 
-static struct usb_endpoint_descriptor acm_fs_out_desc __initdata = {
+static struct usb_endpoint_descriptor acm_fs_out_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 	.bEndpointAddress =	USB_DIR_OUT,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 };
 
-static struct usb_descriptor_header *acm_fs_function[] __initdata = {
+static struct usb_descriptor_header *acm_fs_function[] = {
 	(struct usb_descriptor_header *) &acm_iad_descriptor,
 	(struct usb_descriptor_header *) &acm_control_interface_desc,
 	(struct usb_descriptor_header *) &acm_header_desc,
@@ -209,7 +209,7 @@ static struct usb_descriptor_header *acm_fs_function[] __initdata = {
 
 /* high speed support: */
 
-static struct usb_endpoint_descriptor acm_hs_notify_desc __initdata = {
+static struct usb_endpoint_descriptor acm_hs_notify_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 	.bEndpointAddress =	USB_DIR_IN,
@@ -218,21 +218,21 @@ static struct usb_endpoint_descriptor acm_hs_notify_desc __initdata = {
 	.bInterval =		GS_LOG2_NOTIFY_INTERVAL+4,
 };
 
-static struct usb_endpoint_descriptor acm_hs_in_desc __initdata = {
+static struct usb_endpoint_descriptor acm_hs_in_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 	.wMaxPacketSize =	cpu_to_le16(512),
 };
 
-static struct usb_endpoint_descriptor acm_hs_out_desc __initdata = {
+static struct usb_endpoint_descriptor acm_hs_out_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 	.wMaxPacketSize =	cpu_to_le16(512),
 };
 
-static struct usb_descriptor_header *acm_hs_function[] __initdata = {
+static struct usb_descriptor_header *acm_hs_function[] = {
 	(struct usb_descriptor_header *) &acm_iad_descriptor,
 	(struct usb_descriptor_header *) &acm_control_interface_desc,
 	(struct usb_descriptor_header *) &acm_header_desc,
@@ -570,7 +570,7 @@ static int acm_send_break(struct gserial *port, int duration)
 /*-------------------------------------------------------------------------*/
 
 /* ACM function driver setup/binding */
-static int __init
+static int __cold
 acm_bind(struct usb_configuration *c, struct usb_function *f)
 {
 	struct usb_composite_dev *cdev = c->cdev;
@@ -718,7 +718,7 @@ static inline bool can_support_cdc(struct usb_configuration *c)
  * handle all the ones it binds.  Caller is also responsible
  * for calling @gserial_cleanup() before module unload.
  */
-int __init acm_bind_config(struct usb_configuration *c, u8 port_num)
+int __cold acm_bind_config(struct usb_configuration *c, u8 port_num)
 {
 	struct f_acm	*acm;
 	int		status;
diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c
index 2fff530..e24ced7 100644
--- a/drivers/usb/gadget/f_ecm.c
+++ b/drivers/usb/gadget/f_ecm.c
@@ -112,7 +112,7 @@ static inline unsigned ecm_bitrate(struct usb_gadget *g)
 
 /* interface descriptor: */
 
-static struct usb_interface_descriptor ecm_control_intf __initdata = {
+static struct usb_interface_descriptor ecm_control_intf = {
 	.bLength =		sizeof ecm_control_intf,
 	.bDescriptorType =	USB_DT_INTERFACE,
 
@@ -125,7 +125,7 @@ static struct usb_interface_descriptor ecm_control_intf __initdata = {
 	/* .iInterface = DYNAMIC */
 };
 
-static struct usb_cdc_header_desc ecm_header_desc __initdata = {
+static struct usb_cdc_header_desc ecm_header_desc = {
 	.bLength =		sizeof ecm_header_desc,
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
@@ -133,7 +133,7 @@ static struct usb_cdc_header_desc ecm_header_desc __initdata = {
 	.bcdCDC =		cpu_to_le16(0x0110),
 };
 
-static struct usb_cdc_union_desc ecm_union_desc __initdata = {
+static struct usb_cdc_union_desc ecm_union_desc = {
 	.bLength =		sizeof(ecm_union_desc),
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
@@ -141,7 +141,7 @@ static struct usb_cdc_union_desc ecm_union_desc __initdata = {
 	/* .bSlaveInterface0 =	DYNAMIC */
 };
 
-static struct usb_cdc_ether_desc ecm_desc __initdata = {
+static struct usb_cdc_ether_desc ecm_desc = {
 	.bLength =		sizeof ecm_desc,
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubType =	USB_CDC_ETHERNET_TYPE,
@@ -156,7 +156,7 @@ static struct usb_cdc_ether_desc ecm_desc __initdata = {
 
 /* the default data interface has no endpoints ... */
 
-static struct usb_interface_descriptor ecm_data_nop_intf __initdata = {
+static struct usb_interface_descriptor ecm_data_nop_intf = {
 	.bLength =		sizeof ecm_data_nop_intf,
 	.bDescriptorType =	USB_DT_INTERFACE,
 
@@ -171,7 +171,7 @@ static struct usb_interface_descriptor ecm_data_nop_intf __initdata = {
 
 /* ... but the "real" data interface has two bulk endpoints */
 
-static struct usb_interface_descriptor ecm_data_intf __initdata = {
+static struct usb_interface_descriptor ecm_data_intf = {
 	.bLength =		sizeof ecm_data_intf,
 	.bDescriptorType =	USB_DT_INTERFACE,
 
@@ -186,7 +186,7 @@ static struct usb_interface_descriptor ecm_data_intf __initdata = {
 
 /* full speed support: */
 
-static struct usb_endpoint_descriptor fs_ecm_notify_desc __initdata = {
+static struct usb_endpoint_descriptor fs_ecm_notify_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
@@ -196,7 +196,7 @@ static struct usb_endpoint_descriptor fs_ecm_notify_desc __initdata = {
 	.bInterval =		1 << LOG2_STATUS_INTERVAL_MSEC,
 };
 
-static struct usb_endpoint_descriptor fs_ecm_in_desc __initdata = {
+static struct usb_endpoint_descriptor fs_ecm_in_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
@@ -204,7 +204,7 @@ static struct usb_endpoint_descriptor fs_ecm_in_desc __initdata = {
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 };
 
-static struct usb_endpoint_descriptor fs_ecm_out_desc __initdata = {
+static struct usb_endpoint_descriptor fs_ecm_out_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
@@ -212,7 +212,7 @@ static struct usb_endpoint_descriptor fs_ecm_out_desc __initdata = {
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 };
 
-static struct usb_descriptor_header *ecm_fs_function[] __initdata = {
+static struct usb_descriptor_header *ecm_fs_function[] = {
 	/* CDC ECM control descriptors */
 	(struct usb_descriptor_header *) &ecm_control_intf,
 	(struct usb_descriptor_header *) &ecm_header_desc,
@@ -230,7 +230,7 @@ static struct usb_descriptor_header *ecm_fs_function[] __initdata = {
 
 /* high speed support: */
 
-static struct usb_endpoint_descriptor hs_ecm_notify_desc __initdata = {
+static struct usb_endpoint_descriptor hs_ecm_notify_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
@@ -239,7 +239,7 @@ static struct usb_endpoint_descriptor hs_ecm_notify_desc __initdata = {
 	.wMaxPacketSize =	cpu_to_le16(ECM_STATUS_BYTECOUNT),
 	.bInterval =		LOG2_STATUS_INTERVAL_MSEC + 4,
 };
-static struct usb_endpoint_descriptor hs_ecm_in_desc __initdata = {
+static struct usb_endpoint_descriptor hs_ecm_in_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
@@ -248,7 +248,7 @@ static struct usb_endpoint_descriptor hs_ecm_in_desc __initdata = {
 	.wMaxPacketSize =	cpu_to_le16(512),
 };
 
-static struct usb_endpoint_descriptor hs_ecm_out_desc __initdata = {
+static struct usb_endpoint_descriptor hs_ecm_out_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
@@ -257,7 +257,7 @@ static struct usb_endpoint_descriptor hs_ecm_out_desc __initdata = {
 	.wMaxPacketSize =	cpu_to_le16(512),
 };
 
-static struct usb_descriptor_header *ecm_hs_function[] __initdata = {
+static struct usb_descriptor_header *ecm_hs_function[] = {
 	/* CDC ECM control descriptors */
 	(struct usb_descriptor_header *) &ecm_control_intf,
 	(struct usb_descriptor_header *) &ecm_header_desc,
@@ -596,7 +596,7 @@ static void ecm_close(struct gether *geth)
 
 /* ethernet function driver setup/binding */
 
-static int __init
+static int __cold
 ecm_bind(struct usb_configuration *c, struct usb_function *f)
 {
 	struct usb_composite_dev *cdev = c->cdev;
@@ -762,7 +762,8 @@ ecm_unbind(struct usb_configuration *c, struct usb_function *f)
  * Caller must have called @gether_setup().  Caller is also responsible
  * for calling @gether_cleanup() before module unload.
  */
-int __init ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
+int __cold
+ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
 {
 	struct f_ecm	*ecm;
 	int		status;
diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c
index f4911c0..e1d1454 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -2910,7 +2910,7 @@ static void fsg_unbind(struct usb_configuration *c, struct usb_function *f)
 }
 
 
-static int __init fsg_bind(struct usb_configuration *c, struct usb_function *f)
+static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
 {
 	struct fsg_dev		*fsg = fsg_from_func(f);
 	struct usb_gadget	*gadget = c->cdev->gadget;
diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c
index a30e60c..c1d6985 100644
--- a/drivers/usb/gadget/f_rndis.c
+++ b/drivers/usb/gadget/f_rndis.c
@@ -121,7 +121,7 @@ static unsigned int bitrate(struct usb_gadget *g)
 
 /* interface descriptor: */
 
-static struct usb_interface_descriptor rndis_control_intf __initdata = {
+static struct usb_interface_descriptor rndis_control_intf = {
 	.bLength =		sizeof rndis_control_intf,
 	.bDescriptorType =	USB_DT_INTERFACE,
 
@@ -134,7 +134,7 @@ static struct usb_interface_descriptor rndis_control_intf __initdata = {
 	/* .iInterface = DYNAMIC */
 };
 
-static struct usb_cdc_header_desc header_desc __initdata = {
+static struct usb_cdc_header_desc header_desc = {
 	.bLength =		sizeof header_desc,
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
@@ -142,7 +142,8 @@ static struct usb_cdc_header_desc header_desc __initdata = {
 	.bcdCDC =		cpu_to_le16(0x0110),
 };
 
-static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor __initdata = {
+static struct usb_cdc_call_mgmt_descriptor
+call_mgmt_descriptor = {
 	.bLength =		sizeof call_mgmt_descriptor,
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubType =	USB_CDC_CALL_MANAGEMENT_TYPE,
@@ -151,7 +152,7 @@ static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor __initdata = {
 	.bDataInterface =	0x01,
 };
 
-static struct usb_cdc_acm_descriptor rndis_acm_descriptor __initdata = {
+static struct usb_cdc_acm_descriptor rndis_acm_descriptor = {
 	.bLength =		sizeof rndis_acm_descriptor,
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubType =	USB_CDC_ACM_TYPE,
@@ -159,7 +160,7 @@ static struct usb_cdc_acm_descriptor rndis_acm_descriptor __initdata = {
 	.bmCapabilities =	0x00,
 };
 
-static struct usb_cdc_union_desc rndis_union_desc __initdata = {
+static struct usb_cdc_union_desc rndis_union_desc = {
 	.bLength =		sizeof(rndis_union_desc),
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
@@ -169,7 +170,7 @@ static struct usb_cdc_union_desc rndis_union_desc __initdata = {
 
 /* the data interface has two bulk endpoints */
 
-static struct usb_interface_descriptor rndis_data_intf __initdata = {
+static struct usb_interface_descriptor rndis_data_intf = {
 	.bLength =		sizeof rndis_data_intf,
 	.bDescriptorType =	USB_DT_INTERFACE,
 
@@ -197,7 +198,7 @@ rndis_iad_descriptor = {
 
 /* full speed support: */
 
-static struct usb_endpoint_descriptor fs_notify_desc __initdata = {
+static struct usb_endpoint_descriptor fs_notify_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
@@ -207,7 +208,7 @@ static struct usb_endpoint_descriptor fs_notify_desc __initdata = {
 	.bInterval =		1 << LOG2_STATUS_INTERVAL_MSEC,
 };
 
-static struct usb_endpoint_descriptor fs_in_desc __initdata = {
+static struct usb_endpoint_descriptor fs_in_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
@@ -215,7 +216,7 @@ static struct usb_endpoint_descriptor fs_in_desc __initdata = {
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 };
 
-static struct usb_endpoint_descriptor fs_out_desc __initdata = {
+static struct usb_endpoint_descriptor fs_out_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
@@ -223,7 +224,7 @@ static struct usb_endpoint_descriptor fs_out_desc __initdata = {
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 };
 
-static struct usb_descriptor_header *eth_fs_function[] __initdata = {
+static struct usb_descriptor_header *eth_fs_function[] = {
 	(struct usb_descriptor_header *) &rndis_iad_descriptor,
 	/* control interface matches ACM, not Ethernet */
 	(struct usb_descriptor_header *) &rndis_control_intf,
@@ -241,7 +242,7 @@ static struct usb_descriptor_header *eth_fs_function[] __initdata = {
 
 /* high speed support: */
 
-static struct usb_endpoint_descriptor hs_notify_desc __initdata = {
+static struct usb_endpoint_descriptor hs_notify_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
@@ -250,7 +251,7 @@ static struct usb_endpoint_descriptor hs_notify_desc __initdata = {
 	.wMaxPacketSize =	cpu_to_le16(STATUS_BYTECOUNT),
 	.bInterval =		LOG2_STATUS_INTERVAL_MSEC + 4,
 };
-static struct usb_endpoint_descriptor hs_in_desc __initdata = {
+static struct usb_endpoint_descriptor hs_in_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
@@ -259,7 +260,7 @@ static struct usb_endpoint_descriptor hs_in_desc __initdata = {
 	.wMaxPacketSize =	cpu_to_le16(512),
 };
 
-static struct usb_endpoint_descriptor hs_out_desc __initdata = {
+static struct usb_endpoint_descriptor hs_out_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
@@ -268,7 +269,7 @@ static struct usb_endpoint_descriptor hs_out_desc __initdata = {
 	.wMaxPacketSize =	cpu_to_le16(512),
 };
 
-static struct usb_descriptor_header *eth_hs_function[] __initdata = {
+static struct usb_descriptor_header *eth_hs_function[] = {
 	(struct usb_descriptor_header *) &rndis_iad_descriptor,
 	/* control interface matches ACM, not Ethernet */
 	(struct usb_descriptor_header *) &rndis_control_intf,
@@ -593,7 +594,7 @@ static void rndis_close(struct gether *geth)
 
 /* ethernet function driver setup/binding */
 
-static int __init
+static int __cold
 rndis_bind(struct usb_configuration *c, struct usb_function *f)
 {
 	struct usb_composite_dev *cdev = c->cdev;
@@ -785,7 +786,8 @@ static inline bool can_support_rndis(struct usb_configuration *c)
  * Caller must have called @gether_setup().  Caller is also responsible
  * for calling @gether_cleanup() before module unload.
  */
-int __init rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
+int __cold
+rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
 {
 	struct f_rndis	*rndis;
 	int		status;
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
index 84ca195..e629d91 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -714,7 +714,7 @@ static u8 __init nibble(unsigned char c)
 	return 0;
 }
 
-static int __init get_ether_addr(const char *str, u8 *dev_addr)
+static int __cold get_ether_addr(const char *str, u8 *dev_addr)
 {
 	if (str) {
 		unsigned	i;
@@ -763,7 +763,7 @@ static struct device_type gadget_type = {
  *
  * Returns negative errno, or zero on success
  */
-int __init gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN])
+int __cold gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN])
 {
 	struct eth_dev		*dev;
 	struct net_device	*net;
-- 
1.7.0


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [PATCHv2 4/8] USB: f_fs: the FunctionFS driver
  2010-04-09 19:21     ` [PATCHv2 3/8] USB: gadget: __init and __exit tags removed Michal Nazarewicz
@ 2010-04-09 19:21       ` Michal Nazarewicz
  2010-04-09 19:21         ` [PATCHv2 5/8] USB: g_ffs: the FunctionFS gadget driver Michal Nazarewicz
  2010-04-29 22:15       ` [PATCHv2 3/8] USB: gadget: __init and __exit tags removed Greg KH
  1 sibling, 1 reply; 47+ messages in thread
From: Michal Nazarewicz @ 2010-04-09 19:21 UTC (permalink / raw)
  To: linux-usb
  Cc: Michal Nazarewicz, Michal Nazarewicz, Greg KH, Kyungmin Park,
	Marek Szyprowski, linux-kernel

The FunctionFS is a USB composite function that can be used
with the composite framework to create an USB gadget.

>From kernel point of view it is just a composite function with
some unique behaviour.  It may be added to an USB
configuration only after the user space driver has registered
by writing descriptors and strings (the user space program has
to provide the same information that kernel level composite
functions provide when they are added to the configuration).

>From user space point of view it is a file system which when
mounted provide an "ep0" file.  User space driver need to
write descriptors and strings to that file.  It does not need
to worry about endpoints, interfaces or strings numbers but
simply provide descriptors such as if the function was the
only one (endpoints and strings numbers starting from one and
interface numbers starting from core).  The FunctionFS changes
numbers of those as needed also handling situation when
numbers differ in different configurations.

When descriptors and strings are written "ep#" files appear
(one for each declared endpoint) which handle communication on
a single endpoint.  Again, FunctionFS takes care of the real
numbers and changing of the configuration (which means that
"ep1" file may be really mapped to (say) endpoint 3 (and when
configuration changes to (say) endpoint 2)).  "ep0" is used
for receiving events and handling setup requests.

When all files are closed the function disables itself.

Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com>
Cc: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
---
 drivers/usb/gadget/f_fs.c      | 2451 ++++++++++++++++++++++++++++++++++++++++
 include/linux/usb/functionfs.h |  199 ++++
 2 files changed, 2650 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/gadget/f_fs.c
 create mode 100644 include/linux/usb/functionfs.h

diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c
new file mode 100644
index 0000000..fb0a400
--- /dev/null
+++ b/drivers/usb/gadget/f_fs.c
@@ -0,0 +1,2451 @@
+/*
+ * f_fs.c -- user mode filesystem api for usb composite funtcion controllers
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ * Author: Michal Nazarewicz <m.nazarewicz@samsung.com>
+ *
+ * Based on inode.c (GadgetFS):
+ * Copyright (C) 2003-2004 David Brownell
+ * Copyright (C) 2003 Agilent Technologies
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+/* #define DEBUG */
+/* #define VERBOSE_DEBUG */
+
+#include <linux/blkdev.h>
+#include <asm/unaligned.h>
+#include <linux/smp_lock.h>
+
+#include <linux/usb/composite.h>
+#include <linux/usb/functionfs.h>
+
+
+#define FUNCTIONFS_MAGIC	0xa647361 /* Chosen by a honest dice roll ;) */
+
+
+/* Debuging *****************************************************************/
+
+#define ffs_printk(level, fmt, args...) printk(level "f_fs: " fmt "\n", ## args)
+
+#define FERR(...)  ffs_printk(KERN_ERR,  __VA_ARGS__)
+#define FINFO(...) ffs_printk(KERN_INFO, __VA_ARGS__)
+
+#ifdef DEBUG
+#  define FDBG(...) ffs_printk(KERN_DEBUG, __VA_ARGS__)
+#else
+#  define FDBG(...) do { } while (0)
+#endif /* DEBUG */
+
+#ifdef VERBOSE_DEBUG
+#  define FVDBG FDBG
+#else
+#  define FVDBG(...) do { } while (0)
+#endif /* VERBOSE_DEBUG */
+
+#define ENTER()    FVDBG("%s()", __func__)
+
+#ifdef VERBOSE_DEBUG
+#  define ffs_dump_mem(prefix, ptr, len) \
+	print_hex_dump_bytes("f_fs" prefix ": ", DUMP_PREFIX_NONE, ptr, len)
+#else
+#  define ffs_dump_mem(prefix, ptr, len) do { } while (0)
+#endif
+
+
+/* The data structure and setup file ****************************************/
+
+enum ffs_state {
+	/* Waiting for descriptors and strings. */
+	/* In this state no open(2), read(2) or write(2) on epfiles
+	 * may succeed (which should not be the problem as there
+	 * should be no such files opened in the firts place). */
+	FFS_READ_DESCRIPTORS,
+	FFS_READ_STRINGS,
+
+	/* We've got descriptors and strings.  We are or have called
+	 * functionfs_ready_callback().  functionfs_bind() may have
+	 * been called but we don't know. */
+	/* This is the only state in which operations on epfiles may
+	 * succeed. */
+	FFS_ACTIVE,
+
+	/* All endpoints have been closed.  This state is also set if
+	 * we encounter an unrecoverable error.  The only
+	 * unrecoverable error is situation when after reading strings
+	 * from user space we fail to initialise EP files or
+	 * functionfs_ready_callback() returns with error (<0). */
+	/* In this state no open(2), read(2) or write(2) (both on ep0
+	 * as well as epfile) may succeed (at this point epfiles are
+	 * unlinked and all closed so this is not a problem; ep0 is
+	 * also closed but ep0 file exists and so open(2) on ep0 must
+	 * fail). */
+	FFS_CLOSING
+};
+
+
+enum ffs_setup_state {
+	/* There is no setup request pending. */
+	FFS_NO_SETUP,
+	/* User has read events and there was a setup request event
+	 * there.  The next read/write on ep0 will handle the
+	 * request. */
+	FFS_SETUP_PENDING,
+	/* There was event pending but before user space handled it
+	 * some other event was introduced which canceled existing
+	 * setup.  If this state is set read/write on ep0 return
+	 * -EIDRM.  This state is only set when adding event. */
+	FFS_SETUP_CANCELED
+};
+
+
+
+struct ffs_epfile;
+struct ffs_function;
+
+struct ffs_data {
+	struct usb_gadget		*gadget;
+
+	/* Protect access read/write operations, only one read/write
+	 * at a time.  As a consequence protects ep0req and company.
+	 * While setup request is being processed (queued) this is
+	 * held. */
+	struct mutex			mutex;
+
+	/* Protect access to enpoint related structures (basically
+	 * usb_ep_queue(), usb_ep_dequeue(), etc. calls) except for
+	 * endpint zero. */
+	spinlock_t			eps_lock;
+
+	/* XXX REVISIT do we need our own request? Since we are not
+	 * handling setup requests immidiatelly user space may be so
+	 * slow that another setup will be sent to the gadget but this
+	 * time not to us but another function and then there could be
+	 * a race.  Is taht the case? Or maybe we can use cdev->req
+	 * after all, maybe we just need some spinlock for that? */
+	struct usb_request		*ep0req;		/* P: mutex */
+	struct completion		ep0req_completion;	/* P: mutex */
+	int				ep0req_status;		/* P: mutex */
+
+	/* reference counter */
+	atomic_t			ref;
+	/* how many files are opened (EP0 and others) */
+	atomic_t			opened;
+
+	/* EP0 state */
+	enum ffs_state			state;
+
+	/*
+	 * Possible transations:
+	 * + FFS_NO_SETUP       -> FFS_SETUP_PENDING  -- P: ev.waitq.lock
+	 *               happens only in ep0 read which is P: mutex
+	 * + FFS_SETUP_PENDING  -> FFS_NO_SETUP       -- P: ev.waitq.lock
+	 *               happens only in ep0 i/o  which is P: mutex
+	 * + FFS_SETUP_PENDING  -> FFS_SETUP_CANCELED -- P: ev.waitq.lock
+	 * + FFS_SETUP_CANCELED -> FFS_NO_SETUP       -- cmpxchg
+	 */
+	enum ffs_setup_state		setup_state;
+
+#define FFS_SETUP_STATE(ffs)					\
+	((enum ffs_setup_state)cmpxchg(&(ffs)->setup_state,	\
+				       FFS_SETUP_CANCELED, FFS_NO_SETUP))
+
+	/* Events & such. */
+	struct {
+		u8				types[4];
+		unsigned short			count;
+		/* XXX REVISIT need to update it in some places, or do we? */
+		unsigned short			can_stall;
+		struct usb_ctrlrequest		setup;
+
+		wait_queue_head_t		waitq;
+	} ev; /* the whole structure, P: ev.waitq.lock */
+
+	/* Flags */
+	unsigned long			flags;
+#define FFS_FL_CALL_CLOSED_CALLBACK 0
+#define FFS_FL_BOUND                1
+
+	/* Active function */
+	struct ffs_function		*func;
+
+	/* Device name, write once when file system is mounted.
+	 * Intendet for user to read if she wants. */
+	const char			*dev_name;
+	/* Private data for our user (ie. gadget).  Managed by
+	 * user. */
+	void				*private_data;
+
+	/* filled by __ffs_data_got_descs() */
+	/* real descriptors are 16 bytes after raw_descs (so you need
+	 * to skip 16 bytes (ie. ffs->raw_descs + 16) to get to the
+	 * first full speed descriptor).  raw_descs_length and
+	 * raw_fs_descs_length do not have those 16 bytes added. */
+	const void			*raw_descs;
+	unsigned			raw_descs_length;
+	unsigned			raw_fs_descs_length;
+	unsigned			fs_descs_count;
+	unsigned			hs_descs_count;
+
+	unsigned short			strings_count;
+	unsigned short			interfaces_count;
+	unsigned short			eps_count;
+	unsigned short			_pad1;
+
+	/* filled by __ffs_data_got_strings() */
+	/* ids in stringtabs are set in functionfs_bind() */
+	const void			*raw_strings;
+	struct usb_gadget_strings	**stringtabs;
+
+	/* File system's super block, write once when file system is mounted. */
+	struct super_block		*sb;
+
+	/* File permissions, written once when fs is mounted*/
+	struct ffs_file_perms {
+		umode_t				mode;
+		uid_t				uid;
+		gid_t				gid;
+	}				file_perms;
+
+	/* The endpoint files, filled by ffs_epfiles_create(),
+	 * destroyed by ffs_epfiles_destroy(). */
+	struct ffs_epfile		*epfiles;
+};
+
+/* Reference counter handling */
+static void ffs_data_get(struct ffs_data *ffs);
+static void ffs_data_put(struct ffs_data *ffs);
+/* Creates new ffs_data object. */
+static struct ffs_data *__must_check ffs_data_new(void) __attribute__((malloc));
+
+/* Opened counter handling. */
+static void ffs_data_opened(struct ffs_data *ffs);
+static void ffs_data_closed(struct ffs_data *ffs);
+
+/* Called with ffs->mutex held; take over ownerrship of data. */
+static int __must_check
+__ffs_data_got_descs(struct ffs_data *ffs, char *data, size_t len);
+static int __must_check
+__ffs_data_got_strings(struct ffs_data *ffs, char *data, size_t len);
+
+
+/* The function structure ***************************************************/
+
+struct ffs_ep;
+
+struct ffs_function {
+	struct usb_configuration	*conf;
+	struct usb_gadget		*gadget;
+	struct ffs_data			*ffs;
+
+	struct ffs_ep			*eps;
+	u8				eps_revmap[16];
+	short				*interfaces_nums;
+
+	struct usb_function		function;
+};
+
+
+static struct ffs_function *ffs_func_from_usb(struct usb_function *f)
+{
+	return container_of(f, struct ffs_function, function);
+}
+
+static void ffs_func_free(struct ffs_function *func);
+
+
+static void ffs_func_eps_disable(struct ffs_function *func);
+static int __must_check ffs_func_eps_enable(struct ffs_function *func);
+
+
+static int ffs_func_bind(struct usb_configuration *,
+			 struct usb_function *);
+static void ffs_func_unbind(struct usb_configuration *,
+			    struct usb_function *);
+static int ffs_func_set_alt(struct usb_function *, unsigned, unsigned);
+static void ffs_func_disable(struct usb_function *);
+static int ffs_func_setup(struct usb_function *,
+			  const struct usb_ctrlrequest *);
+static void ffs_func_suspend(struct usb_function *);
+static void ffs_func_resume(struct usb_function *);
+
+
+static int ffs_func_revmap_ep(struct ffs_function *func, u8 num);
+static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf);
+
+
+
+/* The endpoints structures *************************************************/
+
+struct ffs_ep {
+	struct usb_ep			*ep;	/* P: ffs->eps_lock */
+	struct usb_request		*req;	/* P: epfile->mutex */
+
+	/* [0]: full speed, [1]: high speed */
+	struct usb_endpoint_descriptor	*descs[2];
+
+	u8				num;
+
+	int				status;	/* P: epfile->mutex */
+};
+
+struct ffs_epfile {
+	/* Protects ep->ep and ep->req. */
+	struct mutex			mutex;
+	wait_queue_head_t		wait;
+
+	struct ffs_data			*ffs;
+	struct ffs_ep			*ep;	/* P: ffs->eps_lock */
+
+	struct dentry			*dentry;
+
+	char				name[5];
+
+	unsigned char			in;	/* P: ffs->eps_lock */
+	unsigned char			isoc;	/* P: ffs->eps_lock */
+
+	unsigned char			_pad;
+};
+
+
+static int  __must_check ffs_epfiles_create(struct ffs_data *ffs);
+static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count);
+
+static struct inode *__must_check
+ffs_sb_create_file(struct super_block *sb, const char *name, void *data,
+		   const struct file_operations *fops,
+		   struct dentry **dentry_p);
+
+
+/* Misc helper functions ****************************************************/
+
+static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock)
+	__attribute__((warn_unused_result, nonnull));
+static char *ffs_prepare_buffer(const char * __user buf, size_t len)
+	__attribute__((warn_unused_result, nonnull));
+
+
+/* Control file aka ep0 *****************************************************/
+
+static void ffs_ep0_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct ffs_data *ffs = req->context;
+
+	complete_all(&ffs->ep0req_completion);
+}
+
+
+static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
+{
+	struct usb_request *req = ffs->ep0req;
+	int ret;
+
+	req->zero     = len < le16_to_cpu(ffs->ev.setup.wLength);
+
+	spin_unlock_irq(&ffs->ev.waitq.lock);
+
+	req->buf      = data;
+	req->length   = len;
+
+	INIT_COMPLETION(ffs->ep0req_completion);
+
+	ret = usb_ep_queue(ffs->gadget->ep0, req, GFP_ATOMIC);
+	if (unlikely(ret < 0))
+		return ret;
+
+	ret = wait_for_completion_interruptible(&ffs->ep0req_completion);
+	if (unlikely(ret)) {
+		usb_ep_dequeue(ffs->gadget->ep0, req);
+		return -EINTR;
+	}
+
+	ffs->setup_state = FFS_NO_SETUP;
+	return ffs->ep0req_status;
+}
+
+static int __ffs_ep0_stall(struct ffs_data *ffs)
+{
+	if (ffs->ev.can_stall) {
+		FVDBG("ep0 stall\n");
+		usb_ep_set_halt(ffs->gadget->ep0);
+		ffs->setup_state = FFS_NO_SETUP;
+		return -EL2HLT;
+	} else {
+		FDBG("bogus ep0 stall!\n");
+		return -ESRCH;
+	}
+}
+
+
+static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
+			     size_t len, loff_t *ptr)
+{
+	struct ffs_data *ffs = file->private_data;
+	ssize_t ret;
+	char *data;
+
+	ENTER();
+
+	/* Fast check if setup was canceled */
+	if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED)
+		return -EIDRM;
+
+	/* Acquire mutex */
+	ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
+	if (unlikely(ret < 0))
+		return ret;
+
+
+	/* Check state */
+	switch (ffs->state) {
+	case FFS_READ_DESCRIPTORS:
+	case FFS_READ_STRINGS:
+		/* Copy data */
+		if (unlikely(len < 16)) {
+			ret = -EINVAL;
+			break;
+		}
+
+		data = ffs_prepare_buffer(buf, len);
+		if (unlikely(IS_ERR(data))) {
+			ret = PTR_ERR(data);
+			break;
+		}
+
+		/* Handle data */
+		if (ffs->state == FFS_READ_DESCRIPTORS) {
+			FINFO("read descriptors");
+			ret = __ffs_data_got_descs(ffs, data, len);
+			if (unlikely(ret < 0))
+				break;
+
+			ffs->state = FFS_READ_STRINGS;
+			ret = len;
+		} else {
+			FINFO("read strings");
+			ret = __ffs_data_got_strings(ffs, data, len);
+			if (unlikely(ret < 0))
+				break;
+
+			ret = ffs_epfiles_create(ffs);
+			if (unlikely(ret)) {
+				ffs->state = FFS_CLOSING;
+				break;
+			}
+
+			ffs->state = FFS_ACTIVE;
+			mutex_unlock(&ffs->mutex);
+
+			ret = functionfs_ready_callback(ffs);
+			if (unlikely(ret < 0)) {
+				ffs->state = FFS_CLOSING;
+				return ret;
+			}
+
+			set_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags);
+			return len;
+		}
+		break;
+
+
+	case FFS_ACTIVE:
+		data = NULL;
+		/* We're called from user space, we can use _irq
+		 * rather then _irqsave */
+		spin_lock_irq(&ffs->ev.waitq.lock);
+		switch (FFS_SETUP_STATE(ffs)) {
+		case FFS_SETUP_CANCELED:
+			ret = -EIDRM;
+			goto done_spin;
+
+		case FFS_NO_SETUP:
+			ret = -ESRCH;
+			goto done_spin;
+
+		case FFS_SETUP_PENDING:
+			break;
+		}
+
+		/* FFS_SETUP_PENDING */
+		if (!(ffs->ev.setup.bRequestType & USB_DIR_IN)) {
+			ret = __ffs_ep0_stall(ffs);
+			goto done_spin;
+		}
+
+		/* FFS_SETUP_PENDING and not stall */
+		len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength));
+
+		spin_unlock_irq(&ffs->ev.waitq.lock);
+
+		data = ffs_prepare_buffer(buf, len);
+		if (unlikely(IS_ERR(data))) {
+			ret = PTR_ERR(data);
+			break;
+		}
+
+		spin_lock_irq(&ffs->ev.waitq.lock);
+
+		/* We are guaranteed to be still in FFS_ACTIVE state
+		 * but the state of setup could have changed from
+		 * FFS_SETUP_PENDING to FFS_SETUP_CANCELED so we need
+		 * to check for that.  If that happened we copied data
+		 * from user space in vain but it's unlikely. */
+		/* For sure we are not in FFS_NO_SETUP since this is
+		 * the only place FFS_SETUP_PENDING -> FFS_NO_SETUP
+		 * transition can be performed and it's protected by
+		 * mutex. */
+
+		if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) {
+			ret = -EIDRM;
+done_spin:
+			spin_unlock_irq(&ffs->ev.waitq.lock);
+		} else {
+			/* unlocks spinlock */
+			ret = __ffs_ep0_queue_wait(ffs, data, len);
+		}
+		kfree(data);
+		break;
+
+
+	default:
+		ret = -EBADFD;
+		break;
+	}
+
+
+	mutex_unlock(&ffs->mutex);
+	return ret;
+}
+
+
+
+static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
+				     size_t n)
+{
+	/* We are holding ffs->ev.waitq.lock and ffs->mutex and we need
+	 * to release them. */
+
+	struct usb_functionfs_event events[n];
+	unsigned i = 0;
+
+	memset(events, 0, sizeof events);
+
+	do {
+		events[i].type = ffs->ev.types[i];
+		if (events[i].type == FUNCTIONFS_SETUP) {
+			events[i].u.setup = ffs->ev.setup;
+			ffs->setup_state = FFS_SETUP_PENDING;
+		}
+	} while (++i < n);
+
+	if (n < ffs->ev.count) {
+		ffs->ev.count -= n;
+		memmove(ffs->ev.types, ffs->ev.types + n,
+			ffs->ev.count * sizeof *ffs->ev.types);
+	} else {
+		ffs->ev.count = 0;
+	}
+
+	spin_unlock_irq(&ffs->ev.waitq.lock);
+	mutex_unlock(&ffs->mutex);
+
+	return unlikely(__copy_to_user(buf, events, sizeof events))
+		? -EFAULT : sizeof events;
+}
+
+
+static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
+			    size_t len, loff_t *ptr)
+{
+	struct ffs_data *ffs = file->private_data;
+	char *data = NULL;
+	size_t n;
+	int ret;
+
+	ENTER();
+
+	/* Fast check if setup was canceled */
+	if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED)
+		return -EIDRM;
+
+	/* Acquire mutex */
+	ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
+	if (unlikely(ret < 0))
+		return ret;
+
+
+	/* Check state */
+	if (ffs->state != FFS_ACTIVE) {
+		ret = -EBADFD;
+		goto done_mutex;
+	}
+
+
+	/* We're called from user space, we can use _irq rather then
+	 * _irqsave */
+	spin_lock_irq(&ffs->ev.waitq.lock);
+
+	switch (FFS_SETUP_STATE(ffs)) {
+	case FFS_SETUP_CANCELED:
+		ret = -EIDRM;
+		break;
+
+	case FFS_NO_SETUP:
+		n = len / sizeof(struct usb_functionfs_event);
+		if (unlikely(!n)) {
+			ret = -EINVAL;
+			break;
+		}
+
+		if ((file->f_flags & O_NONBLOCK) && !ffs->ev.count) {
+			ret = -EAGAIN;
+			break;
+		}
+
+		if (unlikely(wait_event_interruptible_exclusive_locked_irq(ffs->ev.waitq, ffs->ev.count))) {
+			ret = -EINTR;
+			break;
+		}
+
+		return __ffs_ep0_read_events(ffs, buf,
+					     min(n, (size_t)ffs->ev.count));
+
+
+	case FFS_SETUP_PENDING:
+		if (ffs->ev.setup.bRequestType & USB_DIR_IN) {
+			ret = __ffs_ep0_stall(ffs);
+			break;
+		}
+
+		len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength));
+
+		spin_unlock_irq(&ffs->ev.waitq.lock);
+
+		if (likely(len)) {
+			data = kmalloc(len, GFP_KERNEL);
+			if (unlikely(!data)) {
+				ret = -ENOMEM;
+				goto done_mutex;
+			}
+		}
+
+		spin_lock_irq(&ffs->ev.waitq.lock);
+
+		/* See ffs_ep0_write() */
+		if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) {
+			ret = -EIDRM;
+			break;
+		}
+
+		/* unlocks spinlock */
+		ret = __ffs_ep0_queue_wait(ffs, data, len);
+		if (likely(ret > 0) && unlikely(__copy_to_user(buf, data, len)))
+			ret = -EFAULT;
+		goto done_mutex;
+
+	default:
+		ret = -EBADFD;
+		break;
+	}
+
+	spin_unlock_irq(&ffs->ev.waitq.lock);
+done_mutex:
+	mutex_unlock(&ffs->mutex);
+	kfree(data);
+	return ret;
+}
+
+
+
+static int ffs_ep0_open(struct inode *inode, struct file *file)
+{
+	struct ffs_data *ffs = inode->i_private;
+
+	ENTER();
+
+	if (unlikely(ffs->state == FFS_CLOSING))
+		return -EBUSY;
+
+	file->private_data = ffs;
+	ffs_data_opened(ffs);
+
+	return 0;
+}
+
+
+static int ffs_ep0_release(struct inode *inode, struct file *file)
+{
+	struct ffs_data *ffs = file->private_data;
+
+	ENTER();
+
+	ffs_data_closed(ffs);
+
+	return 0;
+}
+
+
+static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value)
+{
+	struct ffs_data *ffs = file->private_data;
+	struct usb_gadget *gadget = ffs->gadget;
+	long ret;
+
+	ENTER();
+
+	if (code == FUNCTIONFS_INTERFACE_REVMAP) {
+		struct ffs_function *func = ffs->func;
+		ret = func ? ffs_func_revmap_intf(func, value) : -ENODEV;
+	} else if (gadget->ops->ioctl) {
+		lock_kernel();
+		ret = gadget->ops->ioctl(gadget, code, value);
+		unlock_kernel();
+	} else {
+		ret = -ENOTTY;
+	}
+
+	return ret;
+}
+
+
+static const struct file_operations ffs_ep0_operations = {
+	.owner =	THIS_MODULE,
+	.llseek =	no_llseek,
+
+	.open =		ffs_ep0_open,
+	.write =	ffs_ep0_write,
+	.read =		ffs_ep0_read,
+	.release =	ffs_ep0_release,
+	.unlocked_ioctl =	ffs_ep0_ioctl,
+};
+
+
+/* "Normal" endpoints operations ********************************************/
+
+
+static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req)
+{
+	ENTER();
+	if (likely(req->context)) {
+		struct ffs_ep *ep = _ep->driver_data;
+		ep->status = req->status ? req->status : req->actual;
+		complete(req->context);
+	}
+}
+
+
+static ssize_t ffs_epfile_io(struct file *file,
+			     char __user *buf, size_t len, int read)
+{
+	struct ffs_epfile *epfile = file->private_data;
+	struct ffs_ep *ep;
+	char *data = NULL;
+	ssize_t ret;
+	int halt;
+
+	goto first_try;
+	do {
+		spin_unlock_irq(&epfile->ffs->eps_lock);
+		mutex_unlock(&epfile->mutex);
+
+first_try:
+		/* Are we still active? */
+		if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) {
+			ret = -ENODEV;
+			goto error;
+		}
+
+		/* Wait for endpoint to be enabled */
+		ep = epfile->ep;
+		if (!ep) {
+			if (file->f_flags & O_NONBLOCK) {
+				ret = -EAGAIN;
+				goto error;
+			}
+
+			if (unlikely(wait_event_interruptible
+				     (epfile->wait, (ep = epfile->ep)))) {
+				ret = -EINTR;
+				goto error;
+			}
+		}
+
+		/* Do we halt? */
+		halt = !read == !epfile->in;
+		if (halt && epfile->isoc) {
+			ret = -EINVAL;
+			goto error;
+		}
+
+		/* Allocate & copy */
+		if (!halt && !data) {
+			data = kzalloc(len, GFP_KERNEL);
+			if (unlikely(!data))
+				return -ENOMEM;
+
+			if (!read &&
+			    unlikely(__copy_from_user(data, buf, len))) {
+				ret = -EFAULT;
+				goto error;
+			}
+		}
+
+		/* We will be using request */
+		ret = ffs_mutex_lock(&epfile->mutex,
+				     file->f_flags & O_NONBLOCK);
+		if (unlikely(ret))
+			goto error;
+
+		/* We're called from user space, we can use _irq rather then
+		 * _irqsave */
+		spin_lock_irq(&epfile->ffs->eps_lock);
+
+		/* While we were acquiring mutex endpoint got disabled
+		 * or changed? */
+	} while (unlikely(epfile->ep != ep));
+
+	/* Halt */
+	if (unlikely(halt)) {
+		if (likely(epfile->ep == ep) && !WARN_ON(!ep->ep))
+			usb_ep_set_halt(ep->ep);
+		spin_unlock_irq(&epfile->ffs->eps_lock);
+		ret = -EBADMSG;
+	} else {
+		/* Fire the request */
+		DECLARE_COMPLETION_ONSTACK(done);
+
+		struct usb_request *req = ep->req;
+		req->context  = &done;
+		req->complete = ffs_epfile_io_complete;
+		req->buf      = data;
+		req->length   = len;
+
+		ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
+
+		spin_unlock_irq(&epfile->ffs->eps_lock);
+
+		if (unlikely(ret < 0)) {
+			/* nop */
+		} else if (unlikely(wait_for_completion_interruptible(&done))) {
+			ret = -EINTR;
+			usb_ep_dequeue(ep->ep, req);
+		} else {
+			ret = ep->status;
+			if (read && ret > 0 &&
+			    unlikely(copy_to_user(buf, data, ret)))
+				ret = -EFAULT;
+		}
+	}
+
+	mutex_unlock(&epfile->mutex);
+error:
+	kfree(data);
+	return ret;
+}
+
+
+static ssize_t
+ffs_epfile_write(struct file *file, const char __user *buf, size_t len,
+		 loff_t *ptr)
+{
+	ENTER();
+
+	return ffs_epfile_io(file, (char __user *)buf, len, 0);
+}
+
+static ssize_t
+ffs_epfile_read(struct file *file, char __user *buf, size_t len, loff_t *ptr)
+{
+	ENTER();
+
+	return ffs_epfile_io(file, buf, len, 1);
+}
+
+static int
+ffs_epfile_open(struct inode *inode, struct file *file)
+{
+	struct ffs_epfile *epfile = inode->i_private;
+
+	ENTER();
+
+	if (WARN_ON(epfile->ffs->state != FFS_ACTIVE))
+		return -ENODEV;
+
+	file->private_data = epfile;
+	ffs_data_opened(epfile->ffs);
+
+	return 0;
+}
+
+static int
+ffs_epfile_release(struct inode *inode, struct file *file)
+{
+	struct ffs_epfile *epfile = inode->i_private;
+
+	ENTER();
+
+	ffs_data_closed(epfile->ffs);
+
+	return 0;
+}
+
+
+static long ffs_epfile_ioctl(struct file *file, unsigned code,
+			     unsigned long value)
+{
+	struct ffs_epfile *epfile = file->private_data;
+	int ret;
+
+	ENTER();
+
+	if (WARN_ON(epfile->ffs->state != FFS_ACTIVE))
+		return -ENODEV;
+
+	spin_lock_irq(&epfile->ffs->eps_lock);
+	if (likely(epfile->ep)) {
+		switch (code) {
+		case FUNCTIONFS_FIFO_STATUS:
+			ret = usb_ep_fifo_status(epfile->ep->ep);
+			break;
+		case FUNCTIONFS_FIFO_FLUSH:
+			usb_ep_fifo_flush(epfile->ep->ep);
+			ret = 0;
+			break;
+		case FUNCTIONFS_CLEAR_HALT:
+			ret = usb_ep_clear_halt(epfile->ep->ep);
+			break;
+		case FUNCTIONFS_ENDPOINT_REVMAP:
+			ret = epfile->ep->num;
+			break;
+		default:
+			ret = -ENOTTY;
+		}
+	} else {
+		ret = -ENODEV;
+	}
+	spin_unlock_irq(&epfile->ffs->eps_lock);
+
+	return ret;
+}
+
+
+static const struct file_operations ffs_epfile_operations = {
+	.owner =	THIS_MODULE,
+	.llseek =	no_llseek,
+
+	.open =		ffs_epfile_open,
+	.write =	ffs_epfile_write,
+	.read =		ffs_epfile_read,
+	.release =	ffs_epfile_release,
+	.unlocked_ioctl =	ffs_epfile_ioctl,
+};
+
+
+
+/* File system and super block operations ***********************************/
+
+/*
+ * Mounting the filesystem creates a controller file, used first for
+ * function configuration then later for event monitoring.
+ */
+
+
+static struct inode *__must_check
+ffs_sb_make_inode(struct super_block *sb, void *data,
+		  const struct file_operations *fops,
+		  const struct inode_operations *iops,
+		  struct ffs_file_perms *perms)
+{
+	struct inode *inode;
+
+	ENTER();
+
+	inode = new_inode(sb);
+
+	if (likely(inode)) {
+		struct timespec current_time = CURRENT_TIME;
+
+		inode->i_mode    = perms->mode;
+		inode->i_uid     = perms->uid;
+		inode->i_gid     = perms->gid;
+		inode->i_atime   = current_time;
+		inode->i_mtime   = current_time;
+		inode->i_ctime   = current_time;
+		inode->i_private = data;
+		if (fops)
+			inode->i_fop = fops;
+		if (iops)
+			inode->i_op  = iops;
+	}
+
+	return inode;
+}
+
+
+/* Create "regular" file */
+
+static struct inode *ffs_sb_create_file(struct super_block *sb,
+					const char *name, void *data,
+					const struct file_operations *fops,
+					struct dentry **dentry_p)
+{
+	struct ffs_data	*ffs = sb->s_fs_info;
+	struct dentry	*dentry;
+	struct inode	*inode;
+
+	ENTER();
+
+	dentry = d_alloc_name(sb->s_root, name);
+	if (unlikely(!dentry))
+		return NULL;
+
+	inode = ffs_sb_make_inode(sb, data, fops, NULL, &ffs->file_perms);
+	if (unlikely(!inode)) {
+		dput(dentry);
+		return NULL;
+	}
+
+	d_add(dentry, inode);
+	if (dentry_p)
+		*dentry_p = dentry;
+
+	return inode;
+}
+
+
+/* Super block */
+
+static const struct super_operations ffs_sb_operations = {
+	.statfs =	simple_statfs,
+	.drop_inode =	generic_delete_inode,
+};
+
+struct ffs_sb_fill_data {
+	struct ffs_file_perms perms;
+	umode_t root_mode;
+	const char *dev_name;
+};
+
+static int ffs_sb_fill(struct super_block *sb, void *_data, int silent)
+{
+	struct ffs_sb_fill_data *data = _data;
+	struct inode	*inode;
+	struct dentry	*d;
+	struct ffs_data	*ffs;
+
+	ENTER();
+
+	/* Initialize data */
+	ffs = ffs_data_new();
+	if (unlikely(!ffs))
+		goto enomem0;
+
+	ffs->sb              = sb;
+	ffs->dev_name        = data->dev_name;
+	ffs->file_perms      = data->perms;
+
+	sb->s_fs_info        = ffs;
+	sb->s_blocksize      = PAGE_CACHE_SIZE;
+	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+	sb->s_magic          = FUNCTIONFS_MAGIC;
+	sb->s_op             = &ffs_sb_operations;
+	sb->s_time_gran      = 1;
+
+	/* Root inode */
+	data->perms.mode = data->root_mode;
+	inode = ffs_sb_make_inode(sb, NULL,
+				  &simple_dir_operations,
+				  &simple_dir_inode_operations,
+				  &data->perms);
+	if (unlikely(!inode))
+		goto enomem1;
+	d = d_alloc_root(inode);
+	if (unlikely(!d))
+		goto enomem2;
+	sb->s_root = d;
+
+	/* EP0 file */
+	if (unlikely(!ffs_sb_create_file(sb, "ep0", ffs,
+					 &ffs_ep0_operations, NULL)))
+		goto enomem3;
+
+	return 0;
+
+enomem3:
+	dput(d);
+enomem2:
+	iput(inode);
+enomem1:
+	ffs_data_put(ffs);
+enomem0:
+	return -ENOMEM;
+}
+
+
+static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
+{
+	ENTER();
+
+	if (!opts || !*opts)
+		return 0;
+
+	for (;;) {
+		char *end, *eq, *comma;
+		unsigned long value;
+
+		/* Option limit */
+		comma = strchr(opts, ',');
+		if (comma)
+			*comma = 0;
+
+		/* Value limit */
+		eq = strchr(opts, '=');
+		if (unlikely(!eq)) {
+			FERR("'=' missing in %s", opts);
+			return -EINVAL;
+		}
+		*eq = 0;
+
+		/* Parse value */
+		value = simple_strtoul(eq + 1, &end, 0);
+		if (unlikely(*end != ',' && *end != 0)) {
+			FERR("%s: invalid value: %s", opts, eq + 1);
+			return -EINVAL;
+		}
+
+		/* Interpret option */
+		switch (eq - opts) {
+		case 5:
+			if (!memcmp(opts, "rmode", 5))
+				data->root_mode  = (value & 0555) | S_IFDIR;
+			else if (!memcmp(opts, "fmode", 5))
+				data->perms.mode = (value & 0666) | S_IFREG;
+			else
+				goto invalid;
+			break;
+
+		case 4:
+			if (!memcmp(opts, "mode", 4)) {
+				data->root_mode  = (value & 0555) | S_IFDIR;
+				data->perms.mode = (value & 0666) | S_IFREG;
+			} else {
+				goto invalid;
+			}
+			break;
+
+		case 3:
+			if (!memcmp(opts, "uid", 3))
+				data->perms.uid = value;
+			else if (!memcmp(opts, "gid", 3))
+				data->perms.gid = value;
+			else
+				goto invalid;
+			break;
+
+		default:
+invalid:
+			FERR("%s: invalid option", opts);
+			return -EINVAL;
+		}
+
+		/* Next iteration */
+		if (!comma)
+			break;
+		opts = comma + 1;
+	}
+
+	return 0;
+}
+
+
+/* "mount -t functionfs dev_name /dev/function" ends up here */
+
+static int
+ffs_fs_get_sb(struct file_system_type *t, int flags,
+	      const char *dev_name, void *opts, struct vfsmount *mnt)
+{
+	struct ffs_sb_fill_data data = {
+		.perms = {
+			.mode = S_IFREG | 0600,
+			.uid = 0,
+			.gid = 0
+		},
+		.root_mode = S_IFDIR | 0500,
+	};
+	int ret;
+
+	ENTER();
+
+	ret = functionfs_check_dev_callback(dev_name);
+	if (unlikely(ret < 0))
+		return ret;
+
+	ret = ffs_fs_parse_opts(&data, opts);
+	if (unlikely(ret < 0))
+		return ret;
+
+	data.dev_name = dev_name;
+	return get_sb_single(t, flags, &data, ffs_sb_fill, mnt);
+}
+
+static void
+ffs_fs_kill_sb(struct super_block *sb)
+{
+	void *ptr;
+
+	ENTER();
+
+	kill_litter_super(sb);
+	ptr = xchg(&sb->s_fs_info, NULL);
+	if (ptr)
+		ffs_data_put(ptr);
+}
+
+static struct file_system_type ffs_fs_type = {
+	.owner		= THIS_MODULE,
+	.name		= "functionfs",
+	.get_sb		= ffs_fs_get_sb,
+	.kill_sb	= ffs_fs_kill_sb,
+};
+
+
+
+/* Driver's main init/cleanup functions *************************************/
+
+
+static int functionfs_init(void)
+{
+	int ret;
+
+	ENTER();
+
+	ret = register_filesystem(&ffs_fs_type);
+	if (likely(!ret))
+		FINFO("file system registered");
+	else
+		FERR("failed registering file system (%d)", ret);
+
+	return ret;
+}
+
+static void functionfs_cleanup(void)
+{
+	ENTER();
+
+	FINFO("unloading");
+	unregister_filesystem(&ffs_fs_type);
+}
+
+
+
+/* ffs_data and ffs_function construction and destruction code **************/
+
+static void ffs_data_clear(struct ffs_data *ffs);
+static void ffs_data_reset(struct ffs_data *ffs);
+
+
+static void ffs_data_get(struct ffs_data *ffs)
+{
+	ENTER();
+
+	atomic_inc(&ffs->ref);
+}
+
+static void ffs_data_opened(struct ffs_data *ffs)
+{
+	ENTER();
+
+	atomic_inc(&ffs->ref);
+	atomic_inc(&ffs->opened);
+}
+
+static void ffs_data_put(struct ffs_data *ffs)
+{
+	ENTER();
+
+	if (unlikely(atomic_dec_and_test(&ffs->ref))) {
+		FINFO("%s(): freeing", __func__);
+		ffs_data_clear(ffs);
+		BUG_ON(mutex_is_locked(&ffs->mutex) ||
+		       spin_is_locked(&ffs->ev.waitq.lock) ||
+		       waitqueue_active(&ffs->ev.waitq) ||
+		       waitqueue_active(&ffs->ep0req_completion.wait));
+		kfree(ffs);
+	}
+}
+
+
+
+static void ffs_data_closed(struct ffs_data *ffs)
+{
+	ENTER();
+
+	if (atomic_dec_and_test(&ffs->opened)) {
+		ffs->state = FFS_CLOSING;
+		ffs_data_reset(ffs);
+	}
+
+	ffs_data_put(ffs);
+}
+
+
+static struct ffs_data *ffs_data_new(void)
+{
+	struct ffs_data *ffs = kzalloc(sizeof *ffs, GFP_KERNEL);
+	if (unlikely(!ffs))
+		return 0;
+
+	ENTER();
+
+	atomic_set(&ffs->ref, 1);
+	atomic_set(&ffs->opened, 0);
+	ffs->state = FFS_READ_DESCRIPTORS;
+	mutex_init(&ffs->mutex);
+	spin_lock_init(&ffs->eps_lock);
+	init_waitqueue_head(&ffs->ev.waitq);
+	init_completion(&ffs->ep0req_completion);
+
+	/* XXX REVISIT need to update it in some places, or do we? */
+	ffs->ev.can_stall = 1;
+
+	return ffs;
+}
+
+
+static void ffs_data_clear(struct ffs_data *ffs)
+{
+	ENTER();
+
+	if (test_and_clear_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags))
+		functionfs_closed_callback(ffs);
+
+	BUG_ON(ffs->gadget);
+
+	if (ffs->epfiles)
+		ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count);
+
+	kfree(ffs->raw_descs);
+	kfree(ffs->raw_strings);
+	kfree(ffs->stringtabs);
+}
+
+
+static void ffs_data_reset(struct ffs_data *ffs)
+{
+	ENTER();
+
+	ffs_data_clear(ffs);
+
+	ffs->epfiles = NULL;
+	ffs->raw_descs = NULL;
+	ffs->raw_strings = NULL;
+	ffs->stringtabs = NULL;
+
+	ffs->raw_descs_length = 0;
+	ffs->raw_fs_descs_length = 0;
+	ffs->fs_descs_count = 0;
+	ffs->hs_descs_count = 0;
+
+	ffs->strings_count = 0;
+	ffs->interfaces_count = 0;
+	ffs->eps_count = 0;
+
+	ffs->ev.count = 0;
+
+	ffs->state = FFS_READ_DESCRIPTORS;
+	ffs->setup_state = FFS_NO_SETUP;
+	ffs->flags = 0;
+}
+
+
+static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev)
+{
+	unsigned i, count;
+
+	ENTER();
+
+	if (WARN_ON(ffs->state != FFS_ACTIVE
+		 || test_and_set_bit(FFS_FL_BOUND, &ffs->flags)))
+		return -EBADFD;
+
+	ffs_data_get(ffs);
+
+	ffs->gadget = cdev->gadget;
+	ffs->ep0req = usb_ep_alloc_request(ffs->gadget->ep0, GFP_KERNEL);
+	if (unlikely(!ffs->ep0req))
+		return -ENOMEM;
+	ffs->ep0req->complete = ffs_ep0_complete;
+	ffs->ep0req->context = ffs;
+
+	/* Get strings identifiers */
+	for (count = ffs->strings_count, i = 0; i < count; ++i) {
+		struct usb_gadget_strings **lang;
+
+		int id = usb_string_id(cdev);
+		if (unlikely(id < 0))
+			return id;
+
+		lang = ffs->stringtabs;
+		do {
+			(*lang)->strings[i].id = id;
+			++lang;
+		} while (*lang);
+	}
+
+	return 0;
+}
+
+
+static void functionfs_unbind(struct ffs_data *ffs)
+{
+	ENTER();
+
+	if (!WARN_ON(!ffs->gadget)) {
+		usb_ep_free_request(ffs->gadget->ep0, ffs->ep0req);
+		ffs->ep0req = NULL;
+		ffs->gadget = NULL;
+		ffs_data_put(ffs);
+	}
+}
+
+
+static int ffs_epfiles_create(struct ffs_data *ffs)
+{
+	struct ffs_epfile *epfile, *epfiles;
+	unsigned i, count;
+
+	ENTER();
+
+	count = ffs->eps_count;
+	epfiles = kzalloc(count * sizeof *epfiles, GFP_KERNEL);
+	if (!epfiles)
+		return -ENOMEM;
+
+	epfile = epfiles;
+	for (i = 1; i <= count; ++i, ++epfile) {
+		epfile->ffs = ffs;
+		mutex_init(&epfile->mutex);
+		init_waitqueue_head(&epfile->wait);
+		sprintf(epfiles->name, "ep%u",  i);
+		if (!unlikely(ffs_sb_create_file(ffs->sb, epfiles->name, epfile,
+						 &ffs_epfile_operations,
+						 &epfile->dentry))) {
+			ffs_epfiles_destroy(epfiles, i - 1);
+			return -ENOMEM;
+		}
+	}
+
+	ffs->epfiles = epfiles;
+	return 0;
+}
+
+
+static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
+{
+	struct ffs_epfile *epfile = epfiles;
+
+	ENTER();
+
+	for (; count; --count, ++epfile) {
+		BUG_ON(mutex_is_locked(&epfile->mutex) ||
+		       waitqueue_active(&epfile->wait));
+		if (epfile->dentry) {
+			d_delete(epfile->dentry);
+			dput(epfile->dentry);
+			epfile->dentry = NULL;
+		}
+	}
+
+	kfree(epfiles);
+}
+
+
+static int functionfs_add(struct usb_composite_dev *cdev,
+			  struct usb_configuration *c,
+			  struct ffs_data *ffs)
+{
+	struct ffs_function *func;
+	int ret;
+
+	ENTER();
+
+	func = kzalloc(sizeof *func, GFP_KERNEL);
+	if (unlikely(!func))
+		return -ENOMEM;
+
+	func->function.name    = "Function FS Gadget";
+	func->function.strings = ffs->stringtabs;
+
+	func->function.bind    = ffs_func_bind;
+	func->function.unbind  = ffs_func_unbind;
+	func->function.set_alt = ffs_func_set_alt;
+	/*func->function.get_alt = ffs_func_get_alt;*/
+	func->function.disable = ffs_func_disable;
+	func->function.setup   = ffs_func_setup;
+	func->function.suspend = ffs_func_suspend;
+	func->function.resume  = ffs_func_resume;
+
+	func->conf   = c;
+	func->gadget = cdev->gadget;
+	func->ffs = ffs;
+	ffs_data_get(ffs);
+
+	ret = usb_add_function(c, &func->function);
+	if (unlikely(ret))
+		ffs_func_free(func);
+
+	return ret;
+}
+
+static void ffs_func_free(struct ffs_function *func)
+{
+	ENTER();
+
+	ffs_data_put(func->ffs);
+
+	kfree(func->eps);
+	/* eps and interfaces_nums are allocated in the same chunk so
+	 * only one free is required.  Descriptors are also allocated
+	 * in the same chunk. */
+
+	kfree(func);
+}
+
+
+static void ffs_func_eps_disable(struct ffs_function *func)
+{
+	struct ffs_ep *ep         = func->eps;
+	struct ffs_epfile *epfile = func->ffs->epfiles;
+	unsigned count            = func->ffs->eps_count;
+	unsigned long flags;
+
+	spin_lock_irqsave(&func->ffs->eps_lock, flags);
+	do {
+		/* pending requests get nuked */
+		if (likely(ep->ep))
+			usb_ep_disable(ep->ep);
+		epfile->ep = NULL;
+
+		++ep;
+		++epfile;
+	} while (--count);
+	spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
+}
+
+static int ffs_func_eps_enable(struct ffs_function *func)
+{
+	struct ffs_data *ffs      = func->ffs;
+	struct ffs_ep *ep         = func->eps;
+	struct ffs_epfile *epfile = ffs->epfiles;
+	unsigned count            = ffs->eps_count;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&func->ffs->eps_lock, flags);
+	do {
+		struct usb_endpoint_descriptor *ds;
+		ds = ep->descs[ep->descs[1] ? 1 : 0];
+
+		ep->ep->driver_data = ep;
+		ret = usb_ep_enable(ep->ep, ds);
+		if (likely(!ret)) {
+			epfile->ep = ep;
+			epfile->in = usb_endpoint_dir_in(ds);
+			epfile->isoc = usb_endpoint_xfer_isoc(ds);
+		} else {
+			break;
+		}
+
+		wake_up(&epfile->wait);
+
+		++ep;
+		++epfile;
+	} while (--count);
+	spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
+
+	return ret;
+}
+
+
+/* Parsing and building descriptors and strings *****************************/
+
+
+/* This validates if data pointed by data is a valid USB descriptor as
+ * well as record how many interfaces, endpoints and strings are
+ * required by given configuration.  Returns address afther the
+ * descriptor or NULL if data is invalid. */
+
+enum ffs_entity_type {
+	FFS_DESCRIPTOR, FFS_INTERFACE, FFS_STRING, FFS_ENDPOINT
+};
+
+typedef int (*ffs_entity_callback)(enum ffs_entity_type entity,
+				   u8 *valuep,
+				   struct usb_descriptor_header *desc,
+				   void *priv);
+
+static int __must_check ffs_do_desc(char *data, unsigned len,
+				    ffs_entity_callback entity, void *priv)
+{
+	struct usb_descriptor_header *_ds = (void *)data;
+	u8 length;
+	int ret;
+
+	ENTER();
+
+	/* At least two bytes are required: length and type */
+	if (len < 2)
+		return -EINVAL;
+
+	/* If we have at least as many bytes as the descriptor takes? */
+	length = _ds->bLength;
+	if (len < length)
+		return -EINVAL;
+
+#define __entity_check_INTERFACE(val)  1
+#define __entity_check_STRING(val)     (val)
+#define __entity_check_ENDPOINT(val)   ((val) & USB_ENDPOINT_NUMBER_MASK)
+#define __entity(type, val) do {					\
+		FVDBG("entity " #type "(%02x)", (val));			\
+		if (unlikely(!__entity_check_ ##type(val)))		\
+			return -EINVAL;					\
+		ret = entity(FFS_ ##type, &val, _ds, priv);		\
+		if (unlikely(ret < 0)) {				\
+			FDBG("entity " #type "(%02x); ret = %d",	\
+			     (val), ret);				\
+			return ret;					\
+		}							\
+	} while (0)
+
+	/* Parse descriptor depending on type. */
+	switch (_ds->bDescriptorType) {
+	case USB_DT_DEVICE:
+	case USB_DT_CONFIG:
+	case USB_DT_STRING:
+	case USB_DT_DEVICE_QUALIFIER:
+		/* function can't have any of those */
+		return -EINVAL;
+
+	case USB_DT_INTERFACE: {
+		struct usb_interface_descriptor *ds = (void *)_ds;
+		FVDBG("interface descriptor");
+		if (length != sizeof *ds)
+			return -EINVAL;
+
+		__entity(INTERFACE, ds->bInterfaceNumber);
+		if (ds->iInterface)
+			__entity(STRING, ds->iInterface);
+	}
+		break;
+
+	case USB_DT_ENDPOINT: {
+		struct usb_endpoint_descriptor *ds = (void *)_ds;
+		FVDBG("endpoint descriptor");
+		if (length != USB_DT_ENDPOINT_SIZE &&
+		    length != USB_DT_ENDPOINT_AUDIO_SIZE)
+			return -EINVAL;
+		if (!(ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK))
+			return -EINVAL;
+		__entity(ENDPOINT, ds->bEndpointAddress);
+	}
+		break;
+
+	case USB_DT_OTHER_SPEED_CONFIG:
+		/* TODO */
+		return -EINVAL;
+
+	case USB_DT_INTERFACE_POWER:
+		/* TODO */
+		return -EINVAL;
+
+	case USB_DT_OTG:
+		if (length != sizeof(struct usb_otg_descriptor))
+			return -EINVAL;
+		break;
+
+	case USB_DT_DEBUG:
+		/* TODO */
+		return -EINVAL;
+
+	case USB_DT_INTERFACE_ASSOCIATION: {
+		struct usb_interface_assoc_descriptor *ds = (void *)_ds;
+		FVDBG("interface association descriptor");
+		if (length != sizeof *ds)
+			return -EINVAL;
+		if (ds->iFunction)
+			__entity(STRING, ds->iFunction);
+	}
+		break;
+
+	case USB_DT_SECURITY:
+		/* TODO */
+		return -EINVAL;
+
+	case USB_DT_KEY:
+		/* TODO */
+		return -EINVAL;
+
+	case USB_DT_ENCRYPTION_TYPE:
+		/* TODO */
+		return -EINVAL;
+
+	case USB_DT_BOS:
+		/* TODO */
+		return -EINVAL;
+
+	case USB_DT_DEVICE_CAPABILITY:
+		/* TODO */
+		return -EINVAL;
+
+	case USB_DT_WIRELESS_ENDPOINT_COMP:
+		/* TODO */
+		return -EINVAL;
+
+	case USB_DT_WIRE_ADAPTER:
+		/* TODO */
+		return -EINVAL;
+
+	case USB_DT_RPIPE:
+		/* TODO */
+		return -EINVAL;
+
+	case USB_DT_CS_RADIO_CONTROL:
+		/* TODO */
+		return -EINVAL;
+
+	default:
+		/* We should never be here */
+		return -EINVAL;
+	}
+
+#undef __entity
+#undef __entity_check_DESCRIPTOR
+#undef __entity_check_INTERFACE
+#undef __entity_check_STRING
+#undef __entity_check_ENDPOINT
+
+	return length;
+}
+
+
+static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
+				     ffs_entity_callback entity, void *priv)
+{
+	const unsigned _len = len;
+	unsigned long num = 0;
+
+	ENTER();
+
+	for (;;) {
+		int ret;
+
+		if (num == count)
+			data = NULL;
+
+		/* Record "descriptor" entitny */
+		ret = entity(FFS_DESCRIPTOR, (u8 *)num, (void *)data, priv);
+		if (unlikely(ret < 0)) {
+			FDBG("entity DESCRIPTOR(%02lx); ret = %d", num, ret);
+			return ret;
+		}
+
+		if (!data)
+			return _len - len;
+
+		ret = ffs_do_desc(data, len, entity, priv);
+		if (unlikely(ret < 0)) {
+			FDBG("%s returns %d", __func__, ret);
+			return ret;
+		}
+
+		len -= ret;
+		data += ret;
+		++num;
+	}
+}
+
+
+static int __ffs_data_do_entity(enum ffs_entity_type type,
+				u8 *valuep, struct usb_descriptor_header *desc,
+				void *priv)
+{
+	struct ffs_data *ffs = priv;
+
+	ENTER();
+
+	switch (type) {
+	case FFS_DESCRIPTOR:
+		break;
+
+	case FFS_INTERFACE:
+		/* Interfaces are indexed from zero so if we
+		 * encountered interface "n" then there are at least
+		 * "n+1" interfaces. */
+		if (*valuep >= ffs->interfaces_count)
+			ffs->interfaces_count = *valuep + 1;
+		break;
+
+	case FFS_STRING:
+		/* Strings are indexed from 1 (0 is magic ;) reserved
+		 * for languages list or some such) */
+		if (*valuep > ffs->strings_count)
+			ffs->strings_count = *valuep;
+		break;
+
+	case FFS_ENDPOINT:
+		/* Endpoints are indexed from 1 as well. */
+		if ((*valuep & USB_ENDPOINT_NUMBER_MASK) > ffs->eps_count)
+			ffs->eps_count = (*valuep & USB_ENDPOINT_NUMBER_MASK);
+		break;
+	}
+
+	return 0;
+}
+
+
+static int __ffs_data_got_descs(struct ffs_data *ffs,
+				char *const _data, size_t len)
+{
+	unsigned fs_count, hs_count;
+	int fs_len, ret = -EINVAL;
+	char *data = _data;
+
+	ENTER();
+
+	if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_DESCRIPTORS_MAGIC ||
+		     get_unaligned_le32(data + 4) != len))
+		goto error;
+	fs_count = get_unaligned_le32(data +  8);
+	hs_count = get_unaligned_le32(data + 12);
+
+	if (!fs_count && !hs_count)
+		goto einval;
+
+	data += 16;
+	len  -= 16;
+
+	if (likely(fs_count)) {
+		fs_len = ffs_do_descs(fs_count, data, len,
+				      __ffs_data_do_entity, ffs);
+		if (unlikely(fs_len < 0)) {
+			ret = fs_len;
+			goto error;
+		}
+
+		data += fs_len;
+		len  -= fs_len;
+	} else {
+		fs_len = 0;
+	}
+
+	if (likely(hs_count)) {
+		ret = ffs_do_descs(hs_count, data, len,
+				   __ffs_data_do_entity, ffs);
+		if (unlikely(ret < 0))
+			goto error;
+	} else {
+		ret = 0;
+	}
+
+	if (unlikely(len != ret))
+		goto einval;
+
+	ffs->raw_fs_descs_length = fs_len;
+	ffs->raw_descs_length    = fs_len + ret;
+	ffs->raw_descs           = _data;
+	ffs->fs_descs_count      = fs_count;
+	ffs->hs_descs_count      = hs_count;
+
+	return 0;
+
+einval:
+	ret = -EINVAL;
+error:
+	kfree(_data);
+	return ret;
+}
+
+
+
+static int __ffs_data_got_strings(struct ffs_data *ffs,
+				  char *const _data, size_t len)
+{
+	u32 str_count, needed_count, lang_count;
+	struct usb_gadget_strings **stringtabs, *t;
+	struct usb_string *strings, *s;
+	const char *data = _data;
+
+	ENTER();
+
+	if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_STRINGS_MAGIC ||
+		     get_unaligned_le32(data + 4) != len))
+		goto error;
+	str_count  = get_unaligned_le32(data + 8);
+	lang_count = get_unaligned_le32(data + 12);
+
+	/* if one is zero the other must be zero */
+	if (unlikely(!str_count != !lang_count))
+		goto error;
+
+	/* Do we have at least as many strings as descriptors need? */
+	needed_count = ffs->strings_count;
+	if (unlikely(str_count < needed_count))
+		goto error;
+
+	/* If we don't need any strings just return and free all
+	 * memory */
+	if (!needed_count) {
+		kfree(_data);
+		return 0;
+	}
+
+	/* Allocate */
+	{
+		/* Allocate everything in one chunk so there's less
+		 * maintanance. */
+		struct {
+			struct usb_gadget_strings *stringtabs[lang_count + 1];
+			struct usb_gadget_strings stringtab[lang_count];
+			struct usb_string strings[lang_count*(needed_count+1)];
+		} *d;
+		unsigned i = 0;
+
+		d = kmalloc(sizeof *d, GFP_KERNEL);
+		if (unlikely(!d)) {
+			kfree(_data);
+			return -ENOMEM;
+		}
+
+		stringtabs = d->stringtabs;
+		t = d->stringtab;
+		i = lang_count;
+		do {
+			*stringtabs++ = t++;
+		} while (--i);
+		*stringtabs = NULL;
+
+		stringtabs = d->stringtabs;
+		t = d->stringtab;
+		s = d->strings;
+		strings = s;
+	}
+
+	/* For each language */
+	data += 16;
+	len -= 16;
+
+	do { /* lang_count > 0 so we can use do-while */
+		unsigned needed = needed_count;
+
+		if (unlikely(len < 3))
+			goto error_free;
+		t->language = get_unaligned_le16(data);
+		t->strings  = s;
+		++t;
+
+		data += 2;
+		len -= 2;
+
+		/* For each string */
+		do { /* str_count > 0 so we can use do-while */
+			size_t length = strnlen(data, len);
+
+			if (unlikely(length == len))
+				goto error_free;
+
+			/* user may provide more strings then we need,
+			 * if that's the case we simply ingore the
+			 * rest */
+			if (likely(needed)) {
+				/* s->id will be set while adding
+				 * function to configuration so for
+				 * now just leave garbage here. */
+				s->s = data;
+				--needed;
+				++s;
+			}
+
+			data += length + 1;
+			len -= length + 1;
+		} while (--str_count);
+
+		s->id = 0;   /* terminator */
+		s->s = NULL;
+		++s;
+
+	} while (--lang_count);
+
+	/* Some garbage left? */
+	if (unlikely(len))
+		goto error_free;
+
+	/* Done! */
+	ffs->stringtabs = stringtabs;
+	ffs->raw_strings = _data;
+
+	return 0;
+
+error_free:
+	kfree(stringtabs);
+error:
+	kfree(_data);
+	return -EINVAL;
+}
+
+
+
+
+/* Events handling and management *******************************************/
+
+static void __ffs_event_add(struct ffs_data *ffs,
+			    enum usb_functionfs_event_type type)
+{
+	enum usb_functionfs_event_type rem_type1 = type;
+	enum usb_functionfs_event_type rem_type2 = type;
+	int neg = 0;
+
+	/* Abort any unhandled setup */
+	if (ffs->setup_state == FFS_SETUP_PENDING)
+		ffs->setup_state = FFS_SETUP_CANCELED;
+
+	switch (type) {
+	case FUNCTIONFS_RESUME:
+		rem_type2 = FUNCTIONFS_SUSPEND;
+		/* FALL THGOUTH */
+	case FUNCTIONFS_SUSPEND:
+	case FUNCTIONFS_SETUP:
+		/* discard all similar events */
+		break;
+
+	case FUNCTIONFS_BIND:
+	case FUNCTIONFS_UNBIND:
+	case FUNCTIONFS_DISABLE:
+	case FUNCTIONFS_ENABLE:
+		/* discard everything other then power management. */
+		rem_type1 = FUNCTIONFS_SUSPEND;
+		rem_type2 = FUNCTIONFS_RESUME;
+		neg = 1;
+		break;
+
+	default:
+		BUG();
+	}
+
+	{
+		u8 *ev  = ffs->ev.types, *out = ev;
+		unsigned n = ffs->ev.count;
+		for (; n; --n, ++ev)
+			if ((*ev == rem_type1 || *ev == rem_type2) == neg)
+				*out++ = *ev;
+		ffs->ev.count = out - ffs->ev.types;
+	}
+
+	ffs->ev.types[ffs->ev.count++] = type;
+	wake_up_locked(&ffs->ev.waitq);
+}
+
+static void ffs_event_add(struct ffs_data *ffs,
+			  enum usb_functionfs_event_type type)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&ffs->ev.waitq.lock, flags);
+	__ffs_event_add(ffs, type);
+	spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags);
+}
+
+
+/* Bind/unbind USB function hooks *******************************************/
+
+static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
+				    struct usb_descriptor_header *desc,
+				    void *priv)
+{
+	struct usb_endpoint_descriptor *ds = (void *)desc;
+	struct ffs_function *func = priv;
+	struct ffs_ep *ffs_ep;
+
+	/* If hs_descriptors is not NULL then we are reading hs
+	 * descriptors now */
+	const int isHS = func->function.hs_descriptors != NULL;
+	unsigned idx;
+
+	if (type != FFS_DESCRIPTOR)
+		return 0;
+
+	if (isHS)
+		func->function.hs_descriptors[(long)valuep] = desc;
+	else
+		func->function.descriptors[(long)valuep]    = desc;
+
+	if (!desc || desc->bDescriptorType != USB_DT_ENDPOINT)
+		return 0;
+
+	idx = (ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) - 1;
+	ffs_ep = func->eps + idx;
+
+	if (unlikely(ffs_ep->descs[isHS]))
+		return -EINVAL;
+	ffs_ep->descs[isHS] = ds;
+
+	ffs_dump_mem(": Original  ep desc", ds, ds->bLength);
+	if (ffs_ep->ep) {
+		ds->bEndpointAddress = ffs_ep->descs[0]->bEndpointAddress;
+		if (!ds->wMaxPacketSize)
+			ds->wMaxPacketSize = ffs_ep->descs[0]->wMaxPacketSize;
+	} else {
+		struct usb_request *req;
+		struct usb_ep *ep;
+
+		FVDBG("autoconfig");
+		ep = usb_ep_autoconfig(func->gadget, ds);
+		if (unlikely(!ep))
+			return -ENOTSUPP;
+		ep->driver_data = func->eps + idx;;
+
+		req = usb_ep_alloc_request(ep, GFP_KERNEL);
+		if (unlikely(!req))
+			return -ENOMEM;
+
+		ffs_ep->ep  = ep;
+		ffs_ep->req = req;
+		func->eps_revmap[ds->bEndpointAddress &
+				 USB_ENDPOINT_NUMBER_MASK] = idx + 1;
+	}
+	ffs_dump_mem(": Rewritten ep desc", ds, ds->bLength);
+
+	return 0;
+}
+
+
+static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep,
+				   struct usb_descriptor_header *desc,
+				   void *priv)
+{
+	struct ffs_function *func = priv;
+	unsigned idx;
+	u8 newValue;
+
+	switch (type) {
+	default:
+	case FFS_DESCRIPTOR:
+		/* Handled in previous pass by __ffs_func_bind_do_descs() */
+		return 0;
+
+	case FFS_INTERFACE:
+		idx = *valuep;
+		if (func->interfaces_nums[idx] < 0) {
+			int id = usb_interface_id(func->conf, &func->function);
+			if (unlikely(id < 0))
+				return id;
+			func->interfaces_nums[idx] = id;
+		}
+		newValue = func->interfaces_nums[idx];
+		break;
+
+	case FFS_STRING:
+		/* String' IDs are allocated when fsf_data is bound to cdev */
+		newValue = func->ffs->stringtabs[0]->strings[*valuep - 1].id;
+		break;
+
+	case FFS_ENDPOINT:
+		/* USB_DT_ENDPOINT are handled in
+		 * __ffs_func_bind_do_descs(). */
+		if (desc->bDescriptorType == USB_DT_ENDPOINT)
+			return 0;
+
+		idx = (*valuep & USB_ENDPOINT_NUMBER_MASK) - 1;
+		if (unlikely(!func->eps[idx].ep))
+			return -EINVAL;
+
+		{
+			struct usb_endpoint_descriptor **descs;
+			descs = func->eps[idx].descs;
+			newValue = descs[descs[0] ? 0 : 1]->bEndpointAddress;
+		}
+		break;
+	}
+
+	FVDBG("%02x -> %02x", *valuep, newValue);
+	*valuep = newValue;
+	return 0;
+}
+
+static int ffs_func_bind(struct usb_configuration *c,
+			 struct usb_function *f)
+{
+	struct ffs_function *func = ffs_func_from_usb(f);
+	struct ffs_data *ffs = func->ffs;
+
+	const int full = !!func->ffs->fs_descs_count;
+	const int high = gadget_is_dualspeed(func->gadget) &&
+		func->ffs->hs_descs_count;
+
+	int ret;
+
+	/* Make it a single chunk, less management later on */
+	struct {
+		struct ffs_ep eps[ffs->eps_count];
+		struct usb_descriptor_header
+			*fs_descs[full ? ffs->fs_descs_count + 1 : 0];
+		struct usb_descriptor_header
+			*hs_descs[high ? ffs->hs_descs_count + 1 : 0];
+		short inums[ffs->interfaces_count];
+		char raw_descs[high ? ffs->raw_descs_length
+				    : ffs->raw_fs_descs_length];
+	} *data;
+
+	ENTER();
+
+	/* Only high speed but not supported by gadget? */
+	if (unlikely(!(full | high)))
+		return -ENOTSUPP;
+
+	/* Allocate */
+	data = kmalloc(sizeof *data, GFP_KERNEL);
+	if (unlikely(!data))
+		return -ENOMEM;
+
+	/* Zero */
+	memset(data->eps, 0, sizeof data->eps);
+	memcpy(data->raw_descs, ffs->raw_descs + 16, sizeof data->raw_descs);
+	memset(data->inums, 0xff, sizeof data->inums);
+	for (ret = ffs->eps_count; ret; --ret)
+		data->eps[ret].num = -1;
+
+	/* Save pointers */
+	func->eps             = data->eps;
+	func->interfaces_nums = data->inums;
+
+	/* Go throught all the endpoint descriptors and allocate
+	 * endpoints first, so that later we can rewrite the endpoint
+	 * numbers without worying that it may be described later on. */
+	if (likely(full)) {
+		func->function.descriptors = data->fs_descs;
+		ret = ffs_do_descs(ffs->fs_descs_count,
+				   data->raw_descs,
+				   sizeof data->raw_descs,
+				   __ffs_func_bind_do_descs, func);
+		if (unlikely(ret < 0))
+			goto error;
+	} else {
+		ret = 0;
+	}
+
+	if (likely(high)) {
+		func->function.hs_descriptors = data->hs_descs;
+		ret = ffs_do_descs(ffs->hs_descs_count,
+				   data->raw_descs + ret,
+				   (sizeof data->raw_descs) - ret,
+				   __ffs_func_bind_do_descs, func);
+	}
+
+	/* Now handle interface numbers allocation and interface and
+	 * enpoint numbers rewritting.  We can do that in one go
+	 * now. */
+	ret = ffs_do_descs(ffs->fs_descs_count +
+			   (high ? ffs->hs_descs_count : 0),
+			   data->raw_descs, sizeof data->raw_descs,
+			   __ffs_func_bind_do_nums, func);
+	if (unlikely(ret < 0))
+		goto error;
+
+	/* And we're done */
+	ffs_event_add(ffs, FUNCTIONFS_BIND);
+	return 0;
+
+error:
+	/* XXX Do we need to release all claimed endpoints here? */
+	return ret;
+}
+
+
+/* Other USB function hooks *************************************************/
+
+static void ffs_func_unbind(struct usb_configuration *c,
+			    struct usb_function *f)
+{
+	struct ffs_function *func = ffs_func_from_usb(f);
+	struct ffs_data *ffs = func->ffs;
+
+	ENTER();
+
+	if (ffs->func == func) {
+		ffs_func_eps_disable(func);
+		ffs->func = NULL;
+	}
+
+	ffs_event_add(ffs, FUNCTIONFS_UNBIND);
+
+	ffs_func_free(func);
+}
+
+
+static int ffs_func_set_alt(struct usb_function *f,
+			    unsigned interface, unsigned alt)
+{
+	struct ffs_function *func = ffs_func_from_usb(f);
+	struct ffs_data *ffs = func->ffs;
+	int ret = 0, intf;
+
+	if (alt != (unsigned)-1) {
+		intf = ffs_func_revmap_intf(func, interface);
+		if (unlikely(intf < 0))
+			return intf;
+	}
+
+	if (ffs->func)
+		ffs_func_eps_disable(ffs->func);
+
+	if (ffs->state != FFS_ACTIVE)
+		return -ENODEV;
+
+	if (alt == (unsigned)-1) {
+		ffs->func = NULL;
+		ffs_event_add(ffs, FUNCTIONFS_DISABLE);
+		return 0;
+	}
+
+	ffs->func = func;
+	ret = ffs_func_eps_enable(func);
+	if (likely(ret >= 0))
+		ffs_event_add(ffs, FUNCTIONFS_ENABLE);
+	return ret;
+}
+
+static void ffs_func_disable(struct usb_function *f)
+{
+	ffs_func_set_alt(f, 0, (unsigned)-1);
+}
+
+static int ffs_func_setup(struct usb_function *f,
+			  const struct usb_ctrlrequest *creq)
+{
+	struct ffs_function *func = ffs_func_from_usb(f);
+	struct ffs_data *ffs = func->ffs;
+	unsigned long flags;
+	int ret;
+
+	ENTER();
+
+	FVDBG("creq->bRequestType = %02x", creq->bRequestType);
+	FVDBG("creq->bRequest     = %02x", creq->bRequest);
+	FVDBG("creq->wValue       = %04x", le16_to_cpu(creq->wValue));
+	FVDBG("creq->wIndex       = %04x", le16_to_cpu(creq->wIndex));
+	FVDBG("creq->wLength      = %04x", le16_to_cpu(creq->wLength));
+
+	/* Most requests directed to interface go throught here
+	 * (notable exceptions are set/get interface) so we need to
+	 * handle them.  All other either handled by composite or
+	 * passed to usb_configuration->setup() (if one is set).  No
+	 * matter, we will handle requests directed to endpoint here
+	 * as well (as it's straightforward) but what to do with any
+	 * other request? */
+
+	if (ffs->state != FFS_ACTIVE)
+		return -ENODEV;
+
+	switch (creq->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_INTERFACE:
+		ret = ffs_func_revmap_intf(func, le16_to_cpu(creq->wIndex));
+		if (0) /* FALL THROUGHT but skip next instruction */
+	case USB_RECIP_ENDPOINT:
+			ret = ffs_func_revmap_ep(func,
+						 le16_to_cpu(creq->wIndex));
+		if (unlikely(ret < 0))
+			return ret;
+		break;
+
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	spin_lock_irqsave(&ffs->ev.waitq.lock, flags);
+	ffs->ev.setup = *creq;
+	ffs->ev.setup.wIndex = cpu_to_le16(ret);
+	__ffs_event_add(ffs, FUNCTIONFS_SETUP);
+	spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags);
+
+	return 0;
+}
+
+static void ffs_func_suspend(struct usb_function *f)
+{
+	ENTER();
+	ffs_event_add(ffs_func_from_usb(f)->ffs, FUNCTIONFS_SUSPEND);
+}
+
+static void ffs_func_resume(struct usb_function *f)
+{
+	ENTER();
+	ffs_event_add(ffs_func_from_usb(f)->ffs, FUNCTIONFS_RESUME);
+}
+
+
+
+/* Enpoint and interface numbers reverse mapping ****************************/
+
+static int ffs_func_revmap_ep(struct ffs_function *func, u8 num)
+{
+	num = func->eps_revmap[num & USB_ENDPOINT_NUMBER_MASK];
+	return num ? num : -EDOM;
+}
+
+static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf)
+{
+	short *nums = func->interfaces_nums;
+	unsigned count = func->ffs->interfaces_count;
+
+	for (; count; --count, ++nums) {
+		if (*nums >= 0 && *nums == intf)
+			return nums - func->interfaces_nums;
+	}
+
+	return -EDOM;
+}
+
+
+/* Misc helper functions ****************************************************/
+
+static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock)
+{
+	return nonblock
+		? likely(mutex_trylock(mutex)) ? 0 : -EAGAIN
+		: mutex_lock_interruptible(mutex);
+}
+
+
+static char *ffs_prepare_buffer(const char * __user buf, size_t len)
+{
+	char *data;
+
+	if (unlikely(!len))
+		return NULL;
+
+	data = kmalloc(len, GFP_KERNEL);
+	if (unlikely(!data))
+		return ERR_PTR(-ENOMEM);
+
+	if (unlikely(__copy_from_user(data, buf, len))) {
+		kfree(data);
+		return ERR_PTR(-EFAULT);
+	}
+
+	FVDBG("Buffer from user space:");
+	ffs_dump_mem("", data, len);
+
+	return data;
+}
diff --git a/include/linux/usb/functionfs.h b/include/linux/usb/functionfs.h
new file mode 100644
index 0000000..a34a2a0
--- /dev/null
+++ b/include/linux/usb/functionfs.h
@@ -0,0 +1,199 @@
+#ifndef __LINUX_FUNCTIONFS_H__
+#define __LINUX_FUNCTIONFS_H__ 1
+
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#include <linux/usb/ch9.h>
+
+
+enum {
+	FUNCTIONFS_DESCRIPTORS_MAGIC = 1,
+	FUNCTIONFS_STRINGS_MAGIC     = 2
+};
+
+
+#ifndef __KERNEL__
+
+/* Descriptor of an non-audio endpoint */
+struct usb_endpoint_descriptor_no_audio {
+	__u8  bLength;
+	__u8  bDescriptorType;
+
+	__u8  bEndpointAddress;
+	__u8  bmAttributes;
+	__le16 wMaxPacketSize;
+	__u8  bInterval;
+} __attribute__((packed));
+
+
+/*
+ * All numbers must be in little endian order.
+ */
+
+struct usb_functionfs_descs_head {
+	__le32 magic;
+	__le32 length;
+	__le32 fs_count;
+	__le32 hs_count;
+} __attribute__((packed));
+
+/*
+ * Descriptors format:
+ *
+ * | off | name      | type         | description                          |
+ * |-----+-----------+--------------+--------------------------------------|
+ * |   0 | magic     | LE32         | FUNCTIONFS_{FS,HS}_DESCRIPTORS_MAGIC |
+ * |   4 | lenght    | LE32         | length of the whole data chunk       |
+ * |   8 | fs_count  | LE32         | number of full-speed descriptors     |
+ * |  12 | hs_count  | LE32         | number of high-speed descriptors     |
+ * |  16 | fs_descrs | Descriptor[] | list of full-speed descriptors       |
+ * |     | hs_descrs | Descriptor[] | list of high-speed descriptors       |
+ *
+ * descs are just valid USB descriptors and have the following format:
+ *
+ * | off | name            | type | description              |
+ * |-----+-----------------+------+--------------------------|
+ * |   0 | bLength         | U8   | length of the descriptor |
+ * |   1 | bDescriptorType | U8   | descriptor type          |
+ * |   2 | payload         |      | descriptor's payload     |
+ */
+
+struct usb_functionfs_strings_head {
+	__le32 magic;
+	__le32 length;
+	__le32 str_count;
+	__le32 lang_count;
+} __attribute__((packed));
+
+/*
+ * Strings format:
+ *
+ * | off | name       | type                  | description                |
+ * |-----+------------+-----------------------+----------------------------|
+ * |   0 | magic      | LE32                  | FUNCTIONFS_STRINGS_MAGIC   |
+ * |   4 | length     | LE32                  | length of the data chunk   |
+ * |   8 | str_count  | LE32                  | number of strings          |
+ * |  12 | lang_count | LE32                  | number of languages        |
+ * |  16 | stringtab  | StringTab[lang_count] | table of strings per lang  |
+ *
+ * For each language there is one stringtab entry (ie. there are lang_count
+ * stringtab entires).  Each StringTab has following format:
+ *
+ * | off | name    | type              | description                        |
+ * |-----+---------+-------------------+------------------------------------|
+ * |   0 | lang    | LE16              | language code                      |
+ * |   2 | strings | String[str_count] | array of strings in given language |
+ *
+ * For each string ther is one strings entry (ie. there are str_count
+ * string entries).  Each String is a NUL terminated string encoded in
+ * UTF-8.
+ */
+
+#endif
+
+
+/*
+ * Events are delivered on the ep0 file descriptor, when the user mode driver
+ * reads from this file descriptor after writing the descriptors.  Don't
+ * stop polling this descriptor.
+ */
+
+enum usb_functionfs_event_type {
+	FUNCTIONFS_BIND,
+	FUNCTIONFS_UNBIND,
+
+	FUNCTIONFS_ENABLE,
+	FUNCTIONFS_DISABLE,
+
+	FUNCTIONFS_SETUP,
+
+	FUNCTIONFS_SUSPEND,
+	FUNCTIONFS_RESUME
+};
+
+/* NOTE:  this structure must stay the same size and layout on
+ * both 32-bit and 64-bit kernels.
+ */
+struct usb_functionfs_event {
+	union {
+		/* SETUP: packet; DATA phase i/o precedes next event
+		 *(setup.bmRequestType & USB_DIR_IN) flags direction */
+		struct usb_ctrlrequest	setup;
+	} __attribute__((packed)) u;
+
+	/* enum usb_functionfs_event_type */
+	__u8				type;
+	__u8				_pad[3];
+} __attribute__((packed));
+
+
+/* Endpoint ioctls */
+/* The same as in gadgetfs */
+
+/* IN transfers may be reported to the gadget driver as complete
+ *	when the fifo is loaded, before the host reads the data;
+ * OUT transfers may be reported to the host's "client" driver as
+ *	complete when they're sitting in the FIFO unread.
+ * THIS returns how many bytes are "unclaimed" in the endpoint fifo
+ * (needed for precise fault handling, when the hardware allows it)
+ */
+#define	FUNCTIONFS_FIFO_STATUS	_IO('g', 1)
+
+/* discards any unclaimed data in the fifo. */
+#define	FUNCTIONFS_FIFO_FLUSH	_IO('g', 2)
+
+/* resets endpoint halt+toggle; used to implement set_interface.
+ * some hardware (like pxa2xx) can't support this.
+ */
+#define	FUNCTIONFS_CLEAR_HALT	_IO('g', 3)
+
+/* Specific for functionfs */
+
+/*
+ * Returns reverse mapping of an interface.  Called on EP0.  If there
+ * is no such interface returns -EDOM.  If function is not active
+ * returns -ENODEV.
+ */
+#define	FUNCTIONFS_INTERFACE_REVMAP	_IO('g', 128)
+
+/*
+ * Returns real bEndpointAddress of an endpoint.  If function is not
+ * active returns -ENODEV.
+ */
+#define	FUNCTIONFS_ENDPOINT_REVMAP	_IO('g', 129)
+
+
+#ifdef __KERNEL__
+
+struct ffs_data;
+struct usb_composite_dev;
+struct usb_configuration;
+
+
+static int  functionfs_init(void) __attribute__((warn_unused_result));
+static void functionfs_cleanup(void);
+
+static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev)
+	__attribute__((warn_unused_result, nonnull));
+static void functionfs_unbind(struct ffs_data *ffs)
+	__attribute__((nonnull));
+
+static int functionfs_add(struct usb_composite_dev *cdev,
+			  struct usb_configuration *c,
+			  struct ffs_data *ffs)
+	__attribute__((warn_unused_result, nonnull));
+
+
+static int functionfs_ready_callback(struct ffs_data *ffs)
+	__attribute__((warn_unused_result, nonnull));
+static void functionfs_closed_callback(struct ffs_data *ffs)
+	__attribute__((nonnull));
+static int functionfs_check_dev_callback(const char *dev_name)
+	__attribute__((warn_unused_result, nonnull));
+
+
+#endif
+
+#endif
-- 
1.7.0


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [PATCHv2 5/8] USB: g_ffs: the FunctionFS gadget driver
  2010-04-09 19:21       ` [PATCHv2 4/8] USB: f_fs: the FunctionFS driver Michal Nazarewicz
@ 2010-04-09 19:21         ` Michal Nazarewicz
  2010-04-09 19:21           ` [PATCHv2 6/8] USB: ffs-test: FunctionFS testing program Michal Nazarewicz
  0 siblings, 1 reply; 47+ messages in thread
From: Michal Nazarewicz @ 2010-04-09 19:21 UTC (permalink / raw)
  To: linux-usb
  Cc: Michal Nazarewicz, Michal Nazarewicz, Greg KH, Kyungmin Park,
	Marek Szyprowski, linux-kernel

The Function Filesystem (FunctioFS) lets one create USB
composite functions in user space in the same way as GadgetFS
lets one create USB gadgets in user space.  This allows
creation of composite gadgets such that some of the functions
are implemented in kernel space (for instance Ethernet, serial
or mass storage) and other are implemented in user space.

Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com>
Cc: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
---
 drivers/usb/gadget/Kconfig  |   21 +++-
 drivers/usb/gadget/Makefile |    2 +
 drivers/usb/gadget/g_ffs.c  |  322 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 344 insertions(+), 1 deletions(-)
 create mode 100644 drivers/usb/gadget/g_ffs.c

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index d886cd6..6f38a10 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -746,6 +746,26 @@ config USB_GADGETFS
 	  Say "y" to link the driver statically, or "m" to build a
 	  dynamically linked module called "gadgetfs".
 
+config USB_FUNCTIONFS
+	tristate "Function Filesystem (EXPERIMENTAL)"
+	depends on EXPERIMENTAL
+	help
+	  The Function Filesystem (FunctioFS) lets one create USB
+	  composite functions in user space in the same way as GadgetFS
+	  lets one create USB gadgets in user space.  This allows creation
+	  of composite gadgets such that some of the functions are
+	  implemented in kernel space (for instance Ethernet, serial or
+	  mass storage) and other are implemented in user space.
+
+	  Say "y" to link the driver statically, or "m" to build
+	  a dynamically linked module called "g_ffs".
+
+config USB_FUNCTIONFS_ETH
+	bool "Include Ethernet funcien"
+	depends on USB_FUNCTIONFS
+	help
+	  Include an Ethernet funcion in the Funcion Filesystem.
+
 config USB_FILE_STORAGE
 	tristate "File-backed Storage Gadget"
 	depends on BLOCK
@@ -899,7 +919,6 @@ config USB_G_MULTI_CDC
 
 	  If unsure, say "y".
 
-
 # put drivers that need isochronous transfer support (for audio
 # or video class gadget drivers), or specific hardware, here.
 
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index cecbc3d..0dd1e7b 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -50,6 +50,8 @@ obj-$(CONFIG_USB_ZERO)		+= g_zero.o
 obj-$(CONFIG_USB_AUDIO)		+= g_audio.o
 obj-$(CONFIG_USB_ETH)		+= g_ether.o
 obj-$(CONFIG_USB_GADGETFS)	+= gadgetfs.o
+obj-$(CONFIG_USB_FUNCTIONFS)	+= g_ffs.o
+obj-$(CONFIG_USB_ETH_FUNCTIONFS)	+= g_eth_ffs.o
 obj-$(CONFIG_USB_FILE_STORAGE)	+= g_file_storage.o
 obj-$(CONFIG_USB_MASS_STORAGE)	+= g_mass_storage.o
 obj-$(CONFIG_USB_G_SERIAL)	+= g_serial.o
diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c
new file mode 100644
index 0000000..6033f5d
--- /dev/null
+++ b/drivers/usb/gadget/g_ffs.c
@@ -0,0 +1,322 @@
+#include <linux/module.h>
+#include <linux/utsname.h>
+
+
+/*
+ * kbuild is not very cooperative with respect to linking separately
+ * compiled library objects into one module.  So for now we won't use
+ * separate compilation ... ensuring init/exit sections work to shrink
+ * the runtime footprint, and giving us at least some parts of what
+ * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
+ */
+
+/* Do not mark any of the USB and composite initialization functions
+ * as __init, we use it after user space writes descriptors which is
+ * when init segments are removed. */
+#define USB_NO_INIT_SEGMENT 1
+/* Do not mark composite_unbind() and usb_composite_unregister() as
+ * __exit, we may need it even if we are not unloaded. */
+#define USB_NO_EXIT_SEGMENT 1
+
+#include "composite.c"
+#include "usbstring.c"
+#include "config.c"
+#include "epautoconf.c"
+
+#ifdef CONFIG_USB_FUNCTIONFS_ETH
+/* #  if defined USB_ETH_RNDIS */
+/* #    undef USB_ETH_RNDIS */
+/* #  endif */
+/* #  ifdef CONFIG_USB_ETH_FUNCTIONFS_RNDIS */
+/* #    define USB_ETH_RNDIS y */
+/* #  endif */
+#  include "u_ether.h"
+
+#  include "f_ecm.c"
+#  include "f_subset.c"
+/* #  ifdef USB_ETH_RNDIS */
+/* #    include "f_rndis.c" */
+/* #    include "rndis.c" */
+/* #  endif */
+#  include "u_ether.c"
+
+static u8 gfs_hostaddr[ETH_ALEN];
+#endif
+
+#include "f_fs.c"
+
+
+#define DRIVER_NAME	"g_ffs"
+#define DRIVER_DESC	"USB Function Filesystem"
+#define DRIVER_VERSION	"24 Aug 2004"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Michal Nazarewicz");
+MODULE_LICENSE("GPL");
+
+
+static unsigned short gfs_vendor_id    = 0x0525;	/* XXX NetChip */
+static unsigned short gfs_product_id   = 0xa4ac;	/* XXX */
+
+static struct usb_device_descriptor gfs_dev_desc = {
+	.bLength		= sizeof gfs_dev_desc,
+	.bDescriptorType	= USB_DT_DEVICE,
+
+	.bcdUSB			= cpu_to_le16(0x0200),
+	.bDeviceClass		= USB_CLASS_PER_INTERFACE,
+
+	/* Vendor and product id can be overridden by module parameters.  */
+	/* .idVendor		= cpu_to_le16(gfs_vendor_id), */
+	/* .idProduct		= cpu_to_le16(gfs_product_id), */
+	/* .bcdDevice		= f(hardware) */
+	/* .iManufacturer	= DYNAMIC */
+	/* .iProduct		= DYNAMIC */
+	/* NO SERIAL NUMBER */
+	.bNumConfigurations	= 1,
+};
+
+#define GFS_MODULE_PARAM_DESC(name, field) \
+	MODULE_PARM_DESC(name, "Value of the " #field " field of the device descriptor sent to the host.  Takes effect only prior to the user-space driver registering to the FunctionFS.")
+
+module_param_named(usb_class,    gfs_dev_desc.bDeviceClass,    byte,   0644);
+GFS_MODULE_PARAM_DESC(usb_class, bDeviceClass);
+module_param_named(usb_subclass, gfs_dev_desc.bDeviceSubClass, byte,   0644);
+GFS_MODULE_PARAM_DESC(usb_subclass, bDeviceSubClass);
+module_param_named(usb_protocol, gfs_dev_desc.bDeviceProtocol, byte,   0644);
+GFS_MODULE_PARAM_DESC(usb_protocol, bDeviceProtocol);
+module_param_named(usb_vendor,   gfs_vendor_id,                ushort, 0644);
+GFS_MODULE_PARAM_DESC(usb_vendor, idVendor);
+module_param_named(usb_product,  gfs_product_id,               ushort, 0644);
+GFS_MODULE_PARAM_DESC(usb_product, idProduct);
+
+
+
+static const struct usb_descriptor_header *gfs_otg_desc[] = {
+	(const struct usb_descriptor_header *)
+	&(const struct usb_otg_descriptor) {
+		.bLength		= sizeof(struct usb_otg_descriptor),
+		.bDescriptorType	= USB_DT_OTG,
+
+		/* REVISIT SRP-only hardware is possible, although
+		 * it would not be called "OTG" ... */
+		.bmAttributes		= USB_OTG_SRP | USB_OTG_HNP,
+	},
+
+	NULL
+};
+
+/* string IDs are assigned dynamically */
+
+enum {
+	GFS_STRING_MANUFACTURER_IDX,
+	GFS_STRING_PRODUCT_IDX,
+	GFS_STRING_CONFIGURATION_IDX
+};
+
+static       char gfs_manufacturer[50];
+static const char gfs_driver_desc[] = DRIVER_DESC;
+static const char gfs_short_name[]  = DRIVER_NAME;
+
+static struct usb_string gfs_strings[] = {
+	[GFS_STRING_MANUFACTURER_IDX].s = gfs_manufacturer,
+	[GFS_STRING_PRODUCT_IDX].s = gfs_driver_desc,
+	[GFS_STRING_CONFIGURATION_IDX].s = "Self Powered",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings *gfs_dev_strings[] = {
+	&(struct usb_gadget_strings) {
+		.language	= 0x0409,	/* en-us */
+		.strings	= gfs_strings,
+	},
+	NULL,
+};
+
+
+static int gfs_bind(struct usb_composite_dev *cdev);
+static int gfs_unbind(struct usb_composite_dev *cdev);
+static int gfs_do_config(struct usb_configuration *c);
+
+
+static struct usb_composite_driver gfs_driver = {
+	.name		= gfs_short_name,
+	.dev		= &gfs_dev_desc,
+	.strings	= gfs_dev_strings,
+	.bind		= gfs_bind,
+	.unbind		= gfs_unbind,
+};
+
+static struct usb_configuration gfs_config_driver = {
+	.label			= "FunctionFS",
+	.bind			= gfs_do_config,
+	.bConfigurationValue	= 1,
+	/* .iConfiguration	= DYNAMIC */
+	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER,
+};
+
+
+static struct ffs_data *gfs_ffs_data;
+static unsigned long gfs_registered;
+
+
+static int __init gfs_init(void)
+{
+	ENTER();
+
+	return functionfs_init();
+}
+module_init(gfs_init);
+
+static void __exit gfs_exit(void)
+{
+	ENTER();
+
+	if (test_and_clear_bit(0, &gfs_registered))
+		usb_composite_unregister(&gfs_driver);
+
+	functionfs_cleanup();
+}
+module_exit(gfs_exit);
+
+
+static int functionfs_ready_callback(struct ffs_data *ffs)
+{
+	int ret;
+
+	ENTER();
+
+	if (WARN_ON(test_and_set_bit(0, &gfs_registered)))
+		return -EBUSY;
+
+	gfs_ffs_data = ffs;
+	ret = usb_composite_register(&gfs_driver);
+	if (unlikely(ret < 0))
+		clear_bit(0, &gfs_registered);
+	return ret;
+}
+
+static void functionfs_closed_callback(struct ffs_data *ffs)
+{
+	ENTER();
+
+	if (test_and_clear_bit(0, &gfs_registered))
+		usb_composite_unregister(&gfs_driver);
+}
+
+
+static int functionfs_check_dev_callback(const char *dev_name)
+{
+	return 0;
+}
+
+
+
+static int gfs_bind(struct usb_composite_dev *cdev)
+{
+	int ret;
+
+	ENTER();
+
+	if (WARN_ON(!gfs_ffs_data))
+		return -ENODEV;
+
+#ifdef CONFIG_USB_FUNCTIONFS_ETH
+	ret = gether_setup(cdev->gadget, gfs_hostaddr);
+	if (unlikely(ret < 0))
+		goto error_quick;
+#endif
+
+	gfs_dev_desc.idVendor  = cpu_to_le16(gfs_vendor_id);
+	gfs_dev_desc.idProduct = cpu_to_le16(gfs_product_id);
+
+	snprintf(gfs_manufacturer, sizeof gfs_manufacturer, "%s %s with %s",
+		 init_utsname()->sysname, init_utsname()->release,
+		 cdev->gadget->name);
+	ret = usb_string_id(cdev);
+	if (unlikely(ret < 0))
+		goto error;
+	gfs_strings[GFS_STRING_MANUFACTURER_IDX].id = ret;
+	gfs_dev_desc.iManufacturer = ret;
+
+	ret = usb_string_id(cdev);
+	if (unlikely(ret < 0))
+		goto error;
+	gfs_strings[GFS_STRING_PRODUCT_IDX].id = ret;
+	gfs_dev_desc.iProduct = ret;
+
+	ret = usb_string_id(cdev);
+	if (unlikely(ret < 0))
+		goto error;
+	gfs_strings[GFS_STRING_CONFIGURATION_IDX].id = ret;
+	gfs_config_driver.iConfiguration = ret;
+
+	ret = functionfs_bind(gfs_ffs_data, cdev);
+	if (unlikely(ret < 0))
+		goto error;
+
+	ret = usb_add_config(cdev, &gfs_config_driver);
+	if (unlikely(ret < 0))
+		goto error_unbind;
+
+	return 0;
+
+error_unbind:
+	functionfs_unbind(gfs_ffs_data);
+error:
+#ifdef CONFIG_USB_FUNCTIONFS_ETH
+	gether_cleanup();
+error_quick:
+#endif
+	gfs_ffs_data = NULL;
+	return ret;
+}
+
+static int gfs_unbind(struct usb_composite_dev *cdev)
+{
+	ENTER();
+
+#ifdef CONFIG_USB_FUNCTIONFS_ETH
+	gether_cleanup();
+#endif
+
+	if (!WARN_ON(!gfs_ffs_data)) {
+		functionfs_unbind(gfs_ffs_data);
+		gfs_ffs_data = NULL;
+	}
+
+	return 0;
+}
+
+
+static int gfs_do_config(struct usb_configuration *c)
+{
+	int ret;
+
+	ENTER();
+
+	if (WARN_ON(!gfs_ffs_data))
+		return -ENODEV;
+
+	if (gadget_is_otg(c->cdev->gadget)) {
+		c->descriptors = gfs_otg_desc;
+		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+	}
+
+#ifdef CONFIG_USB_FUNCTIONFS_ETH
+	if (can_support_ecm(c->cdev->gadget)) {
+		FINFO("ecm_bind_config");
+		ret = ecm_bind_config(c, gfs_hostaddr);
+	} else {
+		FINFO("geth_bind_config");
+		ret = geth_bind_config(c, gfs_hostaddr);
+	}
+	if (unlikely(ret < 0))
+		return ret;
+#endif
+
+	ret = functionfs_add(c->cdev, c, gfs_ffs_data);
+	if (unlikely(ret < 0))
+		return ret;
+
+	return 0;
+}
-- 
1.7.0


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [PATCHv2 6/8] USB: ffs-test: FunctionFS testing program
  2010-04-09 19:21         ` [PATCHv2 5/8] USB: g_ffs: the FunctionFS gadget driver Michal Nazarewicz
@ 2010-04-09 19:21           ` Michal Nazarewicz
  0 siblings, 0 replies; 47+ messages in thread
From: Michal Nazarewicz @ 2010-04-09 19:21 UTC (permalink / raw)
  To: linux-usb
  Cc: Michal Nazarewicz, Michal Nazarewicz, Greg KH, Kyungmin Park,
	Marek Szyprowski, linux-kernel

This adds an example user-space FunctionFS driver which
implements a source/sink interface used for testing.

Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com>
Cc: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
---
 tools/usb/ffs-test.c |  554 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 554 insertions(+), 0 deletions(-)
 create mode 100644 tools/usb/ffs-test.c

diff --git a/tools/usb/ffs-test.c b/tools/usb/ffs-test.c
new file mode 100644
index 0000000..bbe2e3a
--- /dev/null
+++ b/tools/usb/ffs-test.c
@@ -0,0 +1,554 @@
+/*
+ * ffs-test.c.c -- user mode filesystem api for usb composite function
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *                    Author: Michal Nazarewicz <m.nazarewicz@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* $(CROSS_COMPILE)cc -Wall -Wextra -g -o ffs-test ffs-test.c -lpthread */
+
+
+#define _BSD_SOURCE /* for endian.h */
+
+#include <endian.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <linux/usb/functionfs.h>
+
+
+/******************** Little Endian Handling ********************************/
+
+#define cpu_to_le16(x)  htole16(x)
+#define cpu_to_le32(x)  htole32(x)
+#define le32_to_cpu(x)  le32toh(x)
+#define le16_to_cpu(x)  le16toh(x)
+
+static inline __u16 get_unaligned_le16(const void *_ptr)
+{
+	const __u8 *ptr = _ptr;
+	return ptr[0] | (ptr[1] << 8);
+}
+
+static inline __u32 get_unaligned_le32(const void *_ptr)
+{
+	const __u8 *ptr = _ptr;
+	return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
+}
+
+static inline void put_unaligned_le16(__u16 val, void *_ptr)
+{
+	__u8 *ptr = _ptr;
+	*ptr++ = val;
+	*ptr++ = val >> 8;
+}
+
+static inline void put_unaligned_le32(__u32 val, void *_ptr)
+{
+	__u8 *ptr = _ptr;
+	*ptr++ = val;
+	*ptr++ = val >>  8;
+	*ptr++ = val >> 16;
+	*ptr++ = val >> 24;
+}
+
+
+/******************** Messages and Errors ***********************************/
+
+static const char argv0[] = "ffs-test";
+
+static unsigned verbosity = 7;
+
+static void _msg(unsigned level, const char *fmt, ...)
+{
+	if (level < 2)
+		level = 2;
+	else if (level > 7)
+		level = 7;
+
+	if (level <= verbosity) {
+		static const char levels[8][6] = {
+			[2] = "crit:",
+			[3] = "err: ",
+			[4] = "warn:",
+			[5] = "note:",
+			[6] = "info:",
+			[7] = "dbg: "
+		};
+
+		int _errno = errno;
+		va_list ap;
+
+		fprintf(stderr, "%s: %s ", argv0, levels[level]);
+		va_start(ap, fmt);
+		vfprintf(stderr, fmt, ap);
+		va_end(ap);
+
+		if (fmt[strlen(fmt) - 1] != '\n') {
+			char buffer[128];
+			strerror_r(_errno, buffer, sizeof buffer);
+			fprintf(stderr, ": (-%d) %s\n", _errno, buffer);
+		}
+
+		fflush(stderr);
+	}
+}
+
+#define die(...)  (_msg(2, __VA_ARGS__), exit(1))
+#define err(...)   _msg(3, __VA_ARGS__)
+#define warn(...)  _msg(4, __VA_ARGS__)
+#define note(...)  _msg(5, __VA_ARGS__)
+#define info(...)  _msg(6, __VA_ARGS__)
+#define debug(...) _msg(7, __VA_ARGS__)
+
+#define die_on(cond, ...) do { \
+	if (cond) \
+		die(__VA_ARGS__); \
+	} while (0)
+
+
+/******************** Descriptors and Strings *******************************/
+
+static const struct {
+	struct usb_functionfs_descs_head header;
+	struct {
+		struct usb_interface_descriptor intf;
+		struct usb_endpoint_descriptor_no_audio sink;
+		struct usb_endpoint_descriptor_no_audio source;
+	} __attribute__((packed)) fs_descs, hs_descs;
+} __attribute__((packed)) descriptors = {
+	.header = {
+		.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC),
+		.length = cpu_to_le32(sizeof descriptors),
+		.fs_count = 3,
+		.hs_count = 3,
+	},
+	.fs_descs = {
+		.intf = {
+			.bLength = sizeof descriptors.fs_descs.intf,
+			.bDescriptorType = USB_DT_INTERFACE,
+			.bNumEndpoints = 2,
+			.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+			.iInterface = 1,
+		},
+		.sink = {
+			.bLength = sizeof descriptors.fs_descs.sink,
+			.bDescriptorType = USB_DT_ENDPOINT,
+			.bEndpointAddress = 1 | USB_DIR_IN,
+			.bmAttributes = USB_ENDPOINT_XFER_BULK,
+			/* .wMaxPacketSize = autoconfiguration (kernel) */
+		},
+		.source = {
+			.bLength = sizeof descriptors.fs_descs.source,
+			.bDescriptorType = USB_DT_ENDPOINT,
+			.bEndpointAddress = 2 | USB_DIR_OUT,
+			.bmAttributes = USB_ENDPOINT_XFER_BULK,
+			/* .wMaxPacketSize = autoconfiguration (kernel) */
+		},
+	},
+	.hs_descs = {
+		.intf = {
+			.bLength = sizeof descriptors.fs_descs.intf,
+			.bDescriptorType = USB_DT_INTERFACE,
+			.bNumEndpoints = 2,
+			.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+			.iInterface = 1,
+		},
+		.sink = {
+			.bLength = sizeof descriptors.hs_descs.sink,
+			.bDescriptorType = USB_DT_ENDPOINT,
+			.bEndpointAddress = 1 | USB_DIR_IN,
+			.bmAttributes = USB_ENDPOINT_XFER_BULK,
+			.wMaxPacketSize = cpu_to_le16(512),
+		},
+		.source = {
+			.bLength = sizeof descriptors.hs_descs.source,
+			.bDescriptorType = USB_DT_ENDPOINT,
+			.bEndpointAddress = 2 | USB_DIR_OUT,
+			.bmAttributes = USB_ENDPOINT_XFER_BULK,
+			.wMaxPacketSize = cpu_to_le16(512),
+			.bInterval = 1, /* NAK every 1 uframe */
+		},
+	},
+};
+
+
+#define STR_INTERFACE_ "Source/Sink"
+
+static const struct {
+	struct usb_functionfs_strings_head header;
+	struct {
+		__le16 code;
+		const char str1[sizeof STR_INTERFACE_];
+	} __attribute__((packed)) lang0;
+} __attribute__((packed)) strings = {
+	.header = {
+		.magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
+		.length = cpu_to_le32(sizeof strings),
+		.str_count = cpu_to_le32(1),
+		.lang_count = cpu_to_le32(1),
+	},
+	.lang0 = {
+		cpu_to_le16(0x0409), /* en-us */
+		STR_INTERFACE_,
+	},
+};
+
+#define STR_INTERFACE strings.lang0.str1
+
+
+/******************** Files and Threads Handling ****************************/
+
+struct thread;
+
+static ssize_t read_wrap(struct thread *t, void *buf, size_t nbytes);
+static ssize_t write_wrap(struct thread *t, const void *buf, size_t nbytes);
+static ssize_t ep0_consume(struct thread *t, const void *buf, size_t nbytes);
+static ssize_t fill_in_buf(struct thread *t, void *buf, size_t nbytes);
+static ssize_t empty_out_buf(struct thread *t, const void *buf, size_t nbytes);
+
+
+static struct thread {
+	const char *const filename;
+	size_t buf_size;
+
+	ssize_t (*in)(struct thread *, void *, size_t);
+	const char *const in_name;
+
+	ssize_t (*out)(struct thread *, const void *, size_t);
+	const char *const out_name;
+
+	int fd;
+	pthread_t id;
+	void *buf;
+	ssize_t status;
+} threads[] = {
+	{
+		"ep0", 4 * sizeof(struct usb_functionfs_event),
+		read_wrap, NULL,
+		ep0_consume, "<consume>",
+		0, 0, NULL, 0
+	},
+	{
+		"ep1", 8 * 1024,
+		fill_in_buf, "<in>",
+		write_wrap, NULL,
+		0, 0, NULL, 0
+	},
+	{
+		"ep2", 8 * 1024,
+		read_wrap, NULL,
+		empty_out_buf, "<out>",
+		0, 0, NULL, 0
+	},
+};
+
+
+static void init_thread(struct thread *t)
+{
+	t->buf = malloc(t->buf_size);
+	die_on(!t->buf, "malloc");
+
+	t->fd = open(t->filename, O_RDWR);
+	die_on(t->fd < 0, "%s", t->filename);
+}
+
+static void cleanup_thread(void *arg)
+{
+	struct thread *t = arg;
+	int ret, fd;
+
+	fd = t->fd;
+	if (t->fd < 0)
+		return;
+	t->fd = -1;
+
+	/* test the FIFO ioctls (non-ep0 code paths) */
+	if (t != threads) {
+		ret = ioctl(fd, FUNCTIONFS_FIFO_STATUS);
+		if (ret < 0) {
+			/* ENODEV reported after disconnect */
+			if (errno != ENODEV)
+				err("%s: get fifo status", t->filename);
+		} else if (ret) {
+			warn("%s: unclaimed = %d\n", t->filename, ret);
+			if (ioctl(fd, FUNCTIONFS_FIFO_FLUSH) < 0)
+				err("%s: fifo flush", t->filename);
+		}
+	}
+
+	if (close(fd) < 0)
+		err("%s: close", t->filename);
+
+	free(t->buf);
+	t->buf = NULL;
+}
+
+static void *start_thread_helper(void *arg)
+{
+	const char *name, *op, *in_name, *out_name;
+	struct thread *t = arg;
+	ssize_t ret;
+
+	info("%s: starts\n", t->filename);
+	in_name = t->in_name ? t->in_name : t->filename;
+	out_name = t->out_name ? t->out_name : t->filename;
+
+	pthread_cleanup_push(cleanup_thread, arg);
+
+	for (;;) {
+		pthread_testcancel();
+
+		ret = t->in(t, t->buf, t->buf_size);
+		if (ret > 0) {
+			ret = t->out(t, t->buf, t->buf_size);
+			name = out_name;
+			op = "write";
+		} else {
+			name = in_name;
+			op = "read";
+		}
+
+		if (ret > 0) {
+			/* nop */
+		} else if (!ret) {
+			debug("%s: %s: EOF", name, op);
+			break;
+		} else if (errno == EINTR || errno == EAGAIN) {
+			debug("%s: %s", name, op);
+		} else {
+			warn("%s: %s", name, op);
+			break;
+		}
+	}
+
+	pthread_cleanup_pop(1);
+
+	t->status = ret;
+	info("%s: ends\n", t->filename);
+	return NULL;
+}
+
+static void start_thread(struct thread *t)
+{
+	debug("%s: starting\n", t->filename);
+
+	die_on(pthread_create(&t->id, NULL, start_thread_helper, t) < 0,
+	       "pthread_create(%s)", t->filename);
+}
+
+static void join_thread(struct thread *t)
+{
+	int ret = pthread_join(t->id, NULL);
+
+	if (ret < 0)
+		err("%s: joining thread", t->filename);
+	else
+		debug("%s: joined\n", t->filename);
+}
+
+
+static ssize_t read_wrap(struct thread *t, void *buf, size_t nbytes)
+{
+	return read(t->fd, buf, nbytes);
+}
+
+static ssize_t write_wrap(struct thread *t, const void *buf, size_t nbytes)
+{
+	return write(t->fd, buf, nbytes);
+}
+
+
+/******************** Empty/Fill buffer routines ****************************/
+
+/* 0 -- stream of zeros, 1 -- i % 63, 2 -- pipe */
+enum pattern { PAT_ZERO, PAT_SEQ, PAT_PIPE };
+static enum pattern pattern;
+
+static ssize_t
+fill_in_buf(struct thread *ignore, void *buf, size_t nbytes)
+{
+	size_t i;
+	__u8 *p;
+
+	(void)ignore;
+
+	switch (pattern) {
+	case PAT_ZERO:
+		memset(buf, 0, nbytes);
+		break;
+
+	case PAT_SEQ:
+		for (p = buf, i = 0; i < nbytes; ++i, ++p)
+			*p = i % 63;
+		break;
+
+	case PAT_PIPE:
+		return fread(buf, 1, nbytes, stdin);
+	}
+
+	return nbytes;
+}
+
+static ssize_t
+empty_out_buf(struct thread *ignore, const void *buf, size_t nbytes)
+{
+	const __u8 *p;
+	__u8 expected;
+	ssize_t ret;
+	size_t len;
+
+	(void)ignore;
+
+	switch (pattern) {
+	case PAT_ZERO:
+		expected = 0;
+		for (p = buf, len = 0; len < nbytes; ++p, ++len)
+			if (*p)
+				goto invalid;
+		break;
+
+	case PAT_SEQ:
+		for (p = buf, len = 0; len < nbytes; ++p, ++len)
+			if (*p != len % 63) {
+				expected = len % 63;
+				goto invalid;
+			}
+		break;
+
+	case PAT_PIPE:
+		ret = fwrite(buf, nbytes, 1, stdout);
+		if (ret > 0)
+			fflush(stdout);
+		break;
+
+invalid:
+		err("bad OUT byte %zd, expected %02x got %02x\n",
+		    len, expected, *p);
+		for (p = buf, len = 0; len < nbytes; ++p, ++len) {
+			if (0 == (len % 32))
+				fprintf(stderr, "%4d:", len);
+			fprintf(stderr, " %02x", *p);
+			if (31 == (len % 32))
+				fprintf(stderr, "\n");
+		}
+		fflush(stderr);
+		errno = EILSEQ;
+		return -1;
+	}
+
+	return len;
+}
+
+
+/******************** Endpoints routines ************************************/
+
+static void handle_setup(const struct usb_ctrlrequest *setup)
+{
+	printf("bRequestType = %d\n", setup->bRequestType);
+	printf("bRequest     = %d\n", setup->bRequest);
+	printf("wValue       = %d\n", le16_to_cpu(setup->wValue));
+	printf("wIndex       = %d\n", le16_to_cpu(setup->wIndex));
+	printf("wLength      = %d\n", le16_to_cpu(setup->wLength));
+}
+
+static ssize_t
+ep0_consume(struct thread *ignore, const void *buf, size_t nbytes)
+{
+	static const char *const names[] = {
+		[FUNCTIONFS_BIND] = "BIND",
+		[FUNCTIONFS_UNBIND] = "UNBIND",
+		[FUNCTIONFS_ENABLE] = "ENABLE",
+		[FUNCTIONFS_DISABLE] = "DISABLE",
+		[FUNCTIONFS_SETUP] = "SETUP",
+		[FUNCTIONFS_SUSPEND] = "SUSPEND",
+		[FUNCTIONFS_RESUME] = "RESUME",
+	};
+
+	const struct usb_functionfs_event *event = buf;
+	size_t n;
+
+	(void)ignore;
+
+	for (n = nbytes / sizeof *event; n; --n, ++event)
+		switch (event->type) {
+		case FUNCTIONFS_BIND:
+		case FUNCTIONFS_UNBIND:
+		case FUNCTIONFS_ENABLE:
+		case FUNCTIONFS_DISABLE:
+		case FUNCTIONFS_SETUP:
+		case FUNCTIONFS_SUSPEND:
+		case FUNCTIONFS_RESUME:
+			printf("Event %s\n", names[event->type]);
+			if (event->type == FUNCTIONFS_SETUP)
+				handle_setup(&event->u.setup);
+			break;
+
+		default:
+			printf("Event %03u (unknown)\n", event->type);
+		}
+
+	return nbytes;
+}
+
+static void ep0_init(struct thread *t)
+{
+	ssize_t ret;
+
+	info("%s: writing descriptors\n", t->filename);
+	ret = write(t->fd, &descriptors, sizeof descriptors);
+	die_on(ret < 0, "%s: write: descriptors", t->filename);
+
+	info("%s: writing strings\n", t->filename);
+	ret = write(t->fd, &strings, sizeof strings);
+	die_on(ret < 0, "%s: write: strings", t->filename);
+}
+
+
+/******************** Main **************************************************/
+
+int main(void)
+{
+	unsigned i;
+
+	/* XXX TODO: Argument parsing missing */
+
+	init_thread(threads);
+	ep0_init(threads);
+
+	for (i = 1; i < sizeof threads / sizeof *threads; ++i)
+		init_thread(threads + i);
+
+	for (i = 1; i < sizeof threads / sizeof *threads; ++i)
+		start_thread(threads + i);
+
+	start_thread_helper(threads);
+
+	for (i = 1; i < sizeof threads / sizeof *threads; ++i)
+		join_thread(threads + i);
+
+	return 0;
+}
-- 
1.7.0


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [PATCHv2 7/8] USB: testusb: an USB testing application
  2010-04-08  0:29               ` [PATCH 7/8] USB: testusb: imported David Brownell's USB testing application Greg KH
@ 2010-04-09 19:21                 ` Michal Nazarewicz
  2010-04-09 19:21                   ` [PATCHv2 8/8] USB: testusb: testusb compatibility with FunctionFS gadget Michal Nazarewicz
  2010-04-10 22:51                   ` [PATCHv2 7/8] USB: testusb: an USB testing application David Brownell
  2010-04-14  9:30                 ` [PATCH 7/8] USB: testusb: imported David Brownell's " David Brownell
  1 sibling, 2 replies; 47+ messages in thread
From: Michal Nazarewicz @ 2010-04-09 19:21 UTC (permalink / raw)
  To: linux-usb
  Cc: Michal Nazarewicz, Michal Nazarewicz, Greg KH, Kyungmin Park,
	Marek Szyprowski, linux-kernel, David Brownell

From: David Brownell <dbrownell@users.sourceforge.net>

The testusb application can be used to test various USB gadget
that implment the source/sink intrerface.

This is an import of David Brownel's tool that could be found
on the Internet.

Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com>
Cc: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
---
 tools/usb/testusb.c |  427 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 427 insertions(+), 0 deletions(-)
 create mode 100644 tools/usb/testusb.c

> On Wed, Apr 07, 2010 at 03:41:34PM +0200, Michal Nazarewicz wrote:
>> The testusb application can be used to test various USB gadget
>> that implment the source/sink intrerface.
>>
>> Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com>

On Thu, 08 Apr 2010 02:29:52 +0200, Greg KH <greg@kroah.com> wrote:
> You should put a:
> 	From: David Brownell <dbrownell@users.sourceforge.net>
> as the first line of the body of this patch, so it properly shows up as
> David's code.

Ah, right, sorry.  Fixed now. :)

> I also would like to get an ack from David that he doesn't mind his code
> moving into the kernel here (personally I think it's a good thing to be
> here.)

So, David, if you're happening to read this, would you ack? :)

diff --git a/tools/usb/testusb.c b/tools/usb/testusb.c
new file mode 100644
index 0000000..28e25ca
--- /dev/null
+++ b/tools/usb/testusb.c
@@ -0,0 +1,427 @@
+/* $(CROSS_COMPILE)cc -Wall -g -lpthread -o testusb testusb.c */
+
+/*
+ * Copyright (c) 2002 by David Brownell
+ * 
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ftw.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <sys/ioctl.h>
+#include <linux/usbdevice_fs.h>
+
+/*-------------------------------------------------------------------------*/
+
+#define	TEST_CASES	30
+
+// FIXME make these public somewhere; usbdevfs.h?
+
+struct usbtest_param {
+	// inputs
+	unsigned		test_num;	/* 0..(TEST_CASES-1) */
+	unsigned		iterations;
+	unsigned		length;
+	unsigned		vary;
+	unsigned		sglen;
+
+	// outputs
+	struct timeval		duration;
+};
+#define USBTEST_REQUEST	_IOWR('U', 100, struct usbtest_param)
+
+/*-------------------------------------------------------------------------*/
+
+/* #include <linux/usb_ch9.h> */
+
+struct usb_device_descriptor {
+	__u8  bLength;
+	__u8  bDescriptorType;
+	__u16 bcdUSB;
+	__u8  bDeviceClass;
+	__u8  bDeviceSubClass;
+	__u8  bDeviceProtocol;
+	__u8  bMaxPacketSize0;
+	__u16 idVendor;
+	__u16 idProduct;
+	__u16 bcdDevice;
+	__u8  iManufacturer;
+	__u8  iProduct;
+	__u8  iSerialNumber;
+	__u8  bNumConfigurations;
+} __attribute__ ((packed));
+
+enum usb_device_speed {
+	USB_SPEED_UNKNOWN = 0,			/* enumerating */
+	USB_SPEED_LOW, USB_SPEED_FULL,		/* usb 1.1 */
+	USB_SPEED_HIGH				/* usb 2.0 */
+};
+
+/*-------------------------------------------------------------------------*/
+
+static char *speed (enum usb_device_speed s)
+{
+	switch (s) {
+	case USB_SPEED_UNKNOWN:	return "unknown";
+	case USB_SPEED_LOW:	return "low";
+	case USB_SPEED_FULL:	return "full";
+	case USB_SPEED_HIGH:	return "high";
+	default:		return "??";
+	}
+}
+
+struct testdev {
+	struct testdev		*next;
+	char			*name;
+	pthread_t		thread;
+	enum usb_device_speed	speed;
+	unsigned		ifnum : 8;
+	unsigned		forever : 1;
+	int			test;
+
+	struct usbtest_param	param;
+};
+static struct testdev		*testdevs;
+
+static int is_testdev (struct usb_device_descriptor *dev)
+{
+	/* FX2 with (tweaked) bulksrc firmware */
+	if (dev->idVendor == 0x0547 && dev->idProduct == 0x1002)
+		return 1;
+
+	/*----------------------------------------------------*/
+
+	/* devices that start up using the EZ-USB default device and
+	 * which we can use after loading simple firmware.  hotplug
+	 * can fxload it, and then run this test driver.
+	 *
+	 * we return false positives in two cases:
+	 * - the device has a "real" driver (maybe usb-serial) that
+	 *   renumerates.  the device should vanish quickly.
+	 * - the device doesn't have the test firmware installed.
+	 */
+
+	/* generic EZ-USB FX controller */
+	if (dev->idVendor == 0x0547 && dev->idProduct == 0x2235)
+		return 1;
+
+	/* generic EZ-USB FX2 controller */
+	if (dev->idVendor == 0x04b4 && dev->idProduct == 0x8613)
+		return 1;
+
+	/* CY3671 development board with EZ-USB FX */
+	if (dev->idVendor == 0x0547 && dev->idProduct == 0x0080)
+		return 1;
+
+	/* Keyspan 19Qi uses an21xx (original EZ-USB) */
+	if (dev->idVendor == 0x06cd && dev->idProduct == 0x010b)
+		return 1;
+
+	/*----------------------------------------------------*/
+
+	/* "gadget zero", Linux-USB test software */
+	if (dev->idVendor == 0x0525 && dev->idProduct == 0xa4a0)
+		return 1;
+
+	/* user mode subset of that */
+	if (dev->idVendor == 0x0525 && dev->idProduct == 0xa4a4)
+		return 1;
+
+	/* iso version of usermode code */
+	if (dev->idVendor == 0x0525 && dev->idProduct == 0xa4a3)
+		return 1;
+
+	/* some GPL'd test firmware uses these IDs */
+
+	if (dev->idVendor == 0xfff0 && dev->idProduct == 0xfff0)
+		return 1;
+
+	/*----------------------------------------------------*/
+
+	/* iBOT2 high speed webcam */
+	if (dev->idVendor == 0x0b62 && dev->idProduct == 0x0059)
+		return 1;
+
+	return 0;
+}
+
+static int find_testdev (const char *name, const struct stat *sb, int flag)
+{
+	int				fd;
+	struct usb_device_descriptor	dev;
+
+	if (flag != FTW_F)
+		return 0;
+	/* ignore /proc/bus/usb/{devices,drivers} */
+	if (strrchr (name, '/')[1] == 'd')
+		return 0;
+
+	if ((fd = open (name, O_RDONLY)) < 0) {
+		perror ("can't open dev file r/o");
+		return 0;
+	}
+	if (read (fd, &dev, sizeof dev) != sizeof dev)
+		fputs ("short devfile read!\n", stderr);
+	else if (is_testdev (&dev)) {
+		struct testdev		*entry;
+
+		if ((entry = calloc (1, sizeof *entry)) == 0) {
+			fputs ("no mem!\n", stderr);
+			goto done;
+		}
+		entry->name = strdup (name);
+		if (!entry->name) {
+			free (entry);
+			goto done;
+		}
+
+		// FIXME better to look at each interface and ask if it's
+		// bound to 'usbtest', rather than assume interface 0
+		entry->ifnum = 0;
+
+		// FIXME ask usbfs what speed; update USBDEVFS_CONNECTINFO
+		// so it tells about high speed etc
+
+		fprintf (stderr, "%s speed\t%s\n",
+				speed (entry->speed), entry->name);
+
+		entry->next = testdevs;
+		testdevs = entry;
+	}
+
+done:
+	close (fd);
+	return 0;
+}
+
+static int
+usbdev_ioctl (int fd, int ifno, unsigned request, void *param)
+{
+	struct usbdevfs_ioctl	wrapper;
+
+	wrapper.ifno = ifno;
+	wrapper.ioctl_code = request;
+	wrapper.data = param;
+
+	return ioctl (fd, USBDEVFS_IOCTL, &wrapper);
+}
+
+static void *handle_testdev (void *arg)
+{
+	struct testdev		*dev = arg;
+	int			fd, i;
+	int			status;
+
+	if ((fd = open (dev->name, O_RDWR)) < 0) {
+		perror ("can't open dev file r/w");
+		return 0;
+	}
+
+restart:
+	for (i = 0; i < TEST_CASES; i++) {
+		if (dev->test != -1 && dev->test != i)
+			continue;
+		dev->param.test_num = i;
+
+		status = usbdev_ioctl (fd, dev->ifnum,
+				USBTEST_REQUEST, &dev->param);
+		if (status < 0 && errno == EOPNOTSUPP)
+			continue;
+
+		/* FIXME need a "syslog it" option for background testing */
+
+		/* NOTE: each thread emits complete lines; no fragments! */
+		if (status < 0) {
+			char	buf [80];
+			int	err = errno;
+
+			if (strerror_r (errno, buf, sizeof buf)) {
+				snprintf (buf, sizeof buf, "error %d", err);
+				errno = err;
+			}
+			printf ("%s test %d --> %d (%s)\n",
+				dev->name, i, errno, buf);
+		} else
+			printf ("%s test %d, %4d.%.06d secs\n", dev->name, i,
+				(int) dev->param.duration.tv_sec,
+				(int) dev->param.duration.tv_usec);
+
+		fflush (stdout);
+	}
+	if (dev->forever)
+		goto restart;
+
+	close (fd);
+	return arg;
+}
+
+int main (int argc, char **argv)
+{
+	int			c;
+	struct testdev		*entry;
+	char			*device;
+	int			all = 0, forever = 0, not = 0;
+	int			test = -1 /* all */;
+	struct usbtest_param	param;
+
+	/* pick defaults that works with all speeds, without short packets.
+	 *
+	 * Best per-frame data rates:
+	 *     high speed, bulk       512 * 13 * 8 = 53248
+	 *                 interrupt 1024 *  3 * 8 = 24576
+	 *     full speed, bulk/intr   64 * 19     =  1216
+	 *                 interrupt   64 *  1     =    64
+	 *      low speed, interrupt    8 *  1     =     8
+	 */
+	param.iterations = 1000;
+	param.length = 512;
+	param.vary = 512;
+	param.sglen = 32;
+
+	/* for easy use when hotplugging */
+	device = getenv ("DEVICE");
+
+	while ((c = getopt (argc, argv, "D:ac:g:hns:t:v:")) != EOF)
+	switch (c) {
+	case 'D':	/* device, if only one */
+		device = optarg;
+		continue;
+	case 'a':	/* use all devices */
+		device = 0;
+		all = 1;
+		continue;
+	case 'c':	/* count iterations */
+		param.iterations = atoi (optarg);
+		if (param.iterations < 0)
+			goto usage;
+		continue;
+	case 'g':	/* scatter/gather entries */
+		param.sglen = atoi (optarg);
+		if (param.sglen < 0)
+			goto usage;
+		continue;
+	case 'l':	/* loop forever */
+		forever = 1;
+		continue;
+	case 'n':	/* no test running! */
+		not = 1;
+		continue;
+	case 's':	/* size of packet */
+		param.length = atoi (optarg);
+		if (param.length < 0)
+			goto usage;
+		continue;
+	case 't':	/* run just one test */
+		test = atoi (optarg);
+		if (test < 0)
+			goto usage;
+		continue;
+	case 'v':	/* vary packet size by ... */
+		param.vary = atoi (optarg);
+		if (param.vary < 0)
+			goto usage;
+		continue;
+	case '?':
+	case 'h':
+	default:
+usage:
+		fprintf (stderr, "usage: %s [-an] [-D dev]\n"
+			"\t[-c iterations]  [-t testnum]\n"
+			"\t[-s packetsize] [-g sglen] [-v vary]\n",
+			argv [0]);
+		return 1;
+	}
+	if (optind != argc)
+		goto usage;
+	if (!all && !device) {
+		fprintf (stderr, "must specify '-a' or '-D dev', "
+			"or DEVICE=/proc/bus/usb/BBB/DDD in env\n");
+		goto usage;
+	}
+
+	if ((c = open ("/proc/bus/usb/devices", O_RDONLY)) < 0) {
+		fputs ("usbfs files are missing\n", stderr);
+		return -1;
+	}
+
+	/* collect and list the test devices */
+	if (ftw ("/proc/bus/usb", find_testdev, 3) != 0) {
+		fputs ("ftw failed; is usbfs missing?\n", stderr);
+		return -1;
+	}
+
+	/* quit, run single test, or create test threads */
+	if (!testdevs && !device) {
+		fputs ("no test devices recognized\n", stderr);
+		return -1;
+	}
+	if (not)
+		return 0;
+	if (testdevs && testdevs->next == 0 && !device)
+		device = testdevs->name;
+	for (entry = testdevs; entry; entry = entry->next) {
+		int	status;
+
+		entry->param = param;
+		entry->forever = forever;
+		entry->test = test;
+
+		if (device) {
+			if (strcmp (entry->name, device))
+				continue;
+			return handle_testdev (entry) != entry;
+		}
+		status = pthread_create (&entry->thread, 0, handle_testdev, entry);
+		if (status) {
+			perror ("pthread_create");
+			continue;
+		}
+	}
+	if (device) {
+		struct testdev		dev;
+
+		/* kernel can recognize test devices we don't */
+		fprintf (stderr, "%s: %s may see only control tests\n",
+				argv [0], device);
+
+		memset (&dev, 0, sizeof dev);
+		dev.name = device;
+		dev.param = param;
+		dev.forever = forever;
+		dev.test = test;
+		return handle_testdev (&dev) != &dev;
+	}
+
+	/* wait for tests to complete */
+	for (entry = testdevs; entry; entry = entry->next) {
+		void	*retval;
+
+		if (pthread_join (entry->thread, &retval))
+			perror ("pthread_join");
+		/* testing errors discarded! */
+	}
+
+	return 0;
+}
-- 
1.7.0


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [PATCHv2 8/8] USB: testusb: testusb compatibility with FunctionFS gadget
  2010-04-09 19:21                 ` [PATCHv2 7/8] USB: testusb: an " Michal Nazarewicz
@ 2010-04-09 19:21                   ` Michal Nazarewicz
  2010-04-10 22:51                   ` [PATCHv2 7/8] USB: testusb: an USB testing application David Brownell
  1 sibling, 0 replies; 47+ messages in thread
From: Michal Nazarewicz @ 2010-04-09 19:21 UTC (permalink / raw)
  To: linux-usb
  Cc: Michal Nazarewicz, Michal Nazarewicz, Greg KH, Kyungmin Park,
	Marek Szyprowski, linux-kernel, David Brownell

The FunctionFS gadget may provide the source/sink interface
not as the first interface (with id == 0) but some different
interface hence a code to find the interface number is
required.

(Note that you will still configure the gadget to report
idProduct == 0xa4a4 (an "echo 0xa4a4
>/sys/module/g_ffs/parameters/usb_product" should suffice) or
configure host to handle 0x0525:0xa4ac devices using the
usbtest driver.)

Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com>
Cc: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
---
 tools/usb/testusb.c |  252 ++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 181 insertions(+), 71 deletions(-)

diff --git a/tools/usb/testusb.c b/tools/usb/testusb.c
index 28e25ca..0a1a5b5 100644
--- a/tools/usb/testusb.c
+++ b/tools/usb/testusb.c
@@ -1,8 +1,10 @@
-/* $(CROSS_COMPILE)cc -Wall -g -lpthread -o testusb testusb.c */
+/* $(CROSS_COMPILE)cc -Wall -Wextra -g -lpthread -o testusb testusb.c */
 
 /*
  * Copyright (c) 2002 by David Brownell
- * 
+ * Copyright (c) 2010 by Samsung Electronics
+ * Author: Michal Nazarewicz <m.nazarewicz@samsung.com>
+ *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  * Free Software Foundation; either version 2 of the License, or (at your
@@ -25,6 +27,7 @@
 #include <pthread.h>
 #include <unistd.h>
 #include <errno.h>
+#include <limits.h>
 
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -56,6 +59,13 @@ struct usbtest_param {
 
 /* #include <linux/usb_ch9.h> */
 
+#define USB_DT_DEVICE			0x01
+#define USB_DT_INTERFACE		0x04
+
+#define USB_CLASS_PER_INTERFACE		0	/* for DeviceClass */
+#define USB_CLASS_VENDOR_SPEC		0xff
+
+
 struct usb_device_descriptor {
 	__u8  bLength;
 	__u8  bDescriptorType;
@@ -73,6 +83,19 @@ struct usb_device_descriptor {
 	__u8  bNumConfigurations;
 } __attribute__ ((packed));
 
+struct usb_interface_descriptor {
+	__u8  bLength;
+	__u8  bDescriptorType;
+
+	__u8  bInterfaceNumber;
+	__u8  bAlternateSetting;
+	__u8  bNumEndpoints;
+	__u8  bInterfaceClass;
+	__u8  bInterfaceSubClass;
+	__u8  bInterfaceProtocol;
+	__u8  iInterface;
+} __attribute__ ((packed));
+
 enum usb_device_speed {
 	USB_SPEED_UNKNOWN = 0,			/* enumerating */
 	USB_SPEED_LOW, USB_SPEED_FULL,		/* usb 1.1 */
@@ -105,11 +128,42 @@ struct testdev {
 };
 static struct testdev		*testdevs;
 
-static int is_testdev (struct usb_device_descriptor *dev)
+static int testdev_ffs_ifnum(FILE *fd)
 {
+	union {
+		char buf[255];
+		struct usb_interface_descriptor intf;
+	} u;
+
+	for (;;) {
+		if (fread(u.buf, 1, 1, fd) != 1)
+			return -1;
+		if (fread(u.buf + 1, (unsigned char)u.buf[0] - 1, 1, fd) != 1)
+			return -1;
+
+		if (u.intf.bLength == sizeof u.intf
+		 && u.intf.bDescriptorType == USB_DT_INTERFACE
+		 && u.intf.bNumEndpoints == 2
+		 && u.intf.bInterfaceClass == USB_CLASS_VENDOR_SPEC
+		 && u.intf.bInterfaceSubClass == 0
+		 && u.intf.bInterfaceProtocol == 0)
+			return (unsigned char)u.intf.bInterfaceNumber;
+	}
+}
+
+static int testdev_ifnum(FILE *fd)
+{
+	struct usb_device_descriptor dev;
+
+	if (fread(&dev, sizeof dev, 1, fd) != 1)
+		return -1;
+
+	if (dev.bLength != sizeof dev || dev.bDescriptorType != USB_DT_DEVICE)
+		return -1;
+
 	/* FX2 with (tweaked) bulksrc firmware */
-	if (dev->idVendor == 0x0547 && dev->idProduct == 0x1002)
-		return 1;
+	if (dev.idVendor == 0x0547 && dev.idProduct == 0x1002)
+		return 0;
 
 	/*----------------------------------------------------*/
 
@@ -124,95 +178,108 @@ static int is_testdev (struct usb_device_descriptor *dev)
 	 */
 
 	/* generic EZ-USB FX controller */
-	if (dev->idVendor == 0x0547 && dev->idProduct == 0x2235)
-		return 1;
+	if (dev.idVendor == 0x0547 && dev.idProduct == 0x2235)
+		return 0;
 
 	/* generic EZ-USB FX2 controller */
-	if (dev->idVendor == 0x04b4 && dev->idProduct == 0x8613)
-		return 1;
+	if (dev.idVendor == 0x04b4 && dev.idProduct == 0x8613)
+		return 0;
 
 	/* CY3671 development board with EZ-USB FX */
-	if (dev->idVendor == 0x0547 && dev->idProduct == 0x0080)
-		return 1;
+	if (dev.idVendor == 0x0547 && dev.idProduct == 0x0080)
+		return 0;
 
 	/* Keyspan 19Qi uses an21xx (original EZ-USB) */
-	if (dev->idVendor == 0x06cd && dev->idProduct == 0x010b)
-		return 1;
+	if (dev.idVendor == 0x06cd && dev.idProduct == 0x010b)
+		return 0;
 
 	/*----------------------------------------------------*/
 
 	/* "gadget zero", Linux-USB test software */
-	if (dev->idVendor == 0x0525 && dev->idProduct == 0xa4a0)
-		return 1;
+	if (dev.idVendor == 0x0525 && dev.idProduct == 0xa4a0)
+		return 0;
 
 	/* user mode subset of that */
-	if (dev->idVendor == 0x0525 && dev->idProduct == 0xa4a4)
-		return 1;
+	if (dev.idVendor == 0x0525 && dev.idProduct == 0xa4a4)
+		return testdev_ffs_ifnum(fd);
+		/* return 0; */
 
 	/* iso version of usermode code */
-	if (dev->idVendor == 0x0525 && dev->idProduct == 0xa4a3)
-		return 1;
+	if (dev.idVendor == 0x0525 && dev.idProduct == 0xa4a3)
+		return 0;
 
 	/* some GPL'd test firmware uses these IDs */
 
-	if (dev->idVendor == 0xfff0 && dev->idProduct == 0xfff0)
-		return 1;
+	if (dev.idVendor == 0xfff0 && dev.idProduct == 0xfff0)
+		return 0;
 
 	/*----------------------------------------------------*/
 
 	/* iBOT2 high speed webcam */
-	if (dev->idVendor == 0x0b62 && dev->idProduct == 0x0059)
-		return 1;
+	if (dev.idVendor == 0x0b62 && dev.idProduct == 0x0059)
+		return 0;
 
-	return 0;
+	/*----------------------------------------------------*/
+
+	/* the FunctionFS gadget can have the source/sink interface
+	 * anywhere.  We look for an interface descriptor that match
+	 * what we expect.  We ignore configuratiens thou. */
+
+	if (dev.idVendor == 0x0525 && dev.idProduct == 0xa4ac
+	 && (dev.bDeviceClass == USB_CLASS_PER_INTERFACE
+	  || dev.bDeviceClass == USB_CLASS_VENDOR_SPEC))
+		return testdev_ffs_ifnum(fd);
+
+	return -1;
 }
 
-static int find_testdev (const char *name, const struct stat *sb, int flag)
+static int find_testdev(const char *name, const struct stat *sb, int flag)
 {
-	int				fd;
-	struct usb_device_descriptor	dev;
+	FILE				*fd;
+	int				ifnum;
+	struct testdev			*entry;
+
+	(void)sb; /* unused */
 
 	if (flag != FTW_F)
 		return 0;
 	/* ignore /proc/bus/usb/{devices,drivers} */
-	if (strrchr (name, '/')[1] == 'd')
+	if (strrchr(name, '/')[1] == 'd')
 		return 0;
 
-	if ((fd = open (name, O_RDONLY)) < 0) {
-		perror ("can't open dev file r/o");
+	fd = fopen(name, "rb");
+	if (!fd) {
+		perror(name);
 		return 0;
 	}
-	if (read (fd, &dev, sizeof dev) != sizeof dev)
-		fputs ("short devfile read!\n", stderr);
-	else if (is_testdev (&dev)) {
-		struct testdev		*entry;
-
-		if ((entry = calloc (1, sizeof *entry)) == 0) {
-			fputs ("no mem!\n", stderr);
-			goto done;
-		}
-		entry->name = strdup (name);
-		if (!entry->name) {
-			free (entry);
-			goto done;
-		}
-
-		// FIXME better to look at each interface and ask if it's
-		// bound to 'usbtest', rather than assume interface 0
-		entry->ifnum = 0;
 
-		// FIXME ask usbfs what speed; update USBDEVFS_CONNECTINFO
-		// so it tells about high speed etc
+	ifnum = testdev_ifnum(fd);
+	fclose(fd);
+	if (ifnum < 0)
+		return 0;
 
-		fprintf (stderr, "%s speed\t%s\n",
-				speed (entry->speed), entry->name);
+	entry = calloc(1, sizeof *entry);
+	if (!entry)
+		goto nomem;
 
-		entry->next = testdevs;
-		testdevs = entry;
+	entry->name = strdup(name);
+	if (!entry->name) {
+		free(entry);
+nomem:
+		perror("malloc");
+		return 0;
 	}
 
-done:
-	close (fd);
+	entry->ifnum = ifnum;
+
+	/* FIXME ask usbfs what speed; update USBDEVFS_CONNECTINFO so
+	 * it tells about high speed etc */
+
+	fprintf(stderr, "%s speed\t%s\t%u\n",
+		speed(entry->speed), entry->name, entry->ifnum);
+
+	entry->next = testdevs;
+	testdevs = entry;
 	return 0;
 }
 
@@ -277,11 +344,51 @@ restart:
 	return arg;
 }
 
+static const char *usbfs_dir_find(void)
+{
+	static char usbfs_path_0[] = "/dev/bus/usb/devices";
+	static char usbfs_path_1[] = "/proc/bus/usb/devices";
+
+	static char *const usbfs_paths[] = {
+		usbfs_path_0, usbfs_path_1
+	};
+
+	static char *const *
+		end = usbfs_paths + sizeof usbfs_paths / sizeof *usbfs_paths;
+
+	char *const *it = usbfs_paths;
+	do {
+		int fd = open(*it, O_RDONLY);
+		close(fd);
+		if (fd >= 0) {
+			strrchr(*it, '/')[0] = '\0';
+			return *it;
+		}
+	} while (++it != end);
+
+	return NULL;
+}
+
+static int parse_num(unsigned *num, const char *str)
+{
+	unsigned long val;
+	char *end;
+
+	errno = 0;
+	val = strtoul(str, &end, 0);
+	if (errno || *end || val > UINT_MAX)
+		return -1;
+	*num = val;
+	return 0;
+}
+
 int main (int argc, char **argv)
 {
+
 	int			c;
 	struct testdev		*entry;
 	char			*device;
+	const char		*usbfs_dir = NULL;
 	int			all = 0, forever = 0, not = 0;
 	int			test = -1 /* all */;
 	struct usbtest_param	param;
@@ -303,23 +410,24 @@ int main (int argc, char **argv)
 	/* for easy use when hotplugging */
 	device = getenv ("DEVICE");
 
-	while ((c = getopt (argc, argv, "D:ac:g:hns:t:v:")) != EOF)
+	while ((c = getopt (argc, argv, "D:aA:c:g:hns:t:v:")) != EOF)
 	switch (c) {
 	case 'D':	/* device, if only one */
 		device = optarg;
 		continue;
+	case 'A':	/* use all devices with specified usbfs dir */
+		usbfs_dir = optarg;
+		/* FALL THROUGH */
 	case 'a':	/* use all devices */
-		device = 0;
+		device = NULL;
 		all = 1;
 		continue;
 	case 'c':	/* count iterations */
-		param.iterations = atoi (optarg);
-		if (param.iterations < 0)
+		if (parse_num(&param.iterations, optarg))
 			goto usage;
 		continue;
 	case 'g':	/* scatter/gather entries */
-		param.sglen = atoi (optarg);
-		if (param.sglen < 0)
+		if (parse_num(&param.sglen, optarg))
 			goto usage;
 		continue;
 	case 'l':	/* loop forever */
@@ -329,8 +437,7 @@ int main (int argc, char **argv)
 		not = 1;
 		continue;
 	case 's':	/* size of packet */
-		param.length = atoi (optarg);
-		if (param.length < 0)
+		if (parse_num(&param.length, optarg))
 			goto usage;
 		continue;
 	case 't':	/* run just one test */
@@ -339,15 +446,14 @@ int main (int argc, char **argv)
 			goto usage;
 		continue;
 	case 'v':	/* vary packet size by ... */
-		param.vary = atoi (optarg);
-		if (param.vary < 0)
+		if (parse_num(&param.vary, optarg))
 			goto usage;
 		continue;
 	case '?':
 	case 'h':
 	default:
 usage:
-		fprintf (stderr, "usage: %s [-an] [-D dev]\n"
+		fprintf (stderr, "usage: %s [-n] [-D dev | -a | -A usbfs-dir]\n"
 			"\t[-c iterations]  [-t testnum]\n"
 			"\t[-s packetsize] [-g sglen] [-v vary]\n",
 			argv [0]);
@@ -361,13 +467,17 @@ usage:
 		goto usage;
 	}
 
-	if ((c = open ("/proc/bus/usb/devices", O_RDONLY)) < 0) {
-		fputs ("usbfs files are missing\n", stderr);
-		return -1;
+	/* Find usbfs mount point */
+	if (!usbfs_dir) {
+		usbfs_dir = usbfs_dir_find();
+		if (!usbfs_dir) {
+			fputs ("usbfs files are missing\n", stderr);
+			return -1;
+		}
 	}
 
 	/* collect and list the test devices */
-	if (ftw ("/proc/bus/usb", find_testdev, 3) != 0) {
+	if (ftw (usbfs_dir, find_testdev, 3) != 0) {
 		fputs ("ftw failed; is usbfs missing?\n", stderr);
 		return -1;
 	}
-- 
1.7.0


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* Re: [PATCHv2 7/8] USB: testusb: an USB testing application
  2010-04-09 19:21                 ` [PATCHv2 7/8] USB: testusb: an " Michal Nazarewicz
  2010-04-09 19:21                   ` [PATCHv2 8/8] USB: testusb: testusb compatibility with FunctionFS gadget Michal Nazarewicz
@ 2010-04-10 22:51                   ` David Brownell
  1 sibling, 0 replies; 47+ messages in thread
From: David Brownell @ 2010-04-10 22:51 UTC (permalink / raw)
  To: Michal Nazarewicz
  Cc: linux-usb, Michal Nazarewicz, Greg KH, Kyungmin Park,
	Marek Szyprowski, linux-kernel, David Brownell

On Friday 09 April 2010, Michal Nazarewicz wrote:
> > I also would like to get an ack from David that he doesn't mind his code
> > moving into the kernel here (personally I think it's a good thing to be
> > here.)


Fine by me.  It might seee more miprovements this way too.


^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCHv2 2/8] fs/timerfd.c: make use of wait_event_interruptible_locked_irq()
  2010-04-09 19:21   ` [PATCHv2 2/8] fs/timerfd.c: make use of wait_event_interruptible_locked_irq() Michal Nazarewicz
  2010-04-09 19:21     ` [PATCHv2 3/8] USB: gadget: __init and __exit tags removed Michal Nazarewicz
@ 2010-04-11 14:31     ` Thomas Gleixner
  2010-04-11 19:16         ` Michal Nazarewicz
  1 sibling, 1 reply; 47+ messages in thread
From: Thomas Gleixner @ 2010-04-11 14:31 UTC (permalink / raw)
  To: Michal Nazarewicz
  Cc: linux-usb, Michal Nazarewicz, Davide Libenzi, Greg KH,
	Kyungmin Park, Marek Szyprowski, linux-fsdevel, linux-kernel

On Fri, 9 Apr 2010, Michal Nazarewicz wrote:

> This patch modifies the fs/timerfd.c to use the newly created
> wait_event_interruptible_locked_irq() macro.  This replaces an open
> code implementation with a single macro call.

And thereby breaks the code. :(

> Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com>
> Cc: Kyungmin Park <kyungmin.park@samsung.com>
> Cc: Marek Szyprowski <m.szyprowski@samsung.com>
> ---
>  fs/timerfd.c |   22 ++--------------------
>  1 files changed, 2 insertions(+), 20 deletions(-)
> 
> diff --git a/fs/timerfd.c b/fs/timerfd.c
> index 1bfc95a..4d2c371 100644
> --- a/fs/timerfd.c
> +++ b/fs/timerfd.c
> @@ -109,31 +109,13 @@ static ssize_t timerfd_read(struct file *file, char __user *buf, size_t count,
>  	struct timerfd_ctx *ctx = file->private_data;
>  	ssize_t res;
>  	u64 ticks = 0;
> -	DECLARE_WAITQUEUE(wait, current);
>  
>  	if (count < sizeof(ticks))
>  		return -EINVAL;
>  	spin_lock_irq(&ctx->wqh.lock);
>  	res = -EAGAIN;
> -	if (!ctx->ticks && !(file->f_flags & O_NONBLOCK)) {
> -		__add_wait_queue(&ctx->wqh, &wait);
> -		for (res = 0;;) {
> -			set_current_state(TASK_INTERRUPTIBLE);
> -			if (ctx->ticks) {
> -				res = 0;
> -				break;
> -			}
> -			if (signal_pending(current)) {
> -				res = -ERESTARTSYS;
> -				break;
> -			}
> -			spin_unlock_irq(&ctx->wqh.lock);
> -			schedule();
> -			spin_lock_irq(&ctx->wqh.lock);
> -		}
> -		__remove_wait_queue(&ctx->wqh, &wait);
> -		__set_current_state(TASK_RUNNING);
> -	}
> +	if (!(file->f_flags & O_NONBLOCK))
> +		wait_event_interruptible_locked_irq(ctx->wqh, ctx->ticks);

With this change we return -EAGAIN instead of -ERESTARTSYS when the
wait got interrupted by a signal. That means instead of restarting the
syscall we return -EAGAIN to user space.

You need to return that information from wait_event.....().

Thanks,

	tglx

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCHv2 1/8] wait_event_interruptible_locked() interface
@ 2010-04-11 15:02     ` Thomas Gleixner
  0 siblings, 0 replies; 47+ messages in thread
From: Thomas Gleixner @ 2010-04-11 15:02 UTC (permalink / raw)
  To: Michal Nazarewicz
  Cc: linux-usb, Michal Nazarewicz, Davide Libenzi, Greg KH,
	Kyungmin Park, Marek Szyprowski, linux-fsdevel, linux-kernel



On Fri, 9 Apr 2010, Michal Nazarewicz wrote:

> New wait_event_interruptible{,_exclusive}_locked{,_irq,_irqsave}
> macros added.  They work just like versions without _locked* suffix

The _irqsave variant is really not necessary. It's actively wrong.

If you go to wait then the state _before_ acquiring the waitqueue head
lock must be irqs enabled. Otherwise you would schedule with
interrupts disabled after the unlock_irqrestore which is a BUG.

So if there is code which uses spin_lock_irqsave() in the wait path
then this code is wrong and needs to be fixed to spin_(un)lock_irq()
first instead of adding a bogus interface.

> +
> +#define __wait_event_interruptible_locked(wq, condition, ret, exclusive, lock, unlock, lock_args) \

That will also simplify this to (wq, condition, exclusive, lockmode)

> +do {									\
> +	DEFINE_WAIT(__wait);						\
> +									\
> +	if (exclusive)							\
> +		__wait.flags |= WQ_FLAG_EXCLUSIVE;			\
> +	else								\
> +		__wait.flags &= ~WQ_FLAG_EXCLUSIVE;			\
> +	__add_wait_queue_tail(&(wq), &__wait);				\
> +									\
> +	do {								\
> +		set_current_state(TASK_INTERRUPTIBLE);			\
> +		if (signal_pending(current)) {				\
> +			ret = -ERESTARTSYS;				\
> +			break;						\
> +		}							\
> +		spin_unlock ##unlock lock_args;				\
> +		schedule();						\
> +		spin_lock ##lock lock_args;				\
> +	} while (!(condition));						\
> +	__remove_wait_queue(&(wq), &__wait);				\
> +	__set_current_state(TASK_RUNNING);				\
> +} while (0)
> +
> +
> +/**
> + * wait_event_interruptible_locked - sleep until a condition gets true
> + * @wq: the waitqueue to wait on
> + * @condition: a C expression for the event to wait for
> + *
> + * The process is put to sleep (TASK_INTERRUPTIBLE) until the
> + * @condition evaluates to true or a signal is received.
> + * The @condition is checked each time the waitqueue @wq is woken up.
> + *
> + * It must be called with wq.lock being held.  This spinlock is
> + * unlocked while sleeping but @condition testing is done while lock
> + * is held and when this macro exits the lock is held.
> + *
> + * The lock is locked/unlocked using spin_lock()/spin_unlock()
> + * functions which must match the way they are locked/unlocked outside
> + * of this macro.
> + *
> + * wake_up_locked() has to be called after changing any variable that could
> + * change the result of the wait condition.
> + *
> + * The function will return -ERESTARTSYS if it was interrupted by a
> + * signal and 0 if @condition evaluated to true.
> + */
> +#define wait_event_interruptible_locked(wq, condition)			\
> +({									\
> +	int __ret = 0;							\
> +	if (!(condition))						\
> +		__wait_event_interruptible_locked(wq, condition, __ret, 0, , , (&(wq).lock)); \

  I had to look more than once to figure out how that code might
  return anything else than 0. Can we please change that to

  if (!(condition))
     __ret = __wait_.....();

  to make that less confusing ?   

> +	__ret;								\
> +})

Thanks,

	tglx

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCHv2 1/8] wait_event_interruptible_locked() interface
@ 2010-04-11 15:02     ` Thomas Gleixner
  0 siblings, 0 replies; 47+ messages in thread
From: Thomas Gleixner @ 2010-04-11 15:02 UTC (permalink / raw)
  To: Michal Nazarewicz
  Cc: linux-usb-u79uwXL29TY76Z2rM5mHXA, Michal Nazarewicz,
	Davide Libenzi, Greg KH, Kyungmin Park, Marek Szyprowski,
	linux-fsdevel-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA



On Fri, 9 Apr 2010, Michal Nazarewicz wrote:

> New wait_event_interruptible{,_exclusive}_locked{,_irq,_irqsave}
> macros added.  They work just like versions without _locked* suffix

The _irqsave variant is really not necessary. It's actively wrong.

If you go to wait then the state _before_ acquiring the waitqueue head
lock must be irqs enabled. Otherwise you would schedule with
interrupts disabled after the unlock_irqrestore which is a BUG.

So if there is code which uses spin_lock_irqsave() in the wait path
then this code is wrong and needs to be fixed to spin_(un)lock_irq()
first instead of adding a bogus interface.

> +
> +#define __wait_event_interruptible_locked(wq, condition, ret, exclusive, lock, unlock, lock_args) \

That will also simplify this to (wq, condition, exclusive, lockmode)

> +do {									\
> +	DEFINE_WAIT(__wait);						\
> +									\
> +	if (exclusive)							\
> +		__wait.flags |= WQ_FLAG_EXCLUSIVE;			\
> +	else								\
> +		__wait.flags &= ~WQ_FLAG_EXCLUSIVE;			\
> +	__add_wait_queue_tail(&(wq), &__wait);				\
> +									\
> +	do {								\
> +		set_current_state(TASK_INTERRUPTIBLE);			\
> +		if (signal_pending(current)) {				\
> +			ret = -ERESTARTSYS;				\
> +			break;						\
> +		}							\
> +		spin_unlock ##unlock lock_args;				\
> +		schedule();						\
> +		spin_lock ##lock lock_args;				\
> +	} while (!(condition));						\
> +	__remove_wait_queue(&(wq), &__wait);				\
> +	__set_current_state(TASK_RUNNING);				\
> +} while (0)
> +
> +
> +/**
> + * wait_event_interruptible_locked - sleep until a condition gets true
> + * @wq: the waitqueue to wait on
> + * @condition: a C expression for the event to wait for
> + *
> + * The process is put to sleep (TASK_INTERRUPTIBLE) until the
> + * @condition evaluates to true or a signal is received.
> + * The @condition is checked each time the waitqueue @wq is woken up.
> + *
> + * It must be called with wq.lock being held.  This spinlock is
> + * unlocked while sleeping but @condition testing is done while lock
> + * is held and when this macro exits the lock is held.
> + *
> + * The lock is locked/unlocked using spin_lock()/spin_unlock()
> + * functions which must match the way they are locked/unlocked outside
> + * of this macro.
> + *
> + * wake_up_locked() has to be called after changing any variable that could
> + * change the result of the wait condition.
> + *
> + * The function will return -ERESTARTSYS if it was interrupted by a
> + * signal and 0 if @condition evaluated to true.
> + */
> +#define wait_event_interruptible_locked(wq, condition)			\
> +({									\
> +	int __ret = 0;							\
> +	if (!(condition))						\
> +		__wait_event_interruptible_locked(wq, condition, __ret, 0, , , (&(wq).lock)); \

  I had to look more than once to figure out how that code might
  return anything else than 0. Can we please change that to

  if (!(condition))
     __ret = __wait_.....();

  to make that less confusing ?   

> +	__ret;								\
> +})

Thanks,

	tglx
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCHv2 2/8] fs/timerfd.c: make use of wait_event_interruptible_locked_irq()
@ 2010-04-11 19:16         ` Michal Nazarewicz
  0 siblings, 0 replies; 47+ messages in thread
From: Michal Nazarewicz @ 2010-04-11 19:16 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Michal Nazarewicz, linux-usb, Davide Libenzi, Greg KH,
	Kyungmin Park, Marek Szyprowski, linux-fsdevel, linux-kernel

> On Fri, 9 Apr 2010, Michal Nazarewicz wrote:
>> diff --git a/fs/timerfd.c b/fs/timerfd.c
>> index 1bfc95a..4d2c371 100644
>> --- a/fs/timerfd.c
>> +++ b/fs/timerfd.c
>> @@ -109,31 +109,13 @@ static ssize_t timerfd_read(struct file *file, char __user *buf, size_t count,
>>  	struct timerfd_ctx *ctx = file->private_data;
>>  	ssize_t res;
>>  	u64 ticks = 0;
>> -	DECLARE_WAITQUEUE(wait, current);
>>  
>>  	if (count < sizeof(ticks))
>>  		return -EINVAL;
>>  	spin_lock_irq(&ctx->wqh.lock);
>>  	res = -EAGAIN;
>> -	if (!ctx->ticks && !(file->f_flags & O_NONBLOCK)) {
>> -		__add_wait_queue(&ctx->wqh, &wait);
>> -		for (res = 0;;) {
>> -			set_current_state(TASK_INTERRUPTIBLE);
>> -			if (ctx->ticks) {
>> -				res = 0;
>> -				break;
>> -			}
>> -			if (signal_pending(current)) {
>> -				res = -ERESTARTSYS;
>> -				break;
>> -			}
>> -			spin_unlock_irq(&ctx->wqh.lock);
>> -			schedule();
>> -			spin_lock_irq(&ctx->wqh.lock);
>> -		}
>> -		__remove_wait_queue(&ctx->wqh, &wait);
>> -		__set_current_state(TASK_RUNNING);
>> -	}
>> +	if (!(file->f_flags & O_NONBLOCK))
>> +		wait_event_interruptible_locked_irq(ctx->wqh, ctx->ticks);

Thomas Gleixner <tglx@linutronix.de> writes:
> With this change we return -EAGAIN instead of -ERESTARTSYS when the
> wait got interrupted by a signal. That means instead of restarting the
> syscall we return -EAGAIN to user space.

Stupid mistake, the following should fix it:

+		res = wait_event_interruptible_locked_irq(ctx->wqh, ctx->ticks);

Sorry about that.

I'll resubmit an updated patchset by the end of the week.

-- 
Best regards,                                         _     _
 .o. | Liege of Serenly Enlightened Majesty of      o' \,=./ `o
 ..o | Computer Science,  Michal "mina86" Nazarewicz   (o o)
 ooo +--<mina86-tlen.pl>--<jid:mina86-jabber.org>--ooO--(_)--Ooo--

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCHv2 2/8] fs/timerfd.c: make use of wait_event_interruptible_locked_irq()
@ 2010-04-11 19:16         ` Michal Nazarewicz
  0 siblings, 0 replies; 47+ messages in thread
From: Michal Nazarewicz @ 2010-04-11 19:16 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Michal Nazarewicz, linux-usb-u79uwXL29TY76Z2rM5mHXA,
	Davide Libenzi, Greg KH, Kyungmin Park, Marek Szyprowski,
	linux-fsdevel-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

> On Fri, 9 Apr 2010, Michal Nazarewicz wrote:
>> diff --git a/fs/timerfd.c b/fs/timerfd.c
>> index 1bfc95a..4d2c371 100644
>> --- a/fs/timerfd.c
>> +++ b/fs/timerfd.c
>> @@ -109,31 +109,13 @@ static ssize_t timerfd_read(struct file *file, char __user *buf, size_t count,
>>  	struct timerfd_ctx *ctx = file->private_data;
>>  	ssize_t res;
>>  	u64 ticks = 0;
>> -	DECLARE_WAITQUEUE(wait, current);
>>  
>>  	if (count < sizeof(ticks))
>>  		return -EINVAL;
>>  	spin_lock_irq(&ctx->wqh.lock);
>>  	res = -EAGAIN;
>> -	if (!ctx->ticks && !(file->f_flags & O_NONBLOCK)) {
>> -		__add_wait_queue(&ctx->wqh, &wait);
>> -		for (res = 0;;) {
>> -			set_current_state(TASK_INTERRUPTIBLE);
>> -			if (ctx->ticks) {
>> -				res = 0;
>> -				break;
>> -			}
>> -			if (signal_pending(current)) {
>> -				res = -ERESTARTSYS;
>> -				break;
>> -			}
>> -			spin_unlock_irq(&ctx->wqh.lock);
>> -			schedule();
>> -			spin_lock_irq(&ctx->wqh.lock);
>> -		}
>> -		__remove_wait_queue(&ctx->wqh, &wait);
>> -		__set_current_state(TASK_RUNNING);
>> -	}
>> +	if (!(file->f_flags & O_NONBLOCK))
>> +		wait_event_interruptible_locked_irq(ctx->wqh, ctx->ticks);

Thomas Gleixner <tglx-hfZtesqFncYOwBW4kG4KsQ@public.gmane.org> writes:
> With this change we return -EAGAIN instead of -ERESTARTSYS when the
> wait got interrupted by a signal. That means instead of restarting the
> syscall we return -EAGAIN to user space.

Stupid mistake, the following should fix it:

+		res = wait_event_interruptible_locked_irq(ctx->wqh, ctx->ticks);

Sorry about that.

I'll resubmit an updated patchset by the end of the week.

-- 
Best regards,                                         _     _
 .o. | Liege of Serenly Enlightened Majesty of      o' \,=./ `o
 ..o | Computer Science,  Michal "mina86" Nazarewicz   (o o)
 ooo +--<mina86-tlen.pl>--<jid:mina86-jabber.org>--ooO--(_)--Ooo--
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCHv2 1/8] wait_event_interruptible_locked() interface
  2010-04-11 15:02     ` Thomas Gleixner
  (?)
@ 2010-04-11 19:27     ` Michal Nazarewicz
  -1 siblings, 0 replies; 47+ messages in thread
From: Michal Nazarewicz @ 2010-04-11 19:27 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Michal Nazarewicz, linux-usb, Davide Libenzi, Greg KH,
	Kyungmin Park, Marek Szyprowski, linux-fsdevel, linux-kernel

> On Fri, 9 Apr 2010, Michal Nazarewicz wrote:
>> New wait_event_interruptible{,_exclusive}_locked{,_irq,_irqsave}
>> macros added.  They work just like versions without _locked* suffix

Thomas Gleixner <tglx@linutronix.de> writes:
> The _irqsave variant is really not necessary. It's actively wrong.
>
> If you go to wait then the state _before_ acquiring the waitqueue head
> lock must be irqs enabled. Otherwise you would schedule with
> interrupts disabled after the unlock_irqrestore which is a BUG.
>
> So if there is code which uses spin_lock_irqsave() in the wait path
> then this code is wrong and needs to be fixed to spin_(un)lock_irq()
> first instead of adding a bogus interface.

I haven't seen the big picture here.  Thanks for pointing that out.

>> +
>> +#define __wait_event_interruptible_locked(wq, condition, ret, exclusive, lock, unlock, lock_args) \
>
> That will also simplify this to (wq, condition, exclusive, lockmode)
>
>> +do {									\
>> +	DEFINE_WAIT(__wait);						\
>> +									\
>> +	if (exclusive)							\
>> +		__wait.flags |= WQ_FLAG_EXCLUSIVE;			\
>> +	else								\
>> +		__wait.flags &= ~WQ_FLAG_EXCLUSIVE;			\
>> +	__add_wait_queue_tail(&(wq), &__wait);				\
>> +									\
>> +	do {								\
>> +		set_current_state(TASK_INTERRUPTIBLE);			\
>> +		if (signal_pending(current)) {				\
>> +			ret = -ERESTARTSYS;				\
>> +			break;						\
>> +		}							\
>> +		spin_unlock ##unlock lock_args;				\
>> +		schedule();						\
>> +		spin_lock ##lock lock_args;				\
>> +	} while (!(condition));						\
>> +	__remove_wait_queue(&(wq), &__wait);				\
>> +	__set_current_state(TASK_RUNNING);				\
>> +} while (0)
>> +
>> +
>> +/**
>> + * wait_event_interruptible_locked - sleep until a condition gets true
>> + * @wq: the waitqueue to wait on
>> + * @condition: a C expression for the event to wait for
>> + *
>> + * The process is put to sleep (TASK_INTERRUPTIBLE) until the
>> + * @condition evaluates to true or a signal is received.
>> + * The @condition is checked each time the waitqueue @wq is woken up.
>> + *
>> + * It must be called with wq.lock being held.  This spinlock is
>> + * unlocked while sleeping but @condition testing is done while lock
>> + * is held and when this macro exits the lock is held.
>> + *
>> + * The lock is locked/unlocked using spin_lock()/spin_unlock()
>> + * functions which must match the way they are locked/unlocked outside
>> + * of this macro.
>> + *
>> + * wake_up_locked() has to be called after changing any variable that could
>> + * change the result of the wait condition.
>> + *
>> + * The function will return -ERESTARTSYS if it was interrupted by a
>> + * signal and 0 if @condition evaluated to true.
>> + */
>> +#define wait_event_interruptible_locked(wq, condition)			\
>> +({									\
>> +	int __ret = 0;							\
>> +	if (!(condition))						\
>> +		__wait_event_interruptible_locked(wq, condition, __ret, 0, , , (&(wq).lock)); \
>
>   I had to look more than once to figure out how that code might
>   return anything else than 0. Can we please change that to
>
>   if (!(condition))
>      __ret = __wait_.....();
>
>   to make that less confusing ?   

That's really how the rest of the wait_event*() macros are done so I'd
prefer to stack with the rest of the code.

>> +	__ret;								\
>> +})

Again, I'll resend the patches by the end of the week.

-- 
Best regards,                                         _     _
 .o. | Liege of Serenly Enlightened Majesty of      o' \,=./ `o
 ..o | Computer Science,  Michal "mina86" Nazarewicz   (o o)
 ooo +--<mina86-tlen.pl>--<jid:mina86-jabber.org>--ooO--(_)--Ooo--

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 7/8] USB: testusb: imported David Brownell's USB testing application
  2010-04-08  0:29               ` [PATCH 7/8] USB: testusb: imported David Brownell's USB testing application Greg KH
  2010-04-09 19:21                 ` [PATCHv2 7/8] USB: testusb: an " Michal Nazarewicz
@ 2010-04-14  9:30                 ` David Brownell
  2010-04-14 16:46                   ` Greg KH
  2010-04-14 16:47                   ` Greg KH
  1 sibling, 2 replies; 47+ messages in thread
From: David Brownell @ 2010-04-14  9:30 UTC (permalink / raw)
  To: Greg KH
  Cc: Michal Nazarewicz, linux-usb, Peter Korsgaard, Rupesh Gujare,
	linux-kernel, David Brownell, Kyungmin Park, Marek Szyprowski

On Wednesday 07 April 2010, Greg KH wrote:
> You should put a:
>         From: David Brownell <dbrownell@users.sourceforge.net>
> as the first line of the body of this patch, so it properly shows up as
> David's code.
> 
> I also would like to get an ack from David that he doesn't mind his code
> moving into the kernel here (personally I think it's a good thing to be
> here.)
> 
> thanks,

ISTR sending an ack .... but, not with a "From:" like that.  I did author the
code, but the *patch* is not from me.


Omitting all the instructions and the test plan seems like a big mistake, too...


^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 7/8] USB: testusb: imported David Brownell's USB testing application
  2010-04-07 13:41             ` [PATCH 7/8] USB: testusb: imported David Brownell's USB testing application Michal Nazarewicz
                                 ` (2 preceding siblings ...)
  2010-04-08  6:10               ` Heikki Krogerus
@ 2010-04-14  9:41               ` David Brownell
  2010-04-14 12:50                 ` Michał Nazarewicz
  3 siblings, 1 reply; 47+ messages in thread
From: David Brownell @ 2010-04-14  9:41 UTC (permalink / raw)
  To: Michal Nazarewicz
  Cc: linux-usb, Peter Korsgaard, Rupesh Gujare, linux-kernel,
	David Brownell, Kyungmin Park, Marek Szyprowski

On Wednesday 07 April 2010, Michal Nazarewicz wrote:
> 
> The testusb application can be used to test various USB gadget
> that implment the source/sink intrerface.

That comment is woefully incomplete.  It's not just gadgets it exercises,
and a lot of thought went into testing streaming modes too (within limitations
of a the trivial test harness).

And more significantly ... It's part of a fairly complete test suite which exercises

- all four types of USB 2.0 data transfer,

          on both peripheral and host sides

       - - Good coverage of observed hardware and software failure modes,
           (to help ensure routine fauilts are handled well)

    -  Decent coverage of Linux-USB programmming interfaces ... both
       in-kernel and user-visible.

   - - Stress test modes

For more info, whvih *SHOULD* be referenced from wherever the
kernel tree includes any of this code:

See: http://www.linux-usb.org/usbtest/

Just throwing tools at someone without instructions can be rather
counter-productive .... they get misused, important issues ignored,
results mis-interpreteed, etc.

Note that there's a basic test plan, letting folk put
drivers (and hardware) through their paces.  THe evidence is that when
drivers behave on that whole suite, Linux won't misbehave much at all.

In fact, without such tools and a test plan, it'd be hard to have mch faith
in the drivr quality ... except as a weak and scattershot "this combination of
drivers and hardware seems OK for now".
futile


At one point there were allegedly folk working on Linux testng frameworks, but
they never seem to have looked at this (even on specific request); I'm not sure
if it was just general weakness in driver testing efforts (they're not easy to test),
lack of background (/interest?) in USB, or something else.   (As many of us know, it's
a sad truth that testing isn't all that glamorous, for all that it's essential).

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 7/8] USB: testusb: imported David Brownell's USB testing application
  2010-04-14  9:41               ` David Brownell
@ 2010-04-14 12:50                 ` Michał Nazarewicz
  2010-04-14 16:47                   ` Greg KH
  0 siblings, 1 reply; 47+ messages in thread
From: Michał Nazarewicz @ 2010-04-14 12:50 UTC (permalink / raw)
  To: David Brownell
  Cc: linux-usb, Peter Korsgaard, Rupesh Gujare, linux-kernel,
	David Brownell, Kyungmin Park, Marek Szyprowski

> On Wednesday 07 April 2010, Michal Nazarewicz wrote:
>> The testusb application can be used to test various USB gadget
>> that implment the source/sink intrerface.

On Wed, 14 Apr 2010 11:41:47 +0200, David Brownell <david-b@pacbell.net> wrote:
> That comment is woefully incomplete.  It's not just gadgets it
> exercises, and a lot of thought went into testing streaming modes
> too (within limitations of a the trivial test harness).
>
> It's part of a fairly complete test suite which exercises:
> - all four types of USB 2.0 data transfer, on both peripheral
>   and host sides,
> - good coverage of observed hardware and software failure modes,
> - decent coverage of Linux-USB programming interfaces, and
> - stress test modes
>
> For more info, which *SHOULD* be referenced from wherever the
> kernel tree includes any of this code:
>
> See: http://www.linux-usb.org/usbtest/
>
> Just throwing tools at someone without instructions can be rather
> counter-productive .... they get misused, important issues ignored,
> results mis-interpreteed, etc.
>
> Note that there's a basic test plan, letting folk put drivers
> (and hardware) through their paces.  The evidence is that when
> drivers behave on that whole suite, Linux won't misbehave much
> at all.
>
> In fact, without such tools and a test plan, it'd be hard to have
> much faith in the driver quality...  except as a weak and scattershot
> "this combination of drivers and hardware seems OK for now".  futile
>
> At one point there were allegedly folk working on Linux testing
> frameworks, but they never seem to have looked at this (even on
> specific request); I'm not sure if it was just general weakness
> in driver testing efforts (they're not easy to test), lack of
> background (/interest?) in USB, or something else.


Let me start with explaining that my original intent was not to get the
testing code to the Linux kernel and thus I limited documentation to
minimum.  I just wanted to put some code that may be used to test the
FunctionFS out into the air so that anyone who stumbles across the
patchset can test the FunctionFS code even though the test code may be
not inside the mainline kernel.

However, if Greg thinks it might be a good idea to put some testing code
into the tree, then it's fine by me.  This may even lead to more testing
tools be submitted and maybe after some time an unified testing framework
may emerge.

Bottom line is, if community (represented by the maintainer ;) ) wants the
patches in the kernel, then let them have it!


Obviously, the higher the quality patches are the better, so all the constructive
criticism is welcomed.  Thus I accept your comments and agree with them.  What
I'd like to ask though is what you are proposing?

Should I put a short comment saying this file is part of a greater test suite
with a link to the linux-usb.org/usbtest site?  Or maybe more code and/or
documentation should be included in the kernel?  Or maybe the whole idea of
including this code in the kernel was not so good?

I'm happy either way so I'd like to do what's the best for Linux and community
thus am asking for your comments (and I mean "your" as in plural :) ).


> On Wednesday 07 April 2010, Greg KH wrote:
>> You should put a:
>>         From: David Brownell <dbrownell@users.sourceforge.net>
>> as the first line of the body of this patch, so it properly shows up as
>> David's code.

On Wed, 14 Apr 2010 11:30:52 +0200, David Brownell <david-b@pacbell.net> wrote:
> ISTR sending an ack .... but, not with a "From:" like that.  I did author the
> code, but the *patch* is not from me.

Do you think a commit with me as an author and a clear indication in the
message that it imports your code would be better?

-- 
Best regards,                                           _     _
  .o. | Liege of Serenely Enlightened Majesty of       o' \,=./ `o
  ..o | Computer Science,  Michał "mina86" Nazarewicz     (o o)
  ooo +---[mina86@mina86.com]---[mina86@jabber.org]---ooO--(_)--Ooo--

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 7/8] USB: testusb: imported David Brownell's USB testing application
  2010-04-14  9:30                 ` [PATCH 7/8] USB: testusb: imported David Brownell's " David Brownell
@ 2010-04-14 16:46                   ` Greg KH
  2010-04-14 16:47                   ` Greg KH
  1 sibling, 0 replies; 47+ messages in thread
From: Greg KH @ 2010-04-14 16:46 UTC (permalink / raw)
  To: David Brownell
  Cc: Michal Nazarewicz, linux-usb, Peter Korsgaard, Rupesh Gujare,
	linux-kernel, David Brownell, Kyungmin Park, Marek Szyprowski

On Wed, Apr 14, 2010 at 02:30:52AM -0700, David Brownell wrote:
> On Wednesday 07 April 2010, Greg KH wrote:
> > You should put a:
> > ????????From: David Brownell <dbrownell@users.sourceforge.net>
> > as the first line of the body of this patch, so it properly shows up as
> > David's code.
> > 
> > I also would like to get an ack from David that he doesn't mind his code
> > moving into the kernel here (personally I think it's a good thing to be
> > here.)
> > 
> > thanks,
> 
> ISTR sending an ack .... but, not with a "From:" like that.  I did author the
> code, but the *patch* is not from me.

The "From:" shows authorship, which is the correct statement here, we
want to preserve it.  I know you didn't generate the patch, but that's
ok :)

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 7/8] USB: testusb: imported David Brownell's USB testing application
  2010-04-14  9:30                 ` [PATCH 7/8] USB: testusb: imported David Brownell's " David Brownell
  2010-04-14 16:46                   ` Greg KH
@ 2010-04-14 16:47                   ` Greg KH
  1 sibling, 0 replies; 47+ messages in thread
From: Greg KH @ 2010-04-14 16:47 UTC (permalink / raw)
  To: David Brownell
  Cc: Michal Nazarewicz, linux-usb, Peter Korsgaard, Rupesh Gujare,
	linux-kernel, David Brownell, Kyungmin Park, Marek Szyprowski

On Wed, Apr 14, 2010 at 02:30:52AM -0700, David Brownell wrote:
> On Wednesday 07 April 2010, Greg KH wrote:
> > You should put a:
> > ????????From: David Brownell <dbrownell@users.sourceforge.net>
> > as the first line of the body of this patch, so it properly shows up as
> > David's code.
> > 
> > I also would like to get an ack from David that he doesn't mind his code
> > moving into the kernel here (personally I think it's a good thing to be
> > here.)
> > 
> > thanks,
> 
> ISTR sending an ack .... but, not with a "From:" like that.  I did author the
> code, but the *patch* is not from me.
> 
> 
> Omitting all the instructions and the test plan seems like a big mistake, too...

Agreed, it should be there as well, as you decribed in the follow-on to
the patch.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 7/8] USB: testusb: imported David Brownell's USB testing application
  2010-04-14 12:50                 ` Michał Nazarewicz
@ 2010-04-14 16:47                   ` Greg KH
  0 siblings, 0 replies; 47+ messages in thread
From: Greg KH @ 2010-04-14 16:47 UTC (permalink / raw)
  To: Micha?? Nazarewicz
  Cc: David Brownell, linux-usb, Peter Korsgaard, Rupesh Gujare,
	linux-kernel, David Brownell, Kyungmin Park, Marek Szyprowski

On Wed, Apr 14, 2010 at 02:50:41PM +0200, Micha?? Nazarewicz wrote:
> However, if Greg thinks it might be a good idea to put some testing code
> into the tree, then it's fine by me.  This may even lead to more testing
> tools be submitted and maybe after some time an unified testing framework
> may emerge.
> 
> Bottom line is, if community (represented by the maintainer ;) ) wants the
> patches in the kernel, then let them have it!

Yes, I would like the code in the kernel.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCHv2 3/8] USB: gadget: __init and __exit tags removed
  2010-04-09 19:21     ` [PATCHv2 3/8] USB: gadget: __init and __exit tags removed Michal Nazarewicz
  2010-04-09 19:21       ` [PATCHv2 4/8] USB: f_fs: the FunctionFS driver Michal Nazarewicz
@ 2010-04-29 22:15       ` Greg KH
  2010-04-29 23:02         ` Michal Nazarewicz
  1 sibling, 1 reply; 47+ messages in thread
From: Greg KH @ 2010-04-29 22:15 UTC (permalink / raw)
  To: Michal Nazarewicz
  Cc: linux-usb, Michal Nazarewicz, Kyungmin Park, Marek Szyprowski,
	linux-kernel

On Fri, Apr 09, 2010 at 09:21:20PM +0200, Michal Nazarewicz wrote:
> __init, __initdata and __exit tags have have been removed from
> various files to make it possible for gadgets that do not use
> the __init/__exit tags to use those.
> 
> Files in question are related to:
> * the core composite framework,
> * the mass storage function (fixing a section mismatch) and
> * ethernet driver (ACM, ECM, RNDIS).
> 
> Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com>
> Cc: Kyungmin Park <kyungmin.park@samsung.com>
> Cc: Marek Szyprowski <m.szyprowski@samsung.com>
> ---
>  drivers/usb/gadget/composite.c      |   21 ++++++++++-----------
>  drivers/usb/gadget/config.c         |    4 ++--
>  drivers/usb/gadget/epautoconf.c     |   12 ++++++------
>  drivers/usb/gadget/f_acm.c          |   32 ++++++++++++++++----------------
>  drivers/usb/gadget/f_ecm.c          |   33 +++++++++++++++++----------------
>  drivers/usb/gadget/f_mass_storage.c |    2 +-
>  drivers/usb/gadget/f_rndis.c        |   34 ++++++++++++++++++----------------
>  drivers/usb/gadget/u_ether.c        |    4 ++--
>  8 files changed, 72 insertions(+), 70 deletions(-)
> 
> diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
> index 09289bb..ff155ca 100644
> --- a/drivers/usb/gadget/composite.c
> +++ b/drivers/usb/gadget/composite.c
> @@ -85,7 +85,7 @@ MODULE_PARM_DESC(iSerialNumber, "SerialNumber string");
>   * This function returns the value of the function's bind(), which is
>   * zero for success else a negative errno value.
>   */
> -int __init usb_add_function(struct usb_configuration *config,
> +int __cold usb_add_function(struct usb_configuration *config,

What is the "__cold" for?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCHv2 3/8] USB: gadget: __init and __exit tags removed
  2010-04-29 22:15       ` [PATCHv2 3/8] USB: gadget: __init and __exit tags removed Greg KH
@ 2010-04-29 23:02         ` Michal Nazarewicz
  2010-04-29 23:22           ` Greg KH
  0 siblings, 1 reply; 47+ messages in thread
From: Michal Nazarewicz @ 2010-04-29 23:02 UTC (permalink / raw)
  To: Greg KH
  Cc: Michal Nazarewicz, linux-usb, Kyungmin Park, Marek Szyprowski,
	linux-kernel

Greg KH <greg@kroah.com> writes:

> On Fri, Apr 09, 2010 at 09:21:20PM +0200, Michal Nazarewicz wrote:
>> __init, __initdata and __exit tags have have been removed from
>> various files to make it possible for gadgets that do not use
>> the __init/__exit tags to use those.
>> 
>> Files in question are related to:
>> * the core composite framework,
>> * the mass storage function (fixing a section mismatch) and
>> * ethernet driver (ACM, ECM, RNDIS).
>> 
>> Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com>
>> Cc: Kyungmin Park <kyungmin.park@samsung.com>
>> Cc: Marek Szyprowski <m.szyprowski@samsung.com>
>> ---
>>  drivers/usb/gadget/composite.c      |   21 ++++++++++-----------
>>  drivers/usb/gadget/config.c         |    4 ++--
>>  drivers/usb/gadget/epautoconf.c     |   12 ++++++------
>>  drivers/usb/gadget/f_acm.c          |   32 ++++++++++++++++----------------
>>  drivers/usb/gadget/f_ecm.c          |   33 +++++++++++++++++----------------
>>  drivers/usb/gadget/f_mass_storage.c |    2 +-
>>  drivers/usb/gadget/f_rndis.c        |   34 ++++++++++++++++++----------------
>>  drivers/usb/gadget/u_ether.c        |    4 ++--
>>  8 files changed, 72 insertions(+), 70 deletions(-)
>> 
>> diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
>> index 09289bb..ff155ca 100644
>> --- a/drivers/usb/gadget/composite.c
>> +++ b/drivers/usb/gadget/composite.c
>> @@ -85,7 +85,7 @@ MODULE_PARM_DESC(iSerialNumber, "SerialNumber string");
>>   * This function returns the value of the function's bind(), which is
>>   * zero for success else a negative errno value.
>>   */
>> -int __init usb_add_function(struct usb_configuration *config,
>> +int __cold usb_add_function(struct usb_configuration *config,
>
> What is the "__cold" for?

__init, among other things implies __cold:

include/linux/init.h:43: #define __init   __section(.init.text) __cold notrace

and so I left __cold leaving out __section.

-- 
Pozdrawiam                                            _     _
 .o. | Wasal Jasnie Oswieconej Pani Informatyki     o' \,=./ `o
 ..o | Michal "mina86" Nazarewicz  <mina86*tlen.pl>    (o o)
 ooo +---<jid:mina86-jabber.org>---<tlen:mina86>---ooO--(_)--Ooo--

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCHv2 3/8] USB: gadget: __init and __exit tags removed
  2010-04-29 23:02         ` Michal Nazarewicz
@ 2010-04-29 23:22           ` Greg KH
  2010-04-30  5:41             ` Michal Nazarewicz
  0 siblings, 1 reply; 47+ messages in thread
From: Greg KH @ 2010-04-29 23:22 UTC (permalink / raw)
  To: Michal Nazarewicz
  Cc: Michal Nazarewicz, linux-usb, Kyungmin Park, Marek Szyprowski,
	linux-kernel

On Thu, Apr 29, 2010 at 04:02:55PM -0700, Michal Nazarewicz wrote:
> Greg KH <greg@kroah.com> writes:
> 
> > On Fri, Apr 09, 2010 at 09:21:20PM +0200, Michal Nazarewicz wrote:
> >> __init, __initdata and __exit tags have have been removed from
> >> various files to make it possible for gadgets that do not use
> >> the __init/__exit tags to use those.
> >> 
> >> Files in question are related to:
> >> * the core composite framework,
> >> * the mass storage function (fixing a section mismatch) and
> >> * ethernet driver (ACM, ECM, RNDIS).
> >> 
> >> Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com>
> >> Cc: Kyungmin Park <kyungmin.park@samsung.com>
> >> Cc: Marek Szyprowski <m.szyprowski@samsung.com>
> >> ---
> >>  drivers/usb/gadget/composite.c      |   21 ++++++++++-----------
> >>  drivers/usb/gadget/config.c         |    4 ++--
> >>  drivers/usb/gadget/epautoconf.c     |   12 ++++++------
> >>  drivers/usb/gadget/f_acm.c          |   32 ++++++++++++++++----------------
> >>  drivers/usb/gadget/f_ecm.c          |   33 +++++++++++++++++----------------
> >>  drivers/usb/gadget/f_mass_storage.c |    2 +-
> >>  drivers/usb/gadget/f_rndis.c        |   34 ++++++++++++++++++----------------
> >>  drivers/usb/gadget/u_ether.c        |    4 ++--
> >>  8 files changed, 72 insertions(+), 70 deletions(-)
> >> 
> >> diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
> >> index 09289bb..ff155ca 100644
> >> --- a/drivers/usb/gadget/composite.c
> >> +++ b/drivers/usb/gadget/composite.c
> >> @@ -85,7 +85,7 @@ MODULE_PARM_DESC(iSerialNumber, "SerialNumber string");
> >>   * This function returns the value of the function's bind(), which is
> >>   * zero for success else a negative errno value.
> >>   */
> >> -int __init usb_add_function(struct usb_configuration *config,
> >> +int __cold usb_add_function(struct usb_configuration *config,
> >
> > What is the "__cold" for?
> 
> __init, among other things implies __cold:
> 
> include/linux/init.h:43: #define __init   __section(.init.text) __cold notrace
> 
> and so I left __cold leaving out __section.

Hm, how about just dropping everything?  I don't think we need the
__cold here then, right?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCHv2 3/8] USB: gadget: __init and __exit tags removed
  2010-04-29 23:22           ` Greg KH
@ 2010-04-30  5:41             ` Michal Nazarewicz
  0 siblings, 0 replies; 47+ messages in thread
From: Michal Nazarewicz @ 2010-04-30  5:41 UTC (permalink / raw)
  To: Greg KH
  Cc: Michal Nazarewicz, linux-usb, Kyungmin Park, Marek Szyprowski,
	linux-kernel

>>> On Fri, Apr 09, 2010 at 09:21:20PM +0200, Michal Nazarewicz wrote:
>>>> @@ -85,7 +85,7 @@ MODULE_PARM_DESC(iSerialNumber, "SerialNumber string");
>>>>   * This function returns the value of the function's bind(), which is
>>>>   * zero for success else a negative errno value.
>>>>   */
>>>> -int __init usb_add_function(struct usb_configuration *config,
>>>> +int __cold usb_add_function(struct usb_configuration *config,

>> Greg KH <greg@kroah.com> writes:
>>> What is the "__cold" for?
 
> On Thu, Apr 29, 2010 at 04:02:55PM -0700, Michal Nazarewicz wrote:
>> __init, among other things implies __cold so I left __cold leaving
>> out __section.

Greg KH <greg@kroah.com> writes:
> Hm, how about just dropping everything?  I don't think we need the
> __cold here then, right?

As you wish.  I'm about to resend the whole patchset (probably today or
Monday) with some new code so I'll include this change as well.

-- 
Best regards,                                         _     _
 .o. | Liege of Serenly Enlightened Majesty of      o' \,=./ `o
 ..o | Computer Science,  Michal "mina86" Nazarewicz   (o o)
 ooo +--<mina86-tlen.pl>--<jid:mina86-jabber.org>--ooO--(_)--Ooo--

^ permalink raw reply	[flat|nested] 47+ messages in thread

end of thread, other threads:[~2010-04-30 19:22 UTC | newest]

Thread overview: 47+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-04-07 13:41 [PATCH 0/7] The FunctionFS composite function Michal Nazarewicz
2010-04-07 13:41 ` [PATCH 1/8] USB: composite: allow optional removal of __init and __exit tags Michal Nazarewicz
2010-04-07 13:41   ` [PATCH 2/8] sched: __wake_up_locked() exported Michal Nazarewicz
2010-04-07 13:41     ` [PATCH 3/8] USB: f_fs: the FunctionFS driver Michal Nazarewicz
2010-04-07 13:41       ` [PATCH 4/8] USB: Ethernet: allow optional removal of __init and __init_data tags Michal Nazarewicz
2010-04-07 13:41         ` [PATCH 5/8] USB: g_ffs: the FunctionFS gadget driver Michal Nazarewicz
2010-04-07 13:41           ` [PATCH 6/8] USB: ffs-test: FunctionFS testing program Michal Nazarewicz
2010-04-07 13:41             ` [PATCH 7/8] USB: testusb: imported David Brownell's USB testing application Michal Nazarewicz
2010-04-07 13:41               ` [PATCH 8/8] USB: testusb: testusb compatibility with FunctionFS gadget Michal Nazarewicz
2010-04-08  0:30                 ` Greg KH
2010-04-08  0:29               ` [PATCH 7/8] USB: testusb: imported David Brownell's USB testing application Greg KH
2010-04-09 19:21                 ` [PATCHv2 7/8] USB: testusb: an " Michal Nazarewicz
2010-04-09 19:21                   ` [PATCHv2 8/8] USB: testusb: testusb compatibility with FunctionFS gadget Michal Nazarewicz
2010-04-10 22:51                   ` [PATCHv2 7/8] USB: testusb: an USB testing application David Brownell
2010-04-14  9:30                 ` [PATCH 7/8] USB: testusb: imported David Brownell's " David Brownell
2010-04-14 16:46                   ` Greg KH
2010-04-14 16:47                   ` Greg KH
2010-04-08  6:10               ` Heikki Krogerus
2010-04-08  6:18                 ` Greg KH
2010-04-08  6:34                   ` Heikki Krogerus
2010-04-14  9:41               ` David Brownell
2010-04-14 12:50                 ` Michał Nazarewicz
2010-04-14 16:47                   ` Greg KH
2010-04-07 17:11       ` [PATCH 3/8] USB: f_fs: the FunctionFS driver Michał Nazarewicz
2010-04-07 15:29     ` [PATCH 2/8] sched: __wake_up_locked() exported Greg KH
2010-04-07 17:11       ` Michał Nazarewicz
2010-04-08  0:28         ` Greg KH
2010-04-07 15:28   ` [PATCH 1/8] USB: composite: allow optional removal of __init and __exit tags Greg KH
2010-04-07 15:39     ` Michał Nazarewicz
2010-04-08  0:26       ` Greg KH
2010-04-09 19:21 [PATCH 0/7] The FunctionFS composite function Michal Nazarewicz
2010-04-09 19:21 ` [PATCHv2 1/8] wait_event_interruptible_locked() interface Michal Nazarewicz
2010-04-09 19:21   ` [PATCHv2 2/8] fs/timerfd.c: make use of wait_event_interruptible_locked_irq() Michal Nazarewicz
2010-04-09 19:21     ` [PATCHv2 3/8] USB: gadget: __init and __exit tags removed Michal Nazarewicz
2010-04-09 19:21       ` [PATCHv2 4/8] USB: f_fs: the FunctionFS driver Michal Nazarewicz
2010-04-09 19:21         ` [PATCHv2 5/8] USB: g_ffs: the FunctionFS gadget driver Michal Nazarewicz
2010-04-09 19:21           ` [PATCHv2 6/8] USB: ffs-test: FunctionFS testing program Michal Nazarewicz
2010-04-29 22:15       ` [PATCHv2 3/8] USB: gadget: __init and __exit tags removed Greg KH
2010-04-29 23:02         ` Michal Nazarewicz
2010-04-29 23:22           ` Greg KH
2010-04-30  5:41             ` Michal Nazarewicz
2010-04-11 14:31     ` [PATCHv2 2/8] fs/timerfd.c: make use of wait_event_interruptible_locked_irq() Thomas Gleixner
2010-04-11 19:16       ` Michal Nazarewicz
2010-04-11 19:16         ` Michal Nazarewicz
2010-04-11 15:02   ` [PATCHv2 1/8] wait_event_interruptible_locked() interface Thomas Gleixner
2010-04-11 15:02     ` Thomas Gleixner
2010-04-11 19:27     ` Michal Nazarewicz

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.