2010-04-16 09:49:48

by Michal Nazarewicz

[permalink] [raw]
Subject: [PATCHv3 0/7] The FunctionFS composite function

Hello everyone,


*Changes from Previous Version*

This is the second version of the FunctionFS patchset.

1. Changed wait_event_interruptible_locked() implementation and
removed _irqsave variants (per Thomas Gleixner's comment). See the
first patch.

2. Fixed the fs/timerfd.c patch (again, the bug has been spotted by
Thomas). See the second patch.

3. Comments pointing to <URL:http://www.linux-usb.org/usbtest/> added
to testusb.c and the introductory commit (added after comments from
David Brownell). See the commit message of the last but one patch
and beginning of the last patch.


*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 third 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 | 25 +-
include/linux/usb/functionfs.h | 199 +++
include/linux/wait.h | 149 +++
kernel/sched.c | 1 +
tools/usb/ffs-test.c | 554 ++++++++
tools/usb/testusb.c | 547 ++++++++
18 files changed, 4323 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


2010-04-16 09:49:50

by Michal Nazarewicz

[permalink] [raw]
Subject: [PATCH 1/8] wait_event_interruptible_locked() interface

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:

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

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:

/* 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);

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 <[email protected]>
Cc: Kyungmin Park <[email protected]>
Cc: Marek Szyprowski <[email protected]>
---
include/linux/wait.h | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++
kernel/sched.c | 1 +
2 files changed, 150 insertions(+), 0 deletions(-)

diff --git a/include/linux/wait.h b/include/linux/wait.h
index a48e16b..fc3c040 100644
--- a/include/linux/wait.h
+++ b/include/linux/wait.h
@@ -362,6 +362,155 @@ do { \
__ret; \
})

+
+#define __wait_event_interruptible_locked(wq, condition, exclusive, irq) \
+({ \
+ int __ret = 0; \
+ DEFINE_WAIT(__wait); \
+ if (exclusive) \
+ __wait.flags |= WQ_FLAG_EXCLUSIVE; \
+ do { \
+ if (likely(list_empty(&__wait.task_list))) \
+ __add_wait_queue_tail(&(wq), &__wait); \
+ set_current_state(TASK_INTERRUPTIBLE); \
+ if (signal_pending(current)) { \
+ __ret = -ERESTARTSYS; \
+ break; \
+ } \
+ if (irq) \
+ spin_unlock_irq(&(wq).lock); \
+ else \
+ spin_unlock(&(wq).lock); \
+ schedule(); \
+ if (irq) \
+ spin_lock_irq(&(wq).lock); \
+ else \
+ spin_lock(&(wq).lock); \
+ } while (!(condition)); \
+ __remove_wait_queue(&(wq), &__wait); \
+ __set_current_state(TASK_RUNNING); \
+ __ret; \
+})
+
+
+/**
+ * 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) \
+ ((condition) \
+ ? 0 : __wait_event_interruptible_locked(wq, condition, 0, 0))
+
+/**
+ * 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) \
+ ((condition) \
+ ? 0 : __wait_event_interruptible_locked(wq, condition, 0, 1))
+
+/**
+ * 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) \
+ ((condition) \
+ ? 0 : __wait_event_interruptible_locked(wq, condition, 1, 0))
+
+/**
+ * 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) \
+ ((condition) \
+ ? 0 : __wait_event_interruptible_locked(wq, condition, 1, 1))
+
+
+
#define __wait_event_killable(wq, condition, ret) \
do { \
DEFINE_WAIT(__wait); \
diff --git a/kernel/sched.c b/kernel/sched.c
index a3dff1f..69e1d32 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

2010-04-16 09:50:06

by Michal Nazarewicz

[permalink] [raw]
Subject: [PATCH 8/8] USB: testusb: testusb compatibility with FunctionFS gadget

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 <[email protected]>
Cc: Kyungmin Park <[email protected]>
Cc: Marek Szyprowski <[email protected]>
---
tools/usb/testusb.c | 260 +++++++++++++++++++++++++++++++++++++--------------
1 files changed, 190 insertions(+), 70 deletions(-)

diff --git a/tools/usb/testusb.c b/tools/usb/testusb.c
index 1f37298..f08e894 100644
--- a/tools/usb/testusb.c
+++ b/tools/usb/testusb.c
@@ -1,7 +1,9 @@
-/* $(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 <[email protected]>
*
* 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
@@ -18,6 +20,16 @@
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

+/*
+ * This program issues ioctls to perform the tests implemented by the
+ * kernel driver. It can generate a variety of transfer patterns; you
+ * should make sure to test both regular streaming and mixes of
+ * transfer sizes (including short transfers).
+ *
+ * For more information on how this can be used and on USB testing
+ * refer to <URL:http://www.linux-usb.org/usbtest/>.
+ */
+
#include <stdio.h>
#include <string.h>
#include <ftw.h>
@@ -25,6 +37,7 @@
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
+#include <limits.h>

#include <sys/types.h>
#include <sys/stat.h>
@@ -56,6 +69,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 +93,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 +138,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 +188,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 +354,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;
@@ -303,23 +420,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 +447,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 +456,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 +477,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

2010-04-16 09:50:15

by Michal Nazarewicz

[permalink] [raw]
Subject: [PATCH 7/8] USB: testusb: an USB testing application

From: David Brownell <[email protected]>

The testusb program just issues ioctls to perform the tests
implemented by the kernel driver. It can generate a variety
of transfer patterns; you should make sure to test both regular
streaming and mixes of transfer sizes (including short transfers).

For more information on how this can be used and on USB testing
refer to <URL:http://www.linux-usb.org/usbtest/>.

Signed-off-by: Michal Nazarewicz <[email protected]>
Cc: Kyungmin Park <[email protected]>
Cc: Marek Szyprowski <[email protected]>
---
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..1f37298
--- /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

2010-04-16 09:50:18

by Michal Nazarewicz

[permalink] [raw]
Subject: [PATCH 2/8] fs/timerfd.c: make use of wait_event_interruptible_locked_irq()

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 <[email protected]>
Cc: Kyungmin Park <[email protected]>
Cc: Marek Szyprowski <[email protected]>
---
fs/timerfd.c | 25 ++++---------------------
1 files changed, 4 insertions(+), 21 deletions(-)

diff --git a/fs/timerfd.c b/fs/timerfd.c
index 98158de..b86ab8e 100644
--- a/fs/timerfd.c
+++ b/fs/timerfd.c
@@ -110,31 +110,14 @@ 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)
+ res = -EAGAIN;
+ else
+ res = wait_event_interruptible_locked_irq(ctx->wqh, ctx->ticks);
if (ctx->ticks) {
ticks = ctx->ticks;
if (ctx->expired && ctx->tintv.tv64) {
--
1.7.0

2010-04-16 09:50:00

by Michal Nazarewicz

[permalink] [raw]
Subject: [PATCH 5/8] USB: g_ffs: the FunctionFS gadget driver

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 <[email protected]>
Cc: Kyungmin Park <[email protected]>
Cc: Marek Szyprowski <[email protected]>
---
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
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

2010-04-16 09:51:01

by Michal Nazarewicz

[permalink] [raw]
Subject: [PATCH 6/8] USB: ffs-test: FunctionFS testing program

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

Signed-off-by: Michal Nazarewicz <[email protected]>
Cc: Kyungmin Park <[email protected]>
Cc: Marek Szyprowski <[email protected]>
---
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 <[email protected]>
+ *
+ * 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

2010-04-16 09:51:29

by Michal Nazarewicz

[permalink] [raw]
Subject: [PATCH 4/8] USB: f_fs: the FunctionFS driver

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 <[email protected]>
Cc: Kyungmin Park <[email protected]>
Cc: Marek Szyprowski <[email protected]>
---
drivers/usb/gadget/f_fs.c | 2453 ++++++++++++++++++++++++++++++++++++++++
include/linux/usb/functionfs.h | 199 ++++
2 files changed, 2652 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..159d943
--- /dev/null
+++ b/drivers/usb/gadget/f_fs.c
@@ -0,0 +1,2453 @@
+/*
+ * f_fs.c -- user mode filesystem api for usb composite funtcion controllers
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ * Author: Michal Nazarewicz <[email protected]>
+ *
+ * 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)) {
+ spin_unlock_irq(&ffs->ev.waitq.lock);
+ ret = __ffs_ep0_stall(ffs);
+ break;
+ }
+
+ /* 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) {
+ spin_unlock_irq(&ffs->ev.waitq.lock);
+ ret = __ffs_ep0_stall(ffs);
+ goto done_mutex;
+ }
+
+ 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

2010-04-16 09:51:31

by Michal Nazarewicz

[permalink] [raw]
Subject: [PATCH 3/8] USB: gadget: __init and __exit tags removed

__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 <[email protected]>
Cc: Kyungmin Park <[email protected]>
Cc: Marek Szyprowski <[email protected]>
---
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(-)

Note that among other things this reverts the following commit:

> commit f88f6691b73a35b0c6dcabb9e587aa4c63d09010
> Author: Mike Frysinger <[email protected]>
> Date: Sun Mar 7 10:36:27 2010 -0500
>
> USB: g_mass_storage: fix section mismatch warnings
>
> The recent commit (0e530b45783f75) that moved usb_ep_autoconfig from the
> __devinit section to the __init section missed the mass storage device.
> Its fsg_bind() function uses the usb_ep_autoconfig() function from non
> __init context leading to:
> WARNING: drivers/usb/gadget/g_mass_storage.o(.text): Section mismatch in
> reference from the function _fsg_bind()
> to the function .init.text:_usb_ep_autoconfig()
>
> So move fsg_bind() into __init as well.
>
> Signed-off-by: Mike Frysinger <[email protected]>
> Signed-off-by: Greg Kroah-Hartman <[email protected]>


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 47e8e72..26f44c5 100644
--- a/drivers/usb/gadget/config.c
+++ b/drivers/usb/gadget/config.c
@@ -128,7 +128,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;
@@ -175,7 +175,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 400e1eb..3f18dda 100644
--- a/drivers/usb/gadget/f_acm.c
+++ b/drivers/usb/gadget/f_acm.c
@@ -116,7 +116,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 */
@@ -127,7 +127,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 */
@@ -138,7 +138,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,
@@ -146,7 +146,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,
@@ -154,14 +154,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,
@@ -171,7 +171,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,
@@ -180,21 +180,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,
@@ -210,7 +210,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,
@@ -219,21 +219,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,
@@ -571,7 +571,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;
@@ -719,7 +719,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 4e59532..64054ba 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 = {
.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 = {
.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 = {
.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 = {
.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 = {
.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 = {
.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 = {
.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 = {
.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 = {
.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[] = {
/* 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 = {
.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 = {
.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 = {
.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[] = {
/* 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 __cold
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 __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 56b0221..1b953fc 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 = {
.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 = {
.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 = {
.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 = {
.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 = {
.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 = {
.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 = {
.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 = {
.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 = {
.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[] = {
(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 = {
.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 = {
.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 = {
.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[] = {
(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 __cold
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 __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 07f4178..d653307 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -715,7 +715,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;
@@ -764,7 +764,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

2010-04-16 16:10:28

by Davide Libenzi

[permalink] [raw]
Subject: Re: [PATCH 2/8] fs/timerfd.c: make use of wait_event_interruptible_locked_irq()

On Fri, 16 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.
>
> Signed-off-by: Michal Nazarewicz <[email protected]>
> Cc: Kyungmin Park <[email protected]>
> Cc: Marek Szyprowski <[email protected]>
> ---
> fs/timerfd.c | 25 ++++---------------------
> 1 files changed, 4 insertions(+), 21 deletions(-)
>
> diff --git a/fs/timerfd.c b/fs/timerfd.c
> index 98158de..b86ab8e 100644
> --- a/fs/timerfd.c
> +++ b/fs/timerfd.c
> @@ -110,31 +110,14 @@ 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)
> + res = -EAGAIN;
> + else
> + res = wait_event_interruptible_locked_irq(ctx->wqh, ctx->ticks);
> if (ctx->ticks) {
> ticks = ctx->ticks;
> if (ctx->expired && ctx->tintv.tv64) {

Looks clean to me.

Acked-By: Davide Libenzi <[email protected]>


- Davide

2010-04-16 23:41:13

by Mike Frysinger

[permalink] [raw]
Subject: Re: [PATCH 3/8] USB: gadget: __init and __exit tags removed

On Fri, Apr 16, 2010 at 05:49, 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).

it would be nice if the expected behavior were documented somewhere so
we dont have to keep thrashing the section markings on the gadget
drivers. first they get marked to save space for setups where it
makes no sense to keep the functions. then they get tweaked to
support a semi-dynamic state. now they're all removed to support yet
a different setup.

sounds like the system is insufficiently flexible to meet the
realistic needs of different groups.
-mike

2010-04-19 07:57:47

by Michal Nazarewicz

[permalink] [raw]
Subject: Re: [PATCH 3/8] USB: gadget: __init and __exit tags removed

> On Fri, Apr 16, 2010 at 05:49, 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).

Mike Frysinger wrote:
> it would be nice if the expected behavior were documented somewhere so
> we dont have to keep thrashing the section markings on the gadget
> drivers. first they get marked to save space for setups where it
> makes no sense to keep the functions. then they get tweaked to
> support a semi-dynamic state. now they're all removed to support yet
> a different setup.

Are you referring to my previous patch by saying "then they get tweaked
to support a semi-dynamic state"? Those were just a proposal which was never
accepted and suggested to remove those tags all together.

> sounds like the system is insufficiently flexible to meet the
> realistic needs of different groups.

Yes, I agree. That's why in the first version I proposed the __usb_init,
__usb_exit, etc. tags which could be customized prior to including
composite related files.

I would still go with that solution but it was considered, let me
find the exact phrase, "Ick ick ick." :)

What's more, I don't see any other (that is cleaner) solution which
would allow flexibility so it seems what we can either choose
a non-flexible solution proposed by this patch or an ugly solution
proposed by my previous patch.

So basically, I trusted more experienced kernel developer's opinion
on this one.

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

2010-04-19 08:03:20

by Mike Frysinger

[permalink] [raw]
Subject: Re: [PATCH 3/8] USB: gadget: __init and __exit tags removed

2010/4/19 Michał Nazarewicz:
> Are you referring to my previous patch by saying "then they get tweaked
> to support a semi-dynamic state"? Those were just a proposal which was never
> accepted and suggested to remove those tags all together.

i'm referring to the change that started triggering these section
mismatch warnings with 2.6.33. i dont recall seeing any such issues
with 2.6.28-2.6.32, and probably older as well.

>> sounds like the system is insufficiently flexible to meet the
>> realistic needs of different groups.
>
> Yes, I agree.  That's why in the first version I proposed the __usb_init,
> __usb_exit, etc. tags which could be customized prior to including
> composite related files.
>
> I would still go with that solution but it was considered, let me
> find the exact phrase, "Ick ick ick." :)

seems like __usbgadget_xxx would be better naming, but i'm not
interested in fighting to get the idea in general accepted

> What's more, I don't see any other (that is cleaner) solution which
> would allow flexibility so it seems what we can either choose
> a non-flexible solution proposed by this patch or an ugly solution
> proposed by my previous patch.

i dont have a problem with your current patch (since there are still
seciton mismatch warnings floating about that i imagine this is going
to fix), just with the general situation
-mike

2010-04-19 09:04:12

by Michal Nazarewicz

[permalink] [raw]
Subject: Re: [PATCH 3/8] USB: gadget: __init and __exit tags removed

> 2010/4/19 Michał Nazarewicz:
>> Yes, I agree. That's why in the first version I proposed the
>> __usb_init, __usb_exit, etc. tags which could be customized
>> prior to including composite related files.

Mike Frysinger:
> seems like __usbgadget_xxx would be better naming, but i'm not
> interested in fighting to get the idea in general accepted

Bit long though, but yes makes more sense.

>> What's more, I don't see any other (that is cleaner) solution which
>> would allow flexibility so it seems what we can either choose
>> a non-flexible solution proposed by this patch or an ugly solution
>> proposed by my previous patch.

> i dont have a problem with your current patch (since there are still
> seciton mismatch warnings floating about that i imagine this is going
> to fix), just with the general situation

Yes, I understand that. I'm just clarifying the situation we are in
so maybe someone will came up with some better solution then the ones
already proposed.

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