Hello everyone,
*Prologue*
Patches that follow implement the FunctionFS composite function that
is a conversion of GadgetFS to use the composite framework. Possible
uses are:
1. what Peter Korsgaard has described[1]:
> I could certainly imagine doing something like that, E.G. reuse the
> existing g_serial functionality while implementing E.G. a HID device
> though gadgetfs.
2. or what Rupesh Gujare has described[2]:
> In my requirement, I want to have Media Transfer Protocol (MTP)
> (which uses user level usb driver (usb.c) & gadgetfs) along with
> modem functionality(f_acm.c).
>
> Currently either of them ie. f_acm (as part of composite driver
> along with ums) or gadgetfs (as independent module) can be loaded,
> which allows only one active USB function at a time.
>
> If gadgetfs can be made as a composite function then I can have both
> functionality at same time.
*The Patches*
First five patches implement the FunctionFS and a composite gadget
that uses FunctionFS and Ethernet function (with the later function
optionally disabled via Kconfig).
The first patch has been submitted to the list some time ago and now
I resubmit it with the FunctionFS since it is required.
The last three patches provide a testing code for the FunctionFS.
Those are not really intended to be included in the Linux source tree
but provided for anyone who might want to test it.
*How FunctionFS works*
(Copied from second patch commit message.)
>From kernel point of view it is just a composite function with some
unique behaviour. It may be added to an USB configuration only after
the user space driver has registered by writing descriptors and
strings (the user space program has to provide the same information
that kernel level composite functions provide when they are added to
the configuration).
This in particular means that the composite initialisation functions
may not be in init section (ie. may not use the __init tag) hence the
first and fourth patch in the series.
>From user space point of view it is a file system which when
mounted provide an "ep0" file. User space driver need to
write descriptors and strings to that file. It does not need
to worry about endpoints, interfaces or strings numbers but
simply provide descriptors such as if the function was the
only one (endpoints and strings numbers starting from one and
interface numbers starting from core). The FunctionFS changes
numbers of those as needed also handling situation when
numbers differ in different configurations.
When descriptors and strings are written "ep#" files appear
(one for each declared endpoint) which handle communication on
a single endpoint. Again, FunctionFS takes care of the real
numbers and changing of the configuration (which means that
"ep1" file may be really mapped to (say) endpoint 3 (and when
configuration changes to (say) endpoint 2)). "ep0" is used
for receiving events and handling setup requests.
When all files are closed the function disables itself.
*Testing*
The fifth patch implement a simple source/sink FunctionFS driver based
on similar driver for GadgetFS by David Brownell[3]. It registers
a dual-speed function with a single IN and single OUT endpoints.
The sixth and seventh patch provide a host-side testing code. This is
what David Brownell has created a while back[4] with a simple fix to
make the tool detect the number of our source/sink interface.
Still, you will need to configure the gadget to report idProduct ==
0xa4a4 (an "echo 0xa4a4 >/sys/module/g_ffs/parameters/usb_product"
should suffice) or configure host to handle 0x0525:0xa4ac devices
using the usbtest driver.
Hence, the simplest way to run the test is to do the following:
* On target (machine that runs has the gadget) as root:
$ echo 0xa4a4 >/sys/module/g_ffs/parameters/usb_product &&
$ mkdir /dev/ffs &&
$ mount -t functionfs ffs /dev/ffs &&
$ cd /dev/ffs &&
$ /path/to/ffs-test
* On host (as root):
$ testusb -a
At this point I have to admit that communication on EP0 has not yet
been tested, so beware of bugs there.
*Request for Comments and Future Work*
Regarding presented version there are two aspects I'd like to discuss.
1. First of all, the current code uses similar approach GadgetFS
used -- there is a single file ("ep0" in case of FunctionFS and
named after the controller in case of GadgetFS) that is used to
receive events from kernel and handle ep0 communication.
I think it is not the best approach as it'd be simpler and cleaner
if there were two files: one for receiving events and another for
handling ep0 communication.
What do you think? Should I keep the current version or change to
code to use two files?
2. What still needs to be implemented is a mechanism allowing double
buffering (and in effect transmission without pauses) and maybe
single-thread user-space driver implementation.
I'd like to ask what would be the best way to achieve this.
GadgetFS implements asynchronous I/O -- is it still the best
option?
3. The last thing I'd like to mention is that the FunctionFS is
designed in such a way that with some more work it will be able
to mount it several times so in the end a gadget could use several
FunctionFS functions.
The idea is that each FunctionFS instance is identified by the
device name used when mounting.
One can imagine a gadget that has an Ethernet, MTP and HID
interfaces where the last two are implemented via FunctionFS. On
user space level it would look like this:
$ modprobe g_foo
$ mkdir /dev/ffs-mtp && mount -t functionfs mtp /dev/ffs-mtp
$ ( cd /dev/ffs-mtp && mtp-daemon ) &
$ mkdir /dev/ffs-hid && mount -t functionfs hid /dev/ffs-hid
$ ( cd /dev/ffs-hid && hid-daemon ) &
On kernel level the gadget would check ffs_data->dev_name to
identify whether it's FunctionFS designed for MTP ("mtp") or HID
("hid").
PS. Peter, Rupesh, due to your ealier interest in referenced threads
([1] and [2]) I took the liberty of ccing you.
____________________________________________________________
[1] http://article.gmane.org/gmane.linux.usb.general/23890
[2] http://article.gmane.org/gmane.linux.usb.general/23902
[3] http://www.linux-usb.org/gadget/usb.c
[4] http://www.linux-usb.org/usbtest/testusb.c
Michal Nazarewicz (8):
USB: composite: allow optional removal of __init and __exit tags
sched: __wake_up_locked() exported
USB: f_fs: the FunctionFS driver
USB: Ethernet: allow optional removal of __init and __init_data tags
USB: g_ffs: the FunctionFS gadget driver
USB: ffs-test: FunctionFS testing program
USB: testusb: imported David Brownell's USB testing application
USB: testusb: testusb compatibility with FunctionFS gadget
drivers/usb/gadget/Kconfig | 21 +-
drivers/usb/gadget/Makefile | 2 +
drivers/usb/gadget/composite.c | 25 +-
drivers/usb/gadget/config.c | 6 +-
drivers/usb/gadget/epautoconf.c | 13 +-
drivers/usb/gadget/f_acm.c | 34 +-
drivers/usb/gadget/f_ecm.c | 33 +-
drivers/usb/gadget/f_fs.c | 2475 ++++++++++++++++++++++++++++++++++++
drivers/usb/gadget/f_rndis.c | 34 +-
drivers/usb/gadget/g_ffs.c | 322 +++++
drivers/usb/gadget/mass_storage.c | 4 +
drivers/usb/gadget/u_ether.c | 6 +-
drivers/usb/gadget/usb-init-exit.h | 23 +
include/linux/usb/functionfs.h | 199 +++
kernel/sched.c | 1 +
tools/usb/ffs-test.c | 554 ++++++++
tools/usb/testusb.c | 489 +++++++
17 files changed, 4170 insertions(+), 71 deletions(-)
create mode 100644 drivers/usb/gadget/f_fs.c
create mode 100644 drivers/usb/gadget/g_ffs.c
create mode 100644 drivers/usb/gadget/usb-init-exit.h
create mode 100644 include/linux/usb/functionfs.h
create mode 100644 tools/usb/ffs-test.c
create mode 100644 tools/usb/testusb.c
--
Best regards, _ _
.o. | Liege of Serenely Enlightened Majesty of o' \,=./ `o
..o | Computer Science, Michał "mina86" Nazarewicz (o o)
ooo +---[[email protected]]---[[email protected]]---ooO--(_)--Ooo--
The composite framework has been written using __init and __exit tags
to mark init and exit functions as such. This works with most of the
composite gadgets however some may need to call init/exit functions
during normal operations. One example is mass storage gadget which
needs to call exit functions.
This patch allows gadgets to define USB_NO_INIT_SEGMENT or
USB_NO_EXIT_SEGMENT to remove the __init and __exit declarations
from composite framework.
For example see mass_storage.c.
Signed-off-by: Michal Nazarewicz <[email protected]>
Cc: Kyungmin Park <[email protected]>
Cc: Marek Szyprowski <[email protected]>
---
drivers/usb/gadget/composite.c | 25 +++++++++++++------------
drivers/usb/gadget/config.c | 6 ++++--
drivers/usb/gadget/epautoconf.c | 13 +++++++------
drivers/usb/gadget/mass_storage.c | 4 ++++
drivers/usb/gadget/usb-init-exit.h | 23 +++++++++++++++++++++++
5 files changed, 51 insertions(+), 20 deletions(-)
create mode 100644 drivers/usb/gadget/usb-init-exit.h
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 09289bb..0039aba 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -27,6 +27,8 @@
#include <linux/usb/composite.h>
+#include "usb-init-exit.h"
+
/*
* The code in this file is utility code, used to build a gadget driver
@@ -85,7 +87,7 @@ MODULE_PARM_DESC(iSerialNumber, "SerialNumber string");
* This function returns the value of the function's bind(), which is
* zero for success else a negative errno value.
*/
-int __init usb_add_function(struct usb_configuration *config,
+int __usb_init usb_add_function(struct usb_configuration *config,
struct usb_function *function)
{
int value = -EINVAL;
@@ -215,7 +217,7 @@ int usb_function_activate(struct usb_function *function)
* Returns the interface ID which was allocated; or -ENODEV if no
* more interface IDs can be allocated.
*/
-int __init usb_interface_id(struct usb_configuration *config,
+int __usb_init usb_interface_id(struct usb_configuration *config,
struct usb_function *function)
{
unsigned id = config->next_interface_id;
@@ -480,7 +482,7 @@ done:
* assigns global resources including string IDs, and per-configuration
* resources such as interface IDs and endpoints.
*/
-int __init usb_add_config(struct usb_composite_dev *cdev,
+int __usb_init usb_add_config(struct usb_composite_dev *cdev,
struct usb_configuration *config)
{
int status = -EINVAL;
@@ -677,7 +679,7 @@ static int get_string(struct usb_composite_dev *cdev,
* ensure that for example different functions don't wrongly assign
* different meanings to the same identifier.
*/
-int __init usb_string_id(struct usb_composite_dev *cdev)
+int __usb_init usb_string_id(struct usb_composite_dev *cdev)
{
if (cdev->next_string_id < 254) {
/* string id 0 is reserved */
@@ -898,7 +900,7 @@ static void composite_disconnect(struct usb_gadget *gadget)
/*-------------------------------------------------------------------------*/
-static void /* __init_or_exit */
+static void __usb_init_or_exit
composite_unbind(struct usb_gadget *gadget)
{
struct usb_composite_dev *cdev = get_gadget_data(gadget);
@@ -947,7 +949,7 @@ composite_unbind(struct usb_gadget *gadget)
composite = NULL;
}
-static void __init
+static void __usb_init
string_override_one(struct usb_gadget_strings *tab, u8 id, const char *s)
{
struct usb_string *str = tab->strings;
@@ -960,7 +962,7 @@ string_override_one(struct usb_gadget_strings *tab, u8 id, const char *s)
}
}
-static void __init
+static void __usb_init
string_override(struct usb_gadget_strings **tab, u8 id, const char *s)
{
while (*tab) {
@@ -969,7 +971,7 @@ string_override(struct usb_gadget_strings **tab, u8 id, const char *s)
}
}
-static int __init composite_bind(struct usb_gadget *gadget)
+static int __usb_init composite_bind(struct usb_gadget *gadget)
{
struct usb_composite_dev *cdev;
int status = -ENOMEM;
@@ -1092,8 +1094,7 @@ static struct usb_gadget_driver composite_driver = {
.speed = USB_SPEED_HIGH,
.bind = composite_bind,
- /* .unbind = __exit_p(composite_unbind), */
- .unbind = composite_unbind,
+ .unbind = __usb_exit_p(composite_unbind),
.setup = composite_setup,
.disconnect = composite_disconnect,
@@ -1121,7 +1122,7 @@ static struct usb_gadget_driver composite_driver = {
* while it was binding. That would usually be done in order to wait for
* some userspace participation.
*/
-int __init usb_composite_register(struct usb_composite_driver *driver)
+int __usb_init usb_composite_register(struct usb_composite_driver *driver)
{
if (!driver || !driver->dev || !driver->bind || composite)
return -EINVAL;
@@ -1142,7 +1143,7 @@ int __init usb_composite_register(struct usb_composite_driver *driver)
* This function is used to unregister drivers using the composite
* driver framework.
*/
-void /* __exit */ usb_composite_unregister(struct usb_composite_driver *driver)
+void __usb_exit usb_composite_unregister(struct usb_composite_driver *driver)
{
if (composite != driver)
return;
diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c
index 47e8e72..2f15ee7 100644
--- a/drivers/usb/gadget/config.c
+++ b/drivers/usb/gadget/config.c
@@ -28,6 +28,8 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
+#include "usb-init-exit.h"
+
/**
* usb_descriptor_fillbuf - fill buffer with descriptors
@@ -128,7 +130,7 @@ int usb_gadget_config_buf(
* with identifiers (for interfaces, strings, endpoints, and more)
* as needed by a given function instance.
*/
-struct usb_descriptor_header **__init
+struct usb_descriptor_header **__usb_init
usb_copy_descriptors(struct usb_descriptor_header **src)
{
struct usb_descriptor_header **tmp;
@@ -175,7 +177,7 @@ usb_copy_descriptors(struct usb_descriptor_header **src)
* intended use is to help remembering the endpoint descriptor to use
* when enabling a given endpoint.
*/
-struct usb_endpoint_descriptor *__init
+struct usb_endpoint_descriptor *__usb_init
usb_find_endpoint(
struct usb_descriptor_header **src,
struct usb_descriptor_header **copy,
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index 3568de2..706af4c 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -31,15 +31,16 @@
#include <linux/usb/gadget.h>
#include "gadget_chips.h"
+#include "usb-init-exit.h"
/* we must assign addresses for configurable endpoints (like net2280) */
-static __initdata unsigned epnum;
+static __usb_initdata unsigned epnum;
// #define MANY_ENDPOINTS
#ifdef MANY_ENDPOINTS
/* more than 15 configurable endpoints */
-static __initdata unsigned in_epnum;
+static __usb_initdata unsigned in_epnum;
#endif
@@ -59,7 +60,7 @@ static __initdata unsigned in_epnum;
* NOTE: each endpoint is unidirectional, as specified by its USB
* descriptor; and isn't specific to a configuration or altsetting.
*/
-static int __init
+static int __usb_init
ep_matches (
struct usb_gadget *gadget,
struct usb_ep *ep,
@@ -187,7 +188,7 @@ ep_matches (
return 1;
}
-static struct usb_ep * __init
+static struct usb_ep * __usb_init
find_ep (struct usb_gadget *gadget, const char *name)
{
struct usb_ep *ep;
@@ -229,7 +230,7 @@ find_ep (struct usb_gadget *gadget, const char *name)
*
* On failure, this returns a null endpoint descriptor.
*/
-struct usb_ep * __init usb_ep_autoconfig (
+struct usb_ep * __usb_init usb_ep_autoconfig (
struct usb_gadget *gadget,
struct usb_endpoint_descriptor *desc
)
@@ -304,7 +305,7 @@ struct usb_ep * __init usb_ep_autoconfig (
* state such as ep->driver_data and the record of assigned endpoints
* used by usb_ep_autoconfig().
*/
-void __init usb_ep_autoconfig_reset (struct usb_gadget *gadget)
+void __usb_init usb_ep_autoconfig_reset (struct usb_gadget *gadget)
{
struct usb_ep *ep;
diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c
index 705cc1f..438a395 100644
--- a/drivers/usb/gadget/mass_storage.c
+++ b/drivers/usb/gadget/mass_storage.c
@@ -57,6 +57,10 @@
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
+/* Do not mark composite_unbind() and usb_composite_unregister() as
+ * __exit, we may need it even if we are not unloaded. */
+#define USB_NO_EXIT_SEGMENT 1
+
#include "composite.c"
#include "usbstring.c"
#include "config.c"
diff --git a/drivers/usb/gadget/usb-init-exit.h b/drivers/usb/gadget/usb-init-exit.h
new file mode 100644
index 0000000..04cd624
--- /dev/null
+++ b/drivers/usb/gadget/usb-init-exit.h
@@ -0,0 +1,23 @@
+#ifndef __usb_init
+# if defined USB_NO_INIT_SEGMENT
+# define __usb_init __cold
+# define __usb_initdata
+# else
+# define __usb_init __init
+# define __usb_initdata __initdata
+# endif
+# if defined USB_NO_EXIT_SEGMENT
+# define __usb_exit __cold
+# define __usb_exit_p(func) func
+# define __usb_exitdata
+# else
+# define __usb_exit __exit
+# define __usb_exit_p(func) __exit_p(func)
+# define __usb_exitdata __exitdata
+# endif
+# if defined USB_NO_INIT_SEGMENT || defined USB_NO_EXIT_SEGMENT
+# define __usb_init_or_exit __cold
+# else
+# define __usb_init_or_exit /* __init_or_exit */
+# endif
+#endif
--
1.7.0
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 11a3e0f..e71a730 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -710,6 +710,26 @@ config USB_GADGETFS
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "gadgetfs".
+config USB_FUNCTIONFS
+ tristate "Function Filesystem (EXPERIMENTAL)"
+ depends on EXPERIMENTAL
+ help
+ The Function Filesystem (FunctioFS) lets one create USB
+ composite functions in user space in the same way as GadgetFS
+ lets one create USB gadgets in user space. This allows creation
+ of composite gadgets such that some of the functions are
+ implemented in kernel space (for instance Ethernet, serial or
+ mass storage) and other are implemented in user space.
+
+ Say "y" to link the driver statically, or "m" to build
+ a dynamically linked module called "g_ffs".
+
+config USB_FUNCTIONFS_ETH
+ bool "Include Ethernet funcien"
+ depends on USB_FUNCTIONFS
+ help
+ Include an Ethernet funcion in the Funcion Filesystem.
+
config USB_FILE_STORAGE
tristate "File-backed Storage Gadget"
depends on BLOCK
@@ -863,7 +883,6 @@ config USB_G_MULTI_CDC
If unsure, say "y".
-
# put drivers that need isochronous transfer support (for audio
# or video class gadget drivers), or specific hardware, here.
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 43b51da..6d9d638 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -49,6 +49,8 @@ obj-$(CONFIG_USB_ZERO) += g_zero.o
obj-$(CONFIG_USB_AUDIO) += g_audio.o
obj-$(CONFIG_USB_ETH) += g_ether.o
obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o
+obj-$(CONFIG_USB_FUNCTIONFS) += g_ffs.o
+obj-$(CONFIG_USB_ETH_FUNCTIONFS) += g_eth_ffs.o
obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o
obj-$(CONFIG_USB_MASS_STORAGE) += g_mass_storage.o
obj-$(CONFIG_USB_G_SERIAL) += g_serial.o
diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c
new file mode 100644
index 0000000..6033f5d
--- /dev/null
+++ b/drivers/usb/gadget/g_ffs.c
@@ -0,0 +1,322 @@
+#include <linux/module.h>
+#include <linux/utsname.h>
+
+
+/*
+ * kbuild is not very cooperative with respect to linking separately
+ * compiled library objects into one module. So for now we won't use
+ * separate compilation ... ensuring init/exit sections work to shrink
+ * the runtime footprint, and giving us at least some parts of what
+ * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
+ */
+
+/* Do not mark any of the USB and composite initialization functions
+ * as __init, we use it after user space writes descriptors which is
+ * when init segments are removed. */
+#define USB_NO_INIT_SEGMENT 1
+/* Do not mark composite_unbind() and usb_composite_unregister() as
+ * __exit, we may need it even if we are not unloaded. */
+#define USB_NO_EXIT_SEGMENT 1
+
+#include "composite.c"
+#include "usbstring.c"
+#include "config.c"
+#include "epautoconf.c"
+
+#ifdef CONFIG_USB_FUNCTIONFS_ETH
+/* # if defined USB_ETH_RNDIS */
+/* # undef USB_ETH_RNDIS */
+/* # endif */
+/* # ifdef CONFIG_USB_ETH_FUNCTIONFS_RNDIS */
+/* # define USB_ETH_RNDIS y */
+/* # endif */
+# include "u_ether.h"
+
+# include "f_ecm.c"
+# include "f_subset.c"
+/* # ifdef USB_ETH_RNDIS */
+/* # include "f_rndis.c" */
+/* # include "rndis.c" */
+/* # endif */
+# include "u_ether.c"
+
+static u8 gfs_hostaddr[ETH_ALEN];
+#endif
+
+#include "f_fs.c"
+
+
+#define DRIVER_NAME "g_ffs"
+#define DRIVER_DESC "USB Function Filesystem"
+#define DRIVER_VERSION "24 Aug 2004"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Michal Nazarewicz");
+MODULE_LICENSE("GPL");
+
+
+static unsigned short gfs_vendor_id = 0x0525; /* XXX NetChip */
+static unsigned short gfs_product_id = 0xa4ac; /* XXX */
+
+static struct usb_device_descriptor gfs_dev_desc = {
+ .bLength = sizeof gfs_dev_desc,
+ .bDescriptorType = USB_DT_DEVICE,
+
+ .bcdUSB = cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_PER_INTERFACE,
+
+ /* Vendor and product id can be overridden by module parameters. */
+ /* .idVendor = cpu_to_le16(gfs_vendor_id), */
+ /* .idProduct = cpu_to_le16(gfs_product_id), */
+ /* .bcdDevice = f(hardware) */
+ /* .iManufacturer = DYNAMIC */
+ /* .iProduct = DYNAMIC */
+ /* NO SERIAL NUMBER */
+ .bNumConfigurations = 1,
+};
+
+#define GFS_MODULE_PARAM_DESC(name, field) \
+ MODULE_PARM_DESC(name, "Value of the " #field " field of the device descriptor sent to the host. Takes effect only prior to the user-space driver registering to the FunctionFS.")
+
+module_param_named(usb_class, gfs_dev_desc.bDeviceClass, byte, 0644);
+GFS_MODULE_PARAM_DESC(usb_class, bDeviceClass);
+module_param_named(usb_subclass, gfs_dev_desc.bDeviceSubClass, byte, 0644);
+GFS_MODULE_PARAM_DESC(usb_subclass, bDeviceSubClass);
+module_param_named(usb_protocol, gfs_dev_desc.bDeviceProtocol, byte, 0644);
+GFS_MODULE_PARAM_DESC(usb_protocol, bDeviceProtocol);
+module_param_named(usb_vendor, gfs_vendor_id, ushort, 0644);
+GFS_MODULE_PARAM_DESC(usb_vendor, idVendor);
+module_param_named(usb_product, gfs_product_id, ushort, 0644);
+GFS_MODULE_PARAM_DESC(usb_product, idProduct);
+
+
+
+static const struct usb_descriptor_header *gfs_otg_desc[] = {
+ (const struct usb_descriptor_header *)
+ &(const struct usb_otg_descriptor) {
+ .bLength = sizeof(struct usb_otg_descriptor),
+ .bDescriptorType = USB_DT_OTG,
+
+ /* REVISIT SRP-only hardware is possible, although
+ * it would not be called "OTG" ... */
+ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
+ },
+
+ NULL
+};
+
+/* string IDs are assigned dynamically */
+
+enum {
+ GFS_STRING_MANUFACTURER_IDX,
+ GFS_STRING_PRODUCT_IDX,
+ GFS_STRING_CONFIGURATION_IDX
+};
+
+static char gfs_manufacturer[50];
+static const char gfs_driver_desc[] = DRIVER_DESC;
+static const char gfs_short_name[] = DRIVER_NAME;
+
+static struct usb_string gfs_strings[] = {
+ [GFS_STRING_MANUFACTURER_IDX].s = gfs_manufacturer,
+ [GFS_STRING_PRODUCT_IDX].s = gfs_driver_desc,
+ [GFS_STRING_CONFIGURATION_IDX].s = "Self Powered",
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings *gfs_dev_strings[] = {
+ &(struct usb_gadget_strings) {
+ .language = 0x0409, /* en-us */
+ .strings = gfs_strings,
+ },
+ NULL,
+};
+
+
+static int gfs_bind(struct usb_composite_dev *cdev);
+static int gfs_unbind(struct usb_composite_dev *cdev);
+static int gfs_do_config(struct usb_configuration *c);
+
+
+static struct usb_composite_driver gfs_driver = {
+ .name = gfs_short_name,
+ .dev = &gfs_dev_desc,
+ .strings = gfs_dev_strings,
+ .bind = gfs_bind,
+ .unbind = gfs_unbind,
+};
+
+static struct usb_configuration gfs_config_driver = {
+ .label = "FunctionFS",
+ .bind = gfs_do_config,
+ .bConfigurationValue = 1,
+ /* .iConfiguration = DYNAMIC */
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+};
+
+
+static struct ffs_data *gfs_ffs_data;
+static unsigned long gfs_registered;
+
+
+static int __init gfs_init(void)
+{
+ ENTER();
+
+ return functionfs_init();
+}
+module_init(gfs_init);
+
+static void __exit gfs_exit(void)
+{
+ ENTER();
+
+ if (test_and_clear_bit(0, &gfs_registered))
+ usb_composite_unregister(&gfs_driver);
+
+ functionfs_cleanup();
+}
+module_exit(gfs_exit);
+
+
+static int functionfs_ready_callback(struct ffs_data *ffs)
+{
+ int ret;
+
+ ENTER();
+
+ if (WARN_ON(test_and_set_bit(0, &gfs_registered)))
+ return -EBUSY;
+
+ gfs_ffs_data = ffs;
+ ret = usb_composite_register(&gfs_driver);
+ if (unlikely(ret < 0))
+ clear_bit(0, &gfs_registered);
+ return ret;
+}
+
+static void functionfs_closed_callback(struct ffs_data *ffs)
+{
+ ENTER();
+
+ if (test_and_clear_bit(0, &gfs_registered))
+ usb_composite_unregister(&gfs_driver);
+}
+
+
+static int functionfs_check_dev_callback(const char *dev_name)
+{
+ return 0;
+}
+
+
+
+static int gfs_bind(struct usb_composite_dev *cdev)
+{
+ int ret;
+
+ ENTER();
+
+ if (WARN_ON(!gfs_ffs_data))
+ return -ENODEV;
+
+#ifdef CONFIG_USB_FUNCTIONFS_ETH
+ ret = gether_setup(cdev->gadget, gfs_hostaddr);
+ if (unlikely(ret < 0))
+ goto error_quick;
+#endif
+
+ gfs_dev_desc.idVendor = cpu_to_le16(gfs_vendor_id);
+ gfs_dev_desc.idProduct = cpu_to_le16(gfs_product_id);
+
+ snprintf(gfs_manufacturer, sizeof gfs_manufacturer, "%s %s with %s",
+ init_utsname()->sysname, init_utsname()->release,
+ cdev->gadget->name);
+ ret = usb_string_id(cdev);
+ if (unlikely(ret < 0))
+ goto error;
+ gfs_strings[GFS_STRING_MANUFACTURER_IDX].id = ret;
+ gfs_dev_desc.iManufacturer = ret;
+
+ ret = usb_string_id(cdev);
+ if (unlikely(ret < 0))
+ goto error;
+ gfs_strings[GFS_STRING_PRODUCT_IDX].id = ret;
+ gfs_dev_desc.iProduct = ret;
+
+ ret = usb_string_id(cdev);
+ if (unlikely(ret < 0))
+ goto error;
+ gfs_strings[GFS_STRING_CONFIGURATION_IDX].id = ret;
+ gfs_config_driver.iConfiguration = ret;
+
+ ret = functionfs_bind(gfs_ffs_data, cdev);
+ if (unlikely(ret < 0))
+ goto error;
+
+ ret = usb_add_config(cdev, &gfs_config_driver);
+ if (unlikely(ret < 0))
+ goto error_unbind;
+
+ return 0;
+
+error_unbind:
+ functionfs_unbind(gfs_ffs_data);
+error:
+#ifdef CONFIG_USB_FUNCTIONFS_ETH
+ gether_cleanup();
+error_quick:
+#endif
+ gfs_ffs_data = NULL;
+ return ret;
+}
+
+static int gfs_unbind(struct usb_composite_dev *cdev)
+{
+ ENTER();
+
+#ifdef CONFIG_USB_FUNCTIONFS_ETH
+ gether_cleanup();
+#endif
+
+ if (!WARN_ON(!gfs_ffs_data)) {
+ functionfs_unbind(gfs_ffs_data);
+ gfs_ffs_data = NULL;
+ }
+
+ return 0;
+}
+
+
+static int gfs_do_config(struct usb_configuration *c)
+{
+ int ret;
+
+ ENTER();
+
+ if (WARN_ON(!gfs_ffs_data))
+ return -ENODEV;
+
+ if (gadget_is_otg(c->cdev->gadget)) {
+ c->descriptors = gfs_otg_desc;
+ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
+#ifdef CONFIG_USB_FUNCTIONFS_ETH
+ if (can_support_ecm(c->cdev->gadget)) {
+ FINFO("ecm_bind_config");
+ ret = ecm_bind_config(c, gfs_hostaddr);
+ } else {
+ FINFO("geth_bind_config");
+ ret = geth_bind_config(c, gfs_hostaddr);
+ }
+ if (unlikely(ret < 0))
+ return ret;
+#endif
+
+ ret = functionfs_add(c->cdev, c, gfs_ffs_data);
+ if (unlikely(ret < 0))
+ return ret;
+
+ return 0;
+}
--
1.7.0
The __wake_up_locked() function has been exported in case modules need it.
Signed-off-by: Michal Nazarewicz <[email protected]>
Cc: Kyungmin Park <[email protected]>
Cc: Marek Szyprowski <[email protected]>
---
kernel/sched.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/kernel/sched.c b/kernel/sched.c
index 0a1fce0..5fb8a37 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -3940,6 +3940,7 @@ void __wake_up_locked(wait_queue_head_t *q, unsigned int mode)
{
__wake_up_common(q, mode, 1, 0, NULL);
}
+EXPORT_SYMBOL_GPL(__wake_up_locked);
void __wake_up_locked_key(wait_queue_head_t *q, unsigned int mode, void *key)
{
--
1.7.0
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 | 176 ++++++++++++++++++++++++++++++++++-----------------
1 files changed, 119 insertions(+), 57 deletions(-)
diff --git a/tools/usb/testusb.c b/tools/usb/testusb.c
index 28e25ca..beaeea3 100644
--- a/tools/usb/testusb.c
+++ b/tools/usb/testusb.c
@@ -56,6 +56,13 @@ struct usbtest_param {
/* #include <linux/usb_ch9.h> */
+#define USB_DT_DEVICE 0x01
+#define USB_DT_INTERFACE 0x04
+
+#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */
+#define USB_CLASS_VENDOR_SPEC 0xff
+
+
struct usb_device_descriptor {
__u8 bLength;
__u8 bDescriptorType;
@@ -73,6 +80,19 @@ struct usb_device_descriptor {
__u8 bNumConfigurations;
} __attribute__ ((packed));
+struct usb_interface_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+
+ __u8 bInterfaceNumber;
+ __u8 bAlternateSetting;
+ __u8 bNumEndpoints;
+ __u8 bInterfaceClass;
+ __u8 bInterfaceSubClass;
+ __u8 bInterfaceProtocol;
+ __u8 iInterface;
+} __attribute__ ((packed));
+
enum usb_device_speed {
USB_SPEED_UNKNOWN = 0, /* enumerating */
USB_SPEED_LOW, USB_SPEED_FULL, /* usb 1.1 */
@@ -105,11 +125,42 @@ struct testdev {
};
static struct testdev *testdevs;
-static int is_testdev (struct usb_device_descriptor *dev)
+static int testdev_ffs_ifnum(FILE *fd)
+{
+ union {
+ char buf[255];
+ struct usb_interface_descriptor intf;
+ } u;
+
+ for (;;) {
+ if (fread(u.buf, 1, 1, fd) != 1)
+ return -1;
+ if (fread(u.buf + 1, (unsigned char)u.buf[0] - 1, 1, fd) != 1)
+ return -1;
+
+ if (u.intf.bLength == sizeof u.intf
+ && u.intf.bDescriptorType == USB_DT_INTERFACE
+ && u.intf.bNumEndpoints == 2
+ && u.intf.bInterfaceClass == USB_CLASS_VENDOR_SPEC
+ && u.intf.bInterfaceSubClass == 0
+ && u.intf.bInterfaceProtocol == 0)
+ return (unsigned char)u.intf.bInterfaceNumber;
+ }
+}
+
+static int testdev_ifnum(FILE *fd)
{
+ struct usb_device_descriptor dev;
+
+ if (fread(&dev, sizeof dev, 1, fd) != 1)
+ return -1;
+
+ if (dev.bLength != sizeof dev || dev.bDescriptorType != USB_DT_DEVICE)
+ return -1;
+
/* FX2 with (tweaked) bulksrc firmware */
- if (dev->idVendor == 0x0547 && dev->idProduct == 0x1002)
- return 1;
+ if (dev.idVendor == 0x0547 && dev.idProduct == 0x1002)
+ return 0;
/*----------------------------------------------------*/
@@ -124,95 +175,106 @@ static int is_testdev (struct usb_device_descriptor *dev)
*/
/* generic EZ-USB FX controller */
- if (dev->idVendor == 0x0547 && dev->idProduct == 0x2235)
- return 1;
+ if (dev.idVendor == 0x0547 && dev.idProduct == 0x2235)
+ return 0;
/* generic EZ-USB FX2 controller */
- if (dev->idVendor == 0x04b4 && dev->idProduct == 0x8613)
- return 1;
+ if (dev.idVendor == 0x04b4 && dev.idProduct == 0x8613)
+ return 0;
/* CY3671 development board with EZ-USB FX */
- if (dev->idVendor == 0x0547 && dev->idProduct == 0x0080)
- return 1;
+ if (dev.idVendor == 0x0547 && dev.idProduct == 0x0080)
+ return 0;
/* Keyspan 19Qi uses an21xx (original EZ-USB) */
- if (dev->idVendor == 0x06cd && dev->idProduct == 0x010b)
- return 1;
+ if (dev.idVendor == 0x06cd && dev.idProduct == 0x010b)
+ return 0;
/*----------------------------------------------------*/
/* "gadget zero", Linux-USB test software */
- if (dev->idVendor == 0x0525 && dev->idProduct == 0xa4a0)
- return 1;
+ if (dev.idVendor == 0x0525 && dev.idProduct == 0xa4a0)
+ return 0;
/* user mode subset of that */
- if (dev->idVendor == 0x0525 && dev->idProduct == 0xa4a4)
- return 1;
+ if (dev.idVendor == 0x0525 && dev.idProduct == 0xa4a4)
+ return testdev_ffs_ifnum(fd);
+ /* return 0; */
/* iso version of usermode code */
- if (dev->idVendor == 0x0525 && dev->idProduct == 0xa4a3)
- return 1;
+ if (dev.idVendor == 0x0525 && dev.idProduct == 0xa4a3)
+ return 0;
/* some GPL'd test firmware uses these IDs */
- if (dev->idVendor == 0xfff0 && dev->idProduct == 0xfff0)
- return 1;
+ if (dev.idVendor == 0xfff0 && dev.idProduct == 0xfff0)
+ return 0;
/*----------------------------------------------------*/
/* iBOT2 high speed webcam */
- if (dev->idVendor == 0x0b62 && dev->idProduct == 0x0059)
- return 1;
+ if (dev.idVendor == 0x0b62 && dev.idProduct == 0x0059)
+ return 0;
- return 0;
+ /*----------------------------------------------------*/
+
+ /* the FunctionFS gadget can have the source/sink interface
+ * anywhere. We look for an interface descriptor that match
+ * what we expect. We ignore configuratiens thou. */
+
+ if (dev.idVendor == 0x0525 && dev.idProduct == 0xa4ac
+ && (dev.bDeviceClass == USB_CLASS_PER_INTERFACE
+ || dev.bDeviceClass == USB_CLASS_VENDOR_SPEC))
+ return testdev_ffs_ifnum(fd);
+
+ return -1;
}
-static int find_testdev (const char *name, const struct stat *sb, int flag)
+static int find_testdev(const char *name, const struct stat *sb, int flag)
{
- int fd;
- struct usb_device_descriptor dev;
+ FILE *fd;
+ int ifnum;
+ struct testdev *entry;
if (flag != FTW_F)
return 0;
/* ignore /proc/bus/usb/{devices,drivers} */
- if (strrchr (name, '/')[1] == 'd')
+ if (strrchr(name, '/')[1] == 'd')
return 0;
- if ((fd = open (name, O_RDONLY)) < 0) {
- perror ("can't open dev file r/o");
+ fd = fopen(name, "rb");
+ if (!fd) {
+ perror(name);
return 0;
}
- if (read (fd, &dev, sizeof dev) != sizeof dev)
- fputs ("short devfile read!\n", stderr);
- else if (is_testdev (&dev)) {
- struct testdev *entry;
-
- if ((entry = calloc (1, sizeof *entry)) == 0) {
- fputs ("no mem!\n", stderr);
- goto done;
- }
- entry->name = strdup (name);
- if (!entry->name) {
- free (entry);
- goto done;
- }
-
- // FIXME better to look at each interface and ask if it's
- // bound to 'usbtest', rather than assume interface 0
- entry->ifnum = 0;
- // FIXME ask usbfs what speed; update USBDEVFS_CONNECTINFO
- // so it tells about high speed etc
+ ifnum = testdev_ifnum(fd);
+ fclose(fd);
+ if (ifnum < 0)
+ return 0;
- fprintf (stderr, "%s speed\t%s\n",
- speed (entry->speed), entry->name);
+ entry = calloc(1, sizeof *entry);
+ if (!entry)
+ goto nomem;
- entry->next = testdevs;
- testdevs = entry;
+ entry->name = strdup(name);
+ if (!entry->name) {
+ free(entry);
+nomem:
+ perror("malloc");
+ return 0;
}
-done:
- close (fd);
+ entry->ifnum = ifnum;
+
+ /* FIXME ask usbfs what speed; update USBDEVFS_CONNECTINFO so
+ * it tells about high speed etc */
+
+ fprintf(stderr, "%s speed\t%s\t%u\n",
+ speed(entry->speed), entry->name, entry->ifnum);
+
+ entry->next = testdevs;
+ testdevs = entry;
return 0;
}
--
1.7.0
Use the __usb_init and __usb_initdata tags in Ethernet
composite function related files to allow disabling the init
section micro-optimisation in drivers that require this.
Signed-off-by: Michal Nazarewicz <[email protected]>
Cc: Kyungmin Park <[email protected]>
Cc: Marek Szyprowski <[email protected]>
---
drivers/usb/gadget/f_acm.c | 34 ++++++++++++++++++----------------
drivers/usb/gadget/f_ecm.c | 33 +++++++++++++++++----------------
drivers/usb/gadget/f_rndis.c | 34 ++++++++++++++++++----------------
drivers/usb/gadget/u_ether.c | 6 ++++--
4 files changed, 57 insertions(+), 50 deletions(-)
diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c
index 400e1eb..449296c 100644
--- a/drivers/usb/gadget/f_acm.c
+++ b/drivers/usb/gadget/f_acm.c
@@ -116,7 +116,8 @@ acm_iad_descriptor = {
};
-static struct usb_interface_descriptor acm_control_interface_desc __initdata = {
+static struct usb_interface_descriptor
+acm_control_interface_desc __usb_initdata = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
@@ -127,7 +128,8 @@ static struct usb_interface_descriptor acm_control_interface_desc __initdata = {
/* .iInterface = DYNAMIC */
};
-static struct usb_interface_descriptor acm_data_interface_desc __initdata = {
+static struct usb_interface_descriptor
+acm_data_interface_desc __usb_initdata = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
@@ -138,7 +140,7 @@ static struct usb_interface_descriptor acm_data_interface_desc __initdata = {
/* .iInterface = DYNAMIC */
};
-static struct usb_cdc_header_desc acm_header_desc __initdata = {
+static struct usb_cdc_header_desc acm_header_desc __usb_initdata = {
.bLength = sizeof(acm_header_desc),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_HEADER_TYPE,
@@ -146,7 +148,7 @@ static struct usb_cdc_header_desc acm_header_desc __initdata = {
};
static struct usb_cdc_call_mgmt_descriptor
-acm_call_mgmt_descriptor __initdata = {
+acm_call_mgmt_descriptor __usb_initdata = {
.bLength = sizeof(acm_call_mgmt_descriptor),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
@@ -154,14 +156,14 @@ acm_call_mgmt_descriptor __initdata = {
/* .bDataInterface = DYNAMIC */
};
-static struct usb_cdc_acm_descriptor acm_descriptor __initdata = {
+static struct usb_cdc_acm_descriptor acm_descriptor __usb_initdata = {
.bLength = sizeof(acm_descriptor),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_ACM_TYPE,
.bmCapabilities = USB_CDC_CAP_LINE,
};
-static struct usb_cdc_union_desc acm_union_desc __initdata = {
+static struct usb_cdc_union_desc acm_union_desc __usb_initdata = {
.bLength = sizeof(acm_union_desc),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_UNION_TYPE,
@@ -171,7 +173,7 @@ static struct usb_cdc_union_desc acm_union_desc __initdata = {
/* full speed support: */
-static struct usb_endpoint_descriptor acm_fs_notify_desc __initdata = {
+static struct usb_endpoint_descriptor acm_fs_notify_desc __usb_initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
@@ -180,21 +182,21 @@ static struct usb_endpoint_descriptor acm_fs_notify_desc __initdata = {
.bInterval = 1 << GS_LOG2_NOTIFY_INTERVAL,
};
-static struct usb_endpoint_descriptor acm_fs_in_desc __initdata = {
+static struct usb_endpoint_descriptor acm_fs_in_desc __usb_initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
-static struct usb_endpoint_descriptor acm_fs_out_desc __initdata = {
+static struct usb_endpoint_descriptor acm_fs_out_desc __usb_initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
-static struct usb_descriptor_header *acm_fs_function[] __initdata = {
+static struct usb_descriptor_header *acm_fs_function[] __usb_initdata = {
(struct usb_descriptor_header *) &acm_iad_descriptor,
(struct usb_descriptor_header *) &acm_control_interface_desc,
(struct usb_descriptor_header *) &acm_header_desc,
@@ -210,7 +212,7 @@ static struct usb_descriptor_header *acm_fs_function[] __initdata = {
/* high speed support: */
-static struct usb_endpoint_descriptor acm_hs_notify_desc __initdata = {
+static struct usb_endpoint_descriptor acm_hs_notify_desc __usb_initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
@@ -219,21 +221,21 @@ static struct usb_endpoint_descriptor acm_hs_notify_desc __initdata = {
.bInterval = GS_LOG2_NOTIFY_INTERVAL+4,
};
-static struct usb_endpoint_descriptor acm_hs_in_desc __initdata = {
+static struct usb_endpoint_descriptor acm_hs_in_desc __usb_initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512),
};
-static struct usb_endpoint_descriptor acm_hs_out_desc __initdata = {
+static struct usb_endpoint_descriptor acm_hs_out_desc __usb_initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512),
};
-static struct usb_descriptor_header *acm_hs_function[] __initdata = {
+static struct usb_descriptor_header *acm_hs_function[] __usb_initdata = {
(struct usb_descriptor_header *) &acm_iad_descriptor,
(struct usb_descriptor_header *) &acm_control_interface_desc,
(struct usb_descriptor_header *) &acm_header_desc,
@@ -571,7 +573,7 @@ static int acm_send_break(struct gserial *port, int duration)
/*-------------------------------------------------------------------------*/
/* ACM function driver setup/binding */
-static int __init
+static int __usb_init
acm_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
@@ -719,7 +721,7 @@ static inline bool can_support_cdc(struct usb_configuration *c)
* handle all the ones it binds. Caller is also responsible
* for calling @gserial_cleanup() before module unload.
*/
-int __init acm_bind_config(struct usb_configuration *c, u8 port_num)
+int __usb_init acm_bind_config(struct usb_configuration *c, u8 port_num)
{
struct f_acm *acm;
int status;
diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c
index 4e59532..c880d45 100644
--- a/drivers/usb/gadget/f_ecm.c
+++ b/drivers/usb/gadget/f_ecm.c
@@ -113,7 +113,7 @@ static inline unsigned ecm_bitrate(struct usb_gadget *g)
/* interface descriptor: */
-static struct usb_interface_descriptor ecm_control_intf __initdata = {
+static struct usb_interface_descriptor ecm_control_intf __usb_initdata = {
.bLength = sizeof ecm_control_intf,
.bDescriptorType = USB_DT_INTERFACE,
@@ -126,7 +126,7 @@ static struct usb_interface_descriptor ecm_control_intf __initdata = {
/* .iInterface = DYNAMIC */
};
-static struct usb_cdc_header_desc ecm_header_desc __initdata = {
+static struct usb_cdc_header_desc ecm_header_desc __usb_initdata = {
.bLength = sizeof ecm_header_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_HEADER_TYPE,
@@ -134,7 +134,7 @@ static struct usb_cdc_header_desc ecm_header_desc __initdata = {
.bcdCDC = cpu_to_le16(0x0110),
};
-static struct usb_cdc_union_desc ecm_union_desc __initdata = {
+static struct usb_cdc_union_desc ecm_union_desc __usb_initdata = {
.bLength = sizeof(ecm_union_desc),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_UNION_TYPE,
@@ -142,7 +142,7 @@ static struct usb_cdc_union_desc ecm_union_desc __initdata = {
/* .bSlaveInterface0 = DYNAMIC */
};
-static struct usb_cdc_ether_desc ecm_desc __initdata = {
+static struct usb_cdc_ether_desc ecm_desc __usb_initdata = {
.bLength = sizeof ecm_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_ETHERNET_TYPE,
@@ -157,7 +157,7 @@ static struct usb_cdc_ether_desc ecm_desc __initdata = {
/* the default data interface has no endpoints ... */
-static struct usb_interface_descriptor ecm_data_nop_intf __initdata = {
+static struct usb_interface_descriptor ecm_data_nop_intf __usb_initdata = {
.bLength = sizeof ecm_data_nop_intf,
.bDescriptorType = USB_DT_INTERFACE,
@@ -172,7 +172,7 @@ static struct usb_interface_descriptor ecm_data_nop_intf __initdata = {
/* ... but the "real" data interface has two bulk endpoints */
-static struct usb_interface_descriptor ecm_data_intf __initdata = {
+static struct usb_interface_descriptor ecm_data_intf __usb_initdata = {
.bLength = sizeof ecm_data_intf,
.bDescriptorType = USB_DT_INTERFACE,
@@ -187,7 +187,7 @@ static struct usb_interface_descriptor ecm_data_intf __initdata = {
/* full speed support: */
-static struct usb_endpoint_descriptor fs_ecm_notify_desc __initdata = {
+static struct usb_endpoint_descriptor fs_ecm_notify_desc __usb_initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -197,7 +197,7 @@ static struct usb_endpoint_descriptor fs_ecm_notify_desc __initdata = {
.bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC,
};
-static struct usb_endpoint_descriptor fs_ecm_in_desc __initdata = {
+static struct usb_endpoint_descriptor fs_ecm_in_desc __usb_initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -205,7 +205,7 @@ static struct usb_endpoint_descriptor fs_ecm_in_desc __initdata = {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
-static struct usb_endpoint_descriptor fs_ecm_out_desc __initdata = {
+static struct usb_endpoint_descriptor fs_ecm_out_desc __usb_initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -213,7 +213,7 @@ static struct usb_endpoint_descriptor fs_ecm_out_desc __initdata = {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
-static struct usb_descriptor_header *ecm_fs_function[] __initdata = {
+static struct usb_descriptor_header *ecm_fs_function[] __usb_initdata = {
/* CDC ECM control descriptors */
(struct usb_descriptor_header *) &ecm_control_intf,
(struct usb_descriptor_header *) &ecm_header_desc,
@@ -231,7 +231,7 @@ static struct usb_descriptor_header *ecm_fs_function[] __initdata = {
/* high speed support: */
-static struct usb_endpoint_descriptor hs_ecm_notify_desc __initdata = {
+static struct usb_endpoint_descriptor hs_ecm_notify_desc __usb_initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -240,7 +240,7 @@ static struct usb_endpoint_descriptor hs_ecm_notify_desc __initdata = {
.wMaxPacketSize = cpu_to_le16(ECM_STATUS_BYTECOUNT),
.bInterval = LOG2_STATUS_INTERVAL_MSEC + 4,
};
-static struct usb_endpoint_descriptor hs_ecm_in_desc __initdata = {
+static struct usb_endpoint_descriptor hs_ecm_in_desc __usb_initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -249,7 +249,7 @@ static struct usb_endpoint_descriptor hs_ecm_in_desc __initdata = {
.wMaxPacketSize = cpu_to_le16(512),
};
-static struct usb_endpoint_descriptor hs_ecm_out_desc __initdata = {
+static struct usb_endpoint_descriptor hs_ecm_out_desc __usb_initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -258,7 +258,7 @@ static struct usb_endpoint_descriptor hs_ecm_out_desc __initdata = {
.wMaxPacketSize = cpu_to_le16(512),
};
-static struct usb_descriptor_header *ecm_hs_function[] __initdata = {
+static struct usb_descriptor_header *ecm_hs_function[] __usb_initdata = {
/* CDC ECM control descriptors */
(struct usb_descriptor_header *) &ecm_control_intf,
(struct usb_descriptor_header *) &ecm_header_desc,
@@ -597,7 +597,7 @@ static void ecm_close(struct gether *geth)
/* ethernet function driver setup/binding */
-static int __init
+static int __usb_init
ecm_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
@@ -763,7 +763,8 @@ ecm_unbind(struct usb_configuration *c, struct usb_function *f)
* Caller must have called @gether_setup(). Caller is also responsible
* for calling @gether_cleanup() before module unload.
*/
-int __init ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
+int __usb_init
+ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
{
struct f_ecm *ecm;
int status;
diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c
index 56b0221..35e8744 100644
--- a/drivers/usb/gadget/f_rndis.c
+++ b/drivers/usb/gadget/f_rndis.c
@@ -122,7 +122,7 @@ static unsigned int bitrate(struct usb_gadget *g)
/* interface descriptor: */
-static struct usb_interface_descriptor rndis_control_intf __initdata = {
+static struct usb_interface_descriptor rndis_control_intf __usb_initdata = {
.bLength = sizeof rndis_control_intf,
.bDescriptorType = USB_DT_INTERFACE,
@@ -135,7 +135,7 @@ static struct usb_interface_descriptor rndis_control_intf __initdata = {
/* .iInterface = DYNAMIC */
};
-static struct usb_cdc_header_desc header_desc __initdata = {
+static struct usb_cdc_header_desc header_desc __usb_initdata = {
.bLength = sizeof header_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_HEADER_TYPE,
@@ -143,7 +143,8 @@ static struct usb_cdc_header_desc header_desc __initdata = {
.bcdCDC = cpu_to_le16(0x0110),
};
-static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor __initdata = {
+static struct usb_cdc_call_mgmt_descriptor
+call_mgmt_descriptor __usb_initdata = {
.bLength = sizeof call_mgmt_descriptor,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
@@ -152,7 +153,7 @@ static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor __initdata = {
.bDataInterface = 0x01,
};
-static struct usb_cdc_acm_descriptor rndis_acm_descriptor __initdata = {
+static struct usb_cdc_acm_descriptor rndis_acm_descriptor __usb_initdata = {
.bLength = sizeof rndis_acm_descriptor,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_ACM_TYPE,
@@ -160,7 +161,7 @@ static struct usb_cdc_acm_descriptor rndis_acm_descriptor __initdata = {
.bmCapabilities = 0x00,
};
-static struct usb_cdc_union_desc rndis_union_desc __initdata = {
+static struct usb_cdc_union_desc rndis_union_desc __usb_initdata = {
.bLength = sizeof(rndis_union_desc),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_UNION_TYPE,
@@ -170,7 +171,7 @@ static struct usb_cdc_union_desc rndis_union_desc __initdata = {
/* the data interface has two bulk endpoints */
-static struct usb_interface_descriptor rndis_data_intf __initdata = {
+static struct usb_interface_descriptor rndis_data_intf __usb_initdata = {
.bLength = sizeof rndis_data_intf,
.bDescriptorType = USB_DT_INTERFACE,
@@ -198,7 +199,7 @@ rndis_iad_descriptor = {
/* full speed support: */
-static struct usb_endpoint_descriptor fs_notify_desc __initdata = {
+static struct usb_endpoint_descriptor fs_notify_desc __usb_initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -208,7 +209,7 @@ static struct usb_endpoint_descriptor fs_notify_desc __initdata = {
.bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC,
};
-static struct usb_endpoint_descriptor fs_in_desc __initdata = {
+static struct usb_endpoint_descriptor fs_in_desc __usb_initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -216,7 +217,7 @@ static struct usb_endpoint_descriptor fs_in_desc __initdata = {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
-static struct usb_endpoint_descriptor fs_out_desc __initdata = {
+static struct usb_endpoint_descriptor fs_out_desc __usb_initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -224,7 +225,7 @@ static struct usb_endpoint_descriptor fs_out_desc __initdata = {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
-static struct usb_descriptor_header *eth_fs_function[] __initdata = {
+static struct usb_descriptor_header *eth_fs_function[] __usb_initdata = {
(struct usb_descriptor_header *) &rndis_iad_descriptor,
/* control interface matches ACM, not Ethernet */
(struct usb_descriptor_header *) &rndis_control_intf,
@@ -242,7 +243,7 @@ static struct usb_descriptor_header *eth_fs_function[] __initdata = {
/* high speed support: */
-static struct usb_endpoint_descriptor hs_notify_desc __initdata = {
+static struct usb_endpoint_descriptor hs_notify_desc __usb_initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -251,7 +252,7 @@ static struct usb_endpoint_descriptor hs_notify_desc __initdata = {
.wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT),
.bInterval = LOG2_STATUS_INTERVAL_MSEC + 4,
};
-static struct usb_endpoint_descriptor hs_in_desc __initdata = {
+static struct usb_endpoint_descriptor hs_in_desc __usb_initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -260,7 +261,7 @@ static struct usb_endpoint_descriptor hs_in_desc __initdata = {
.wMaxPacketSize = cpu_to_le16(512),
};
-static struct usb_endpoint_descriptor hs_out_desc __initdata = {
+static struct usb_endpoint_descriptor hs_out_desc __usb_initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -269,7 +270,7 @@ static struct usb_endpoint_descriptor hs_out_desc __initdata = {
.wMaxPacketSize = cpu_to_le16(512),
};
-static struct usb_descriptor_header *eth_hs_function[] __initdata = {
+static struct usb_descriptor_header *eth_hs_function[] __usb_initdata = {
(struct usb_descriptor_header *) &rndis_iad_descriptor,
/* control interface matches ACM, not Ethernet */
(struct usb_descriptor_header *) &rndis_control_intf,
@@ -594,7 +595,7 @@ static void rndis_close(struct gether *geth)
/* ethernet function driver setup/binding */
-static int __init
+static int __usb_init
rndis_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
@@ -786,7 +787,8 @@ static inline bool can_support_rndis(struct usb_configuration *c)
* Caller must have called @gether_setup(). Caller is also responsible
* for calling @gether_cleanup() before module unload.
*/
-int __init rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
+int __usb_init
+rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
{
struct f_rndis *rndis;
int status;
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
index 07f4178..2d46412 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -31,6 +31,8 @@
#include "u_ether.h"
+#include "usb-init-exit.h"
+
/*
* This component encapsulates the Ethernet link glue needed to provide
@@ -715,7 +717,7 @@ static u8 __init nibble(unsigned char c)
return 0;
}
-static int __init get_ether_addr(const char *str, u8 *dev_addr)
+static int __usb_init get_ether_addr(const char *str, u8 *dev_addr)
{
if (str) {
unsigned i;
@@ -764,7 +766,7 @@ static struct device_type gadget_type = {
*
* Returns negative errno, or zero on success
*/
-int __init gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN])
+int __usb_init gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN])
{
struct eth_dev *dev;
struct net_device *net;
--
1.7.0
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
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 | 2475 ++++++++++++++++++++++++++++++++++++++++
include/linux/usb/functionfs.h | 199 ++++
2 files changed, 2674 insertions(+), 0 deletions(-)
create mode 100644 drivers/usb/gadget/f_fs.c
create mode 100644 include/linux/usb/functionfs.h
diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c
new file mode 100644
index 0000000..d218a40
--- /dev/null
+++ b/drivers/usb/gadget/f_fs.c
@@ -0,0 +1,2475 @@
+/*
+ * f_fs.c -- user mode filesystem api for usb composite funtcion controllers
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ * Author: Michal Nazarewicz <[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));
+
+
+/* Control file aka ep0 *****************************************************/
+
+static void ffs_ep0_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct ffs_data *ffs = req->context;
+
+ complete_all(&ffs->ep0req_completion);
+}
+
+
+static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
+{
+ struct usb_request *req = ffs->ep0req;
+ int ret;
+
+ req->zero = len < le16_to_cpu(ffs->ev.setup.wLength);
+
+ spin_unlock_irq(&ffs->ev.waitq.lock);
+
+ req->buf = data;
+ req->length = len;
+
+ INIT_COMPLETION(ffs->ep0req_completion);
+
+ ret = usb_ep_queue(ffs->gadget->ep0, req, GFP_ATOMIC);
+ if (unlikely(ret < 0))
+ return ret;
+
+ ret = wait_for_completion_interruptible(&ffs->ep0req_completion);
+ if (unlikely(ret)) {
+ usb_ep_dequeue(ffs->gadget->ep0, req);
+ return -EINTR;
+ }
+
+ ffs->setup_state = FFS_NO_SETUP;
+ return ffs->ep0req_status;
+}
+
+static int __ffs_ep0_stall(struct ffs_data *ffs)
+{
+ if (ffs->ev.can_stall) {
+ FVDBG("ep0 stall\n");
+ usb_ep_set_halt(ffs->gadget->ep0);
+ ffs->setup_state = FFS_NO_SETUP;
+ return -EL2HLT;
+ } else {
+ FDBG("bogus ep0 stall!\n");
+ return -ESRCH;
+ }
+}
+
+
+static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ptr)
+{
+ struct ffs_data *ffs = file->private_data;
+ ssize_t ret;
+ char *data;
+
+ ENTER();
+
+ /* Fast check if setup was canceled */
+ if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED)
+ return -EIDRM;
+
+ /* Acquire mutex */
+ ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
+ if (unlikely(ret < 0))
+ return ret;
+
+
+ /* Check state */
+ switch (ffs->state) {
+ case FFS_READ_DESCRIPTORS:
+ case FFS_READ_STRINGS:
+ /* Copy data */
+ if (unlikely(len < 16)) {
+ ret = -EINVAL;
+ break;
+ }
+
+ data = ffs_prepare_buffer(buf, len);
+ if (unlikely(IS_ERR(data))) {
+ ret = PTR_ERR(data);
+ break;
+ }
+
+ /* Handle data */
+ if (ffs->state == FFS_READ_DESCRIPTORS) {
+ FINFO("read descriptors");
+ ret = __ffs_data_got_descs(ffs, data, len);
+ if (unlikely(ret < 0))
+ break;
+
+ ffs->state = FFS_READ_STRINGS;
+ ret = len;
+ } else {
+ FINFO("read strings");
+ ret = __ffs_data_got_strings(ffs, data, len);
+ if (unlikely(ret < 0))
+ break;
+
+ ret = ffs_epfiles_create(ffs);
+ if (unlikely(ret)) {
+ ffs->state = FFS_CLOSING;
+ break;
+ }
+
+ ffs->state = FFS_ACTIVE;
+ mutex_unlock(&ffs->mutex);
+
+ ret = functionfs_ready_callback(ffs);
+ if (unlikely(ret < 0)) {
+ ffs->state = FFS_CLOSING;
+ return ret;
+ }
+
+ set_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags);
+ return len;
+ }
+ break;
+
+
+ case FFS_ACTIVE:
+ data = NULL;
+ /* We're called from user space, we can use _irq
+ * rather then _irqsave */
+ spin_lock_irq(&ffs->ev.waitq.lock);
+ switch (FFS_SETUP_STATE(ffs)) {
+ case FFS_SETUP_CANCELED:
+ ret = -EIDRM;
+ goto done_spin;
+
+ case FFS_NO_SETUP:
+ ret = -ESRCH;
+ goto done_spin;
+
+ case FFS_SETUP_PENDING:
+ break;
+ }
+
+ /* FFS_SETUP_PENDING */
+ if (!(ffs->ev.setup.bRequestType & USB_DIR_IN)) {
+ ret = __ffs_ep0_stall(ffs);
+ goto done_spin;
+ }
+
+ /* FFS_SETUP_PENDING and not stall */
+ len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength));
+
+ spin_unlock_irq(&ffs->ev.waitq.lock);
+
+ data = ffs_prepare_buffer(buf, len);
+ if (unlikely(IS_ERR(data))) {
+ ret = PTR_ERR(data);
+ break;
+ }
+
+ spin_lock_irq(&ffs->ev.waitq.lock);
+
+ /* We are guaranteed to be still in FFS_ACTIVE state
+ * but the state of setup could have changed from
+ * FFS_SETUP_PENDING to FFS_SETUP_CANCELED so we need
+ * to check for that. If that happened we copied data
+ * from user space in vain but it's unlikely. */
+ /* For sure we are not in FFS_NO_SETUP since this is
+ * the only place FFS_SETUP_PENDING -> FFS_NO_SETUP
+ * transition can be performed and it's protected by
+ * mutex. */
+
+ if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) {
+ ret = -EIDRM;
+done_spin:
+ spin_unlock_irq(&ffs->ev.waitq.lock);
+ } else {
+ /* unlocks spinlock */
+ ret = __ffs_ep0_queue_wait(ffs, data, len);
+ }
+ kfree(data);
+ break;
+
+
+ default:
+ ret = -EBADFD;
+ break;
+ }
+
+
+ mutex_unlock(&ffs->mutex);
+ return ret;
+}
+
+
+
+static int __ffs_ep0_read_wait_for_events(struct ffs_data *ffs)
+{
+ /* We are holding ffs->ev.waitq.lock */
+
+ DEFINE_WAIT(wait);
+ int ret = 0;
+
+ wait.flags |= WQ_FLAG_EXCLUSIVE;
+ __add_wait_queue_tail(&ffs->ev.waitq, &wait);
+
+ do {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ spin_unlock_irq(&ffs->ev.waitq.lock);
+ schedule();
+ spin_lock_irq(&ffs->ev.waitq.lock);
+ } while (!ffs->ev.count);
+
+ __remove_wait_queue(&ffs->ev.waitq, &wait);
+ __set_current_state(TASK_RUNNING);
+
+ return ret;
+}
+
+static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
+ size_t n)
+{
+ /* We are holding ffs->ev.waitq.lock and ffs->mutex and we need
+ * to release them. */
+
+ struct usb_functionfs_event events[n];
+ unsigned i = 0;
+
+ memset(events, 0, sizeof events);
+
+ do {
+ events[i].type = ffs->ev.types[i];
+ if (events[i].type == FUNCTIONFS_SETUP) {
+ events[i].u.setup = ffs->ev.setup;
+ ffs->setup_state = FFS_SETUP_PENDING;
+ }
+ } while (++i < n);
+
+ ffs->ev.count = 0;
+
+ spin_unlock_irq(&ffs->ev.waitq.lock);
+ mutex_unlock(&ffs->mutex);
+
+ return unlikely(__copy_to_user(buf, events, sizeof events))
+ ? -EFAULT : sizeof events;
+}
+
+
+static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
+ size_t len, loff_t *ptr)
+{
+ struct ffs_data *ffs = file->private_data;
+ size_t n;
+ char *data;
+ int ret;
+
+ ENTER();
+
+ /* Fast check if setup was canceled */
+ if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED)
+ return -EIDRM;
+
+ /* Acquire mutex */
+ ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
+ if (unlikely(ret < 0))
+ return ret;
+
+
+ /* Check state */
+ if (ffs->state != FFS_ACTIVE) {
+ ret = -EBADFD;
+ goto done_mutex;
+ }
+
+
+ /* We're called from user space, we can use _irq rather then
+ * _irqsave */
+ spin_lock_irq(&ffs->ev.waitq.lock);
+
+ switch (FFS_SETUP_STATE(ffs)) {
+ case FFS_SETUP_CANCELED:
+ ret = -EIDRM;
+ break;
+
+ case FFS_NO_SETUP:
+ n = len / sizeof(struct usb_functionfs_event);
+ if (unlikely(!n)) {
+ ret = -EINVAL;
+ break;
+ }
+
+ if ((file->f_flags & O_NONBLOCK) && !ffs->ev.count) {
+ ret = -EAGAIN;
+ break;
+ }
+
+ if (!ffs->ev.count &&
+ unlikely(__ffs_ep0_read_wait_for_events(ffs))) {
+ ret = -EINTR;
+ break;
+ }
+
+ return __ffs_ep0_read_events(ffs, buf,
+ min(n, (size_t)ffs->ev.count));
+
+
+ case FFS_SETUP_PENDING:
+ if (ffs->ev.setup.bRequestType & USB_DIR_IN) {
+ ret = __ffs_ep0_stall(ffs);
+ break;
+ }
+
+ len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength));
+
+ spin_unlock_irq(&ffs->ev.waitq.lock);
+
+ data = ffs_prepare_buffer(NULL, len);
+ if (unlikely(IS_ERR(data))) {
+ ret = PTR_ERR(data);
+ goto done_mutex;
+ }
+
+ spin_lock_irq(&ffs->ev.waitq.lock);
+
+ /* See ffs_ep0_write() */
+ if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) {
+ kfree(data);
+ ret = -EIDRM;
+ break;
+ }
+
+ /* unlocks spinlock */
+ ret = __ffs_ep0_queue_wait(ffs, data, len);
+ if (likely(ret > 0) && unlikely(__copy_to_user(buf, data, len)))
+ ret = -EFAULT;
+ kfree(data);
+ goto done_mutex;
+
+ default:
+ ret = -EBADFD;
+ break;
+ }
+
+ spin_unlock_irq(&ffs->ev.waitq.lock);
+done_mutex:
+ mutex_unlock(&ffs->mutex);
+ return ret;
+}
+
+
+
+static int ffs_ep0_open(struct inode *inode, struct file *file)
+{
+ struct ffs_data *ffs = inode->i_private;
+
+ ENTER();
+
+ if (unlikely(ffs->state == FFS_CLOSING))
+ return -EBUSY;
+
+ file->private_data = ffs;
+ ffs_data_opened(ffs);
+
+ return 0;
+}
+
+
+static int ffs_ep0_release(struct inode *inode, struct file *file)
+{
+ struct ffs_data *ffs = file->private_data;
+
+ ENTER();
+
+ ffs_data_closed(ffs);
+
+ return 0;
+}
+
+
+static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value)
+{
+ struct ffs_data *ffs = file->private_data;
+ struct usb_gadget *gadget = ffs->gadget;
+ long ret;
+
+ ENTER();
+
+ if (code == FUNCTIONFS_INTERFACE_REVMAP) {
+ struct ffs_function *func = ffs->func;
+ ret = func ? ffs_func_revmap_intf(func, value) : -ENODEV;
+ } else if (gadget->ops->ioctl) {
+ lock_kernel();
+ ret = gadget->ops->ioctl(gadget, code, value);
+ unlock_kernel();
+ } else {
+ ret = -ENOTTY;
+ }
+
+ return ret;
+}
+
+
+static const struct file_operations ffs_ep0_operations = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+
+ .open = ffs_ep0_open,
+ .write = ffs_ep0_write,
+ .read = ffs_ep0_read,
+ .release = ffs_ep0_release,
+ .unlocked_ioctl = ffs_ep0_ioctl,
+};
+
+
+/* "Normal" endpoints operations ********************************************/
+
+
+static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req)
+{
+ ENTER();
+ if (likely(req->context)) {
+ struct ffs_ep *ep = _ep->driver_data;
+ ep->status = req->status ? req->status : req->actual;
+ complete(req->context);
+ }
+}
+
+
+static ssize_t ffs_epfile_io(struct file *file,
+ char __user *buf, size_t len, int read)
+{
+ struct ffs_epfile *epfile = file->private_data;
+ struct ffs_ep *ep;
+ char *data = NULL;
+ ssize_t ret;
+ int halt;
+
+ goto first_try;
+ do {
+ spin_unlock_irq(&epfile->ffs->eps_lock);
+ mutex_unlock(&epfile->mutex);
+
+first_try:
+ /* Are we still active? */
+ if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) {
+ ret = -ENODEV;
+ goto error;
+ }
+
+ /* Wait for endpoint to be enabled */
+ ep = epfile->ep;
+ if (!ep) {
+ if (file->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ goto error;
+ }
+
+ if (unlikely(wait_event_interruptible
+ (epfile->wait, (ep = epfile->ep)))) {
+ ret = -EINTR;
+ goto error;
+ }
+ }
+
+ /* Do we halt? */
+ halt = !read == !epfile->in;
+ if (halt && epfile->isoc) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* Allocate & copy */
+ if (!halt && !data) {
+ data = kzalloc(len, GFP_KERNEL);
+ if (unlikely(!data))
+ return -ENOMEM;
+
+ if (!read &&
+ unlikely(__copy_from_user(data, buf, len))) {
+ ret = -EFAULT;
+ goto error;
+ }
+ }
+
+ /* We will be using request */
+ ret = ffs_mutex_lock(&epfile->mutex,
+ file->f_flags & O_NONBLOCK);
+ if (unlikely(ret))
+ goto error;
+
+ /* We're called from user space, we can use _irq rather then
+ * _irqsave */
+ spin_lock_irq(&epfile->ffs->eps_lock);
+
+ /* While we were acquiring mutex endpoint got disabled
+ * or changed? */
+ } while (unlikely(epfile->ep != ep));
+
+ /* Halt */
+ if (unlikely(halt)) {
+ if (likely(epfile->ep == ep) && !WARN_ON(!ep->ep))
+ usb_ep_set_halt(ep->ep);
+ spin_unlock_irq(&epfile->ffs->eps_lock);
+ ret = -EBADMSG;
+ } else {
+ /* Fire the request */
+ DECLARE_COMPLETION_ONSTACK(done);
+
+ struct usb_request *req = ep->req;
+ req->context = &done;
+ req->complete = ffs_epfile_io_complete;
+ req->buf = data;
+ req->length = len;
+
+ ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
+
+ spin_unlock_irq(&epfile->ffs->eps_lock);
+
+ if (unlikely(ret < 0)) {
+ /* nop */
+ } else if (unlikely(wait_for_completion_interruptible(&done))) {
+ ret = -EINTR;
+ usb_ep_dequeue(ep->ep, req);
+ } else {
+ ret = ep->status;
+ if (read && ret > 0 &&
+ unlikely(copy_to_user(buf, data, ret)))
+ ret = -EFAULT;
+ }
+ }
+
+ mutex_unlock(&epfile->mutex);
+error:
+ kfree(data);
+ return ret;
+}
+
+
+static ssize_t
+ffs_epfile_write(struct file *file, const char __user *buf, size_t len,
+ loff_t *ptr)
+{
+ ENTER();
+
+ return ffs_epfile_io(file, (char __user *)buf, len, 0);
+}
+
+static ssize_t
+ffs_epfile_read(struct file *file, char __user *buf, size_t len, loff_t *ptr)
+{
+ ENTER();
+
+ return ffs_epfile_io(file, buf, len, 1);
+}
+
+static int
+ffs_epfile_open(struct inode *inode, struct file *file)
+{
+ struct ffs_epfile *epfile = inode->i_private;
+
+ ENTER();
+
+ if (WARN_ON(epfile->ffs->state != FFS_ACTIVE))
+ return -ENODEV;
+
+ file->private_data = epfile;
+ ffs_data_opened(epfile->ffs);
+
+ return 0;
+}
+
+static int
+ffs_epfile_release(struct inode *inode, struct file *file)
+{
+ struct ffs_epfile *epfile = inode->i_private;
+
+ ENTER();
+
+ ffs_data_closed(epfile->ffs);
+
+ return 0;
+}
+
+
+static long ffs_epfile_ioctl(struct file *file, unsigned code,
+ unsigned long value)
+{
+ struct ffs_epfile *epfile = file->private_data;
+ int ret;
+
+ ENTER();
+
+ if (WARN_ON(epfile->ffs->state != FFS_ACTIVE))
+ return -ENODEV;
+
+ spin_lock_irq(&epfile->ffs->eps_lock);
+ if (likely(epfile->ep)) {
+ switch (code) {
+ case FUNCTIONFS_FIFO_STATUS:
+ ret = usb_ep_fifo_status(epfile->ep->ep);
+ break;
+ case FUNCTIONFS_FIFO_FLUSH:
+ usb_ep_fifo_flush(epfile->ep->ep);
+ ret = 0;
+ break;
+ case FUNCTIONFS_CLEAR_HALT:
+ ret = usb_ep_clear_halt(epfile->ep->ep);
+ break;
+ case FUNCTIONFS_ENDPOINT_REVMAP:
+ ret = epfile->ep->num;
+ break;
+ default:
+ ret = -ENOTTY;
+ }
+ } else {
+ ret = -ENODEV;
+ }
+ spin_unlock_irq(&epfile->ffs->eps_lock);
+
+ return ret;
+}
+
+
+static const struct file_operations ffs_epfile_operations = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+
+ .open = ffs_epfile_open,
+ .write = ffs_epfile_write,
+ .read = ffs_epfile_read,
+ .release = ffs_epfile_release,
+ .unlocked_ioctl = ffs_epfile_ioctl,
+};
+
+
+
+/* File system and super block operations ***********************************/
+
+/*
+ * Mounting the filesystem creates a controller file, used first for
+ * function configuration then later for event monitoring.
+ */
+
+
+static struct inode *__must_check
+ffs_sb_make_inode(struct super_block *sb, void *data,
+ const struct file_operations *fops,
+ const struct inode_operations *iops,
+ struct ffs_file_perms *perms)
+{
+ struct inode *inode;
+
+ ENTER();
+
+ inode = new_inode(sb);
+
+ if (likely(inode)) {
+ struct timespec current_time = CURRENT_TIME;
+
+ inode->i_mode = perms->mode;
+ inode->i_uid = perms->uid;
+ inode->i_gid = perms->gid;
+ inode->i_atime = current_time;
+ inode->i_mtime = current_time;
+ inode->i_ctime = current_time;
+ inode->i_private = data;
+ if (fops)
+ inode->i_fop = fops;
+ if (iops)
+ inode->i_op = iops;
+ }
+
+ return inode;
+}
+
+
+/* Create "regular" file */
+
+static struct inode *ffs_sb_create_file(struct super_block *sb,
+ const char *name, void *data,
+ const struct file_operations *fops,
+ struct dentry **dentry_p)
+{
+ struct ffs_data *ffs = sb->s_fs_info;
+ struct dentry *dentry;
+ struct inode *inode;
+
+ ENTER();
+
+ dentry = d_alloc_name(sb->s_root, name);
+ if (unlikely(!dentry))
+ return NULL;
+
+ inode = ffs_sb_make_inode(sb, data, fops, NULL, &ffs->file_perms);
+ if (unlikely(!inode)) {
+ dput(dentry);
+ return NULL;
+ }
+
+ d_add(dentry, inode);
+ if (dentry_p)
+ *dentry_p = dentry;
+
+ return inode;
+}
+
+
+/* Super block */
+
+static const struct super_operations ffs_sb_operations = {
+ .statfs = simple_statfs,
+ .drop_inode = generic_delete_inode,
+};
+
+struct ffs_sb_fill_data {
+ struct ffs_file_perms perms;
+ umode_t root_mode;
+ const char *dev_name;
+};
+
+static int ffs_sb_fill(struct super_block *sb, void *_data, int silent)
+{
+ struct ffs_sb_fill_data *data = _data;
+ struct inode *inode;
+ struct dentry *d;
+ struct ffs_data *ffs;
+
+ ENTER();
+
+ /* Initialize data */
+ ffs = ffs_data_new();
+ if (unlikely(!ffs))
+ goto enomem0;
+
+ ffs->sb = sb;
+ ffs->dev_name = data->dev_name;
+ ffs->file_perms = data->perms;
+
+ sb->s_fs_info = ffs;
+ sb->s_blocksize = PAGE_CACHE_SIZE;
+ sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+ sb->s_magic = FUNCTIONFS_MAGIC;
+ sb->s_op = &ffs_sb_operations;
+ sb->s_time_gran = 1;
+
+ /* Root inode */
+ data->perms.mode = data->root_mode;
+ inode = ffs_sb_make_inode(sb, NULL,
+ &simple_dir_operations,
+ &simple_dir_inode_operations,
+ &data->perms);
+ if (unlikely(!inode))
+ goto enomem1;
+ d = d_alloc_root(inode);
+ if (unlikely(!d))
+ goto enomem2;
+ sb->s_root = d;
+
+ /* EP0 file */
+ if (unlikely(!ffs_sb_create_file(sb, "ep0", ffs,
+ &ffs_ep0_operations, NULL)))
+ goto enomem3;
+
+ return 0;
+
+enomem3:
+ dput(d);
+enomem2:
+ iput(inode);
+enomem1:
+ ffs_data_put(ffs);
+enomem0:
+ return -ENOMEM;
+}
+
+
+static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
+{
+ ENTER();
+
+ if (!opts || !*opts)
+ return 0;
+
+ for (;;) {
+ char *end, *eq, *comma;
+ unsigned long value;
+
+ /* Option limit */
+ comma = strchr(opts, ',');
+ if (comma)
+ *comma = 0;
+
+ /* Value limit */
+ eq = strchr(opts, '=');
+ if (unlikely(!eq)) {
+ FERR("'=' missing in %s", opts);
+ return -EINVAL;
+ }
+ *eq = 0;
+
+ /* Parse value */
+ value = simple_strtoul(eq + 1, &end, 0);
+ if (unlikely(*end != ',' && *end != 0)) {
+ FERR("%s: invalid value: %s", opts, eq + 1);
+ return -EINVAL;
+ }
+
+ /* Interpret option */
+ switch (eq - opts) {
+ case 5:
+ if (!memcmp(opts, "rmode", 5))
+ data->root_mode = (value & 0555) | S_IFDIR;
+ else if (!memcmp(opts, "fmode", 5))
+ data->perms.mode = (value & 0666) | S_IFREG;
+ else
+ goto invalid;
+ break;
+
+ case 4:
+ if (!memcmp(opts, "mode", 4)) {
+ data->root_mode = (value & 0555) | S_IFDIR;
+ data->perms.mode = (value & 0666) | S_IFREG;
+ } else {
+ goto invalid;
+ }
+ break;
+
+ case 3:
+ if (!memcmp(opts, "uid", 3))
+ data->perms.uid = value;
+ else if (!memcmp(opts, "gid", 3))
+ data->perms.gid = value;
+ else
+ goto invalid;
+ break;
+
+ default:
+invalid:
+ FERR("%s: invalid option", opts);
+ return -EINVAL;
+ }
+
+ /* Next iteration */
+ if (!comma)
+ break;
+ opts = comma + 1;
+ }
+
+ return 0;
+}
+
+
+/* "mount -t functionfs dev_name /dev/function" ends up here */
+
+static int
+ffs_fs_get_sb(struct file_system_type *t, int flags,
+ const char *dev_name, void *opts, struct vfsmount *mnt)
+{
+ struct ffs_sb_fill_data data = {
+ .perms = {
+ .mode = S_IFREG | 0600,
+ .uid = 0,
+ .gid = 0
+ },
+ .root_mode = S_IFDIR | 0500,
+ };
+ int ret;
+
+ ENTER();
+
+ ret = functionfs_check_dev_callback(dev_name);
+ if (unlikely(ret < 0))
+ return ret;
+
+ ret = ffs_fs_parse_opts(&data, opts);
+ if (unlikely(ret < 0))
+ return ret;
+
+ data.dev_name = dev_name;
+ return get_sb_single(t, flags, &data, ffs_sb_fill, mnt);
+}
+
+static void
+ffs_fs_kill_sb(struct super_block *sb)
+{
+ void *ptr;
+
+ ENTER();
+
+ kill_litter_super(sb);
+ ptr = xchg(&sb->s_fs_info, NULL);
+ if (ptr)
+ ffs_data_put(ptr);
+}
+
+static struct file_system_type ffs_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "functionfs",
+ .get_sb = ffs_fs_get_sb,
+ .kill_sb = ffs_fs_kill_sb,
+};
+
+
+
+/* Driver's main init/cleanup functions *************************************/
+
+
+static int functionfs_init(void)
+{
+ int ret;
+
+ ENTER();
+
+ ret = register_filesystem(&ffs_fs_type);
+ if (likely(!ret))
+ FINFO("file system registered");
+ else
+ FERR("failed registering file system (%d)", ret);
+
+ return ret;
+}
+
+static void functionfs_cleanup(void)
+{
+ ENTER();
+
+ FINFO("unloading");
+ unregister_filesystem(&ffs_fs_type);
+}
+
+
+
+/* ffs_data and ffs_function construction and destruction code **************/
+
+static void ffs_data_clear(struct ffs_data *ffs);
+static void ffs_data_reset(struct ffs_data *ffs);
+
+
+static void ffs_data_get(struct ffs_data *ffs)
+{
+ ENTER();
+
+ atomic_inc(&ffs->ref);
+}
+
+static void ffs_data_opened(struct ffs_data *ffs)
+{
+ ENTER();
+
+ atomic_inc(&ffs->ref);
+ atomic_inc(&ffs->opened);
+}
+
+static void ffs_data_put(struct ffs_data *ffs)
+{
+ ENTER();
+
+ if (unlikely(atomic_dec_and_test(&ffs->ref))) {
+ FINFO("%s(): freeing", __func__);
+ ffs_data_clear(ffs);
+ BUG_ON(mutex_is_locked(&ffs->mutex) ||
+ spin_is_locked(&ffs->ev.waitq.lock) ||
+ waitqueue_active(&ffs->ev.waitq) ||
+ waitqueue_active(&ffs->ep0req_completion.wait));
+ kfree(ffs);
+ }
+}
+
+
+
+static void ffs_data_closed(struct ffs_data *ffs)
+{
+ ENTER();
+
+ if (atomic_dec_and_test(&ffs->opened)) {
+ ffs->state = FFS_CLOSING;
+ ffs_data_reset(ffs);
+ }
+
+ ffs_data_put(ffs);
+}
+
+
+static struct ffs_data *ffs_data_new(void)
+{
+ struct ffs_data *ffs = kzalloc(sizeof *ffs, GFP_KERNEL);
+ if (unlikely(!ffs))
+ return 0;
+
+ ENTER();
+
+ atomic_set(&ffs->ref, 1);
+ atomic_set(&ffs->opened, 0);
+ ffs->state = FFS_READ_DESCRIPTORS;
+ mutex_init(&ffs->mutex);
+ spin_lock_init(&ffs->eps_lock);
+ init_waitqueue_head(&ffs->ev.waitq);
+ init_completion(&ffs->ep0req_completion);
+
+ /* XXX REVISIT need to update it in some places, or do we? */
+ ffs->ev.can_stall = 1;
+
+ return ffs;
+}
+
+
+static void ffs_data_clear(struct ffs_data *ffs)
+{
+ ENTER();
+
+ if (test_and_clear_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags))
+ functionfs_closed_callback(ffs);
+
+ BUG_ON(ffs->gadget);
+
+ if (ffs->epfiles)
+ ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count);
+
+ kfree(ffs->raw_descs);
+ kfree(ffs->raw_strings);
+ kfree(ffs->stringtabs);
+}
+
+
+static void ffs_data_reset(struct ffs_data *ffs)
+{
+ ENTER();
+
+ ffs_data_clear(ffs);
+
+ ffs->epfiles = NULL;
+ ffs->raw_descs = NULL;
+ ffs->raw_strings = NULL;
+ ffs->stringtabs = NULL;
+
+ ffs->raw_descs_length = 0;
+ ffs->raw_fs_descs_length = 0;
+ ffs->fs_descs_count = 0;
+ ffs->hs_descs_count = 0;
+
+ ffs->strings_count = 0;
+ ffs->interfaces_count = 0;
+ ffs->eps_count = 0;
+
+ ffs->ev.count = 0;
+
+ ffs->state = FFS_READ_DESCRIPTORS;
+ ffs->setup_state = FFS_NO_SETUP;
+ ffs->flags = 0;
+}
+
+
+static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev)
+{
+ unsigned i, count;
+
+ ENTER();
+
+ if (WARN_ON(ffs->state != FFS_ACTIVE
+ || test_and_set_bit(FFS_FL_BOUND, &ffs->flags)))
+ return -EBADFD;
+
+ ffs_data_get(ffs);
+
+ ffs->gadget = cdev->gadget;
+ ffs->ep0req = usb_ep_alloc_request(ffs->gadget->ep0, GFP_KERNEL);
+ if (unlikely(!ffs->ep0req))
+ return -ENOMEM;
+ ffs->ep0req->complete = ffs_ep0_complete;
+ ffs->ep0req->context = ffs;
+
+ /* Get strings identifiers */
+ for (count = ffs->strings_count, i = 0; i < count; ++i) {
+ struct usb_gadget_strings **lang;
+
+ int id = usb_string_id(cdev);
+ if (unlikely(id < 0))
+ return id;
+
+ lang = ffs->stringtabs;
+ do {
+ (*lang)->strings[i].id = id;
+ ++lang;
+ } while (*lang);
+ }
+
+ return 0;
+}
+
+
+static void functionfs_unbind(struct ffs_data *ffs)
+{
+ ENTER();
+
+ if (!WARN_ON(!ffs->gadget)) {
+ usb_ep_free_request(ffs->gadget->ep0, ffs->ep0req);
+ ffs->ep0req = NULL;
+ ffs->gadget = NULL;
+ ffs_data_put(ffs);
+ }
+}
+
+
+static int ffs_epfiles_create(struct ffs_data *ffs)
+{
+ struct ffs_epfile *epfile, *epfiles;
+ unsigned i, count;
+
+ ENTER();
+
+ count = ffs->eps_count;
+ epfiles = kzalloc(count * sizeof *epfiles, GFP_KERNEL);
+ if (!epfiles)
+ return -ENOMEM;
+
+ epfile = epfiles;
+ for (i = 1; i <= count; ++i, ++epfile) {
+ epfile->ffs = ffs;
+ mutex_init(&epfile->mutex);
+ init_waitqueue_head(&epfile->wait);
+ sprintf(epfiles->name, "ep%u", i);
+ if (!unlikely(ffs_sb_create_file(ffs->sb, epfiles->name, epfile,
+ &ffs_epfile_operations,
+ &epfile->dentry))) {
+ ffs_epfiles_destroy(epfiles, i - 1);
+ return -ENOMEM;
+ }
+ }
+
+ ffs->epfiles = epfiles;
+ return 0;
+}
+
+
+static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
+{
+ struct ffs_epfile *epfile = epfiles;
+
+ ENTER();
+
+ for (; count; --count, ++epfile) {
+ BUG_ON(mutex_is_locked(&epfile->mutex) ||
+ waitqueue_active(&epfile->wait));
+ if (epfile->dentry) {
+ d_delete(epfile->dentry);
+ dput(epfile->dentry);
+ epfile->dentry = NULL;
+ }
+ }
+
+ kfree(epfiles);
+}
+
+
+static int functionfs_add(struct usb_composite_dev *cdev,
+ struct usb_configuration *c,
+ struct ffs_data *ffs)
+{
+ struct ffs_function *func;
+ int ret;
+
+ ENTER();
+
+ func = kzalloc(sizeof *func, GFP_KERNEL);
+ if (unlikely(!func))
+ return -ENOMEM;
+
+ func->function.name = "Function FS Gadget";
+ func->function.strings = ffs->stringtabs;
+
+ func->function.bind = ffs_func_bind;
+ func->function.unbind = ffs_func_unbind;
+ func->function.set_alt = ffs_func_set_alt;
+ /*func->function.get_alt = ffs_func_get_alt;*/
+ func->function.disable = ffs_func_disable;
+ func->function.setup = ffs_func_setup;
+ func->function.suspend = ffs_func_suspend;
+ func->function.resume = ffs_func_resume;
+
+ func->conf = c;
+ func->gadget = cdev->gadget;
+ func->ffs = ffs;
+ ffs_data_get(ffs);
+
+ ret = usb_add_function(c, &func->function);
+ if (unlikely(ret))
+ ffs_func_free(func);
+
+ return ret;
+}
+
+static void ffs_func_free(struct ffs_function *func)
+{
+ ENTER();
+
+ ffs_data_put(func->ffs);
+
+ kfree(func->eps);
+ /* eps and interfaces_nums are allocated in the same chunk so
+ * only one free is required. Descriptors are also allocated
+ * in the same chunk. */
+
+ kfree(func);
+}
+
+
+static void ffs_func_eps_disable(struct ffs_function *func)
+{
+ struct ffs_ep *ep = func->eps;
+ struct ffs_epfile *epfile = func->ffs->epfiles;
+ unsigned count = func->ffs->eps_count;
+ unsigned long flags;
+
+ spin_lock_irqsave(&func->ffs->eps_lock, flags);
+ do {
+ /* pending requests get nuked */
+ if (likely(ep->ep))
+ usb_ep_disable(ep->ep);
+ epfile->ep = NULL;
+
+ ++ep;
+ ++epfile;
+ } while (--count);
+ spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
+}
+
+static int ffs_func_eps_enable(struct ffs_function *func)
+{
+ struct ffs_data *ffs = func->ffs;
+ struct ffs_ep *ep = func->eps;
+ struct ffs_epfile *epfile = ffs->epfiles;
+ unsigned count = ffs->eps_count;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&func->ffs->eps_lock, flags);
+ do {
+ struct usb_endpoint_descriptor *ds;
+ ds = ep->descs[ep->descs[1] ? 1 : 0];
+
+ ep->ep->driver_data = ep;
+ ret = usb_ep_enable(ep->ep, ds);
+ if (likely(!ret)) {
+ epfile->ep = ep;
+ epfile->in = usb_endpoint_dir_in(ds);
+ epfile->isoc = usb_endpoint_xfer_isoc(ds);
+ } else {
+ break;
+ }
+
+ wake_up(&epfile->wait);
+
+ ++ep;
+ ++epfile;
+ } while (--count);
+ spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
+
+ return ret;
+}
+
+
+/* Parsing and building descriptors and strings *****************************/
+
+
+/* This validates if data pointed by data is a valid USB descriptor as
+ * well as record how many interfaces, endpoints and strings are
+ * required by given configuration. Returns address afther the
+ * descriptor or NULL if data is invalid. */
+
+enum ffs_entity_type {
+ FFS_DESCRIPTOR, FFS_INTERFACE, FFS_STRING, FFS_ENDPOINT
+};
+
+typedef int (*ffs_entity_callback)(enum ffs_entity_type entity,
+ u8 *valuep,
+ struct usb_descriptor_header *desc,
+ void *priv);
+
+static int __must_check ffs_do_desc(char *data, unsigned len,
+ ffs_entity_callback entity, void *priv)
+{
+ struct usb_descriptor_header *_ds = (void *)data;
+ u8 length;
+ int ret;
+
+ ENTER();
+
+ /* At least two bytes are required: length and type */
+ if (len < 2)
+ return -EINVAL;
+
+ /* If we have at least as many bytes as the descriptor takes? */
+ length = _ds->bLength;
+ if (len < length)
+ return -EINVAL;
+
+#define __entity_check_INTERFACE(val) 1
+#define __entity_check_STRING(val) (val)
+#define __entity_check_ENDPOINT(val) ((val) & USB_ENDPOINT_NUMBER_MASK)
+#define __entity(type, val) do { \
+ FVDBG("entity " #type "(%02x)", (val)); \
+ if (unlikely(!__entity_check_ ##type(val))) \
+ return -EINVAL; \
+ ret = entity(FFS_ ##type, &val, _ds, priv); \
+ if (unlikely(ret < 0)) { \
+ FDBG("entity " #type "(%02x); ret = %d", \
+ (val), ret); \
+ return ret; \
+ } \
+ } while (0)
+
+ /* Parse descriptor depending on type. */
+ switch (_ds->bDescriptorType) {
+ case USB_DT_DEVICE:
+ case USB_DT_CONFIG:
+ case USB_DT_STRING:
+ case USB_DT_DEVICE_QUALIFIER:
+ /* function can't have any of those */
+ return -EINVAL;
+
+ case USB_DT_INTERFACE: {
+ struct usb_interface_descriptor *ds = (void *)_ds;
+ FVDBG("interface descriptor");
+ if (length != sizeof *ds)
+ return -EINVAL;
+
+ __entity(INTERFACE, ds->bInterfaceNumber);
+ if (ds->iInterface)
+ __entity(STRING, ds->iInterface);
+ }
+ break;
+
+ case USB_DT_ENDPOINT: {
+ struct usb_endpoint_descriptor *ds = (void *)_ds;
+ FVDBG("endpoint descriptor");
+ if (length != USB_DT_ENDPOINT_SIZE &&
+ length != USB_DT_ENDPOINT_AUDIO_SIZE)
+ return -EINVAL;
+ if (!(ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK))
+ return -EINVAL;
+ __entity(ENDPOINT, ds->bEndpointAddress);
+ }
+ break;
+
+ case USB_DT_OTHER_SPEED_CONFIG:
+ /* TODO */
+ return -EINVAL;
+
+ case USB_DT_INTERFACE_POWER:
+ /* TODO */
+ return -EINVAL;
+
+ case USB_DT_OTG:
+ if (length != sizeof(struct usb_otg_descriptor))
+ return -EINVAL;
+ break;
+
+ case USB_DT_DEBUG:
+ /* TODO */
+ return -EINVAL;
+
+ case USB_DT_INTERFACE_ASSOCIATION: {
+ struct usb_interface_assoc_descriptor *ds = (void *)_ds;
+ FVDBG("interface association descriptor");
+ if (length != sizeof *ds)
+ return -EINVAL;
+ if (ds->iFunction)
+ __entity(STRING, ds->iFunction);
+ }
+ break;
+
+ case USB_DT_SECURITY:
+ /* TODO */
+ return -EINVAL;
+
+ case USB_DT_KEY:
+ /* TODO */
+ return -EINVAL;
+
+ case USB_DT_ENCRYPTION_TYPE:
+ /* TODO */
+ return -EINVAL;
+
+ case USB_DT_BOS:
+ /* TODO */
+ return -EINVAL;
+
+ case USB_DT_DEVICE_CAPABILITY:
+ /* TODO */
+ return -EINVAL;
+
+ case USB_DT_WIRELESS_ENDPOINT_COMP:
+ /* TODO */
+ return -EINVAL;
+
+ case USB_DT_WIRE_ADAPTER:
+ /* TODO */
+ return -EINVAL;
+
+ case USB_DT_RPIPE:
+ /* TODO */
+ return -EINVAL;
+
+ case USB_DT_CS_RADIO_CONTROL:
+ /* TODO */
+ return -EINVAL;
+
+ default:
+ /* We should never be here */
+ return -EINVAL;
+ }
+
+#undef __entity
+#undef __entity_check_DESCRIPTOR
+#undef __entity_check_INTERFACE
+#undef __entity_check_STRING
+#undef __entity_check_ENDPOINT
+
+ return length;
+}
+
+
+static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
+ ffs_entity_callback entity, void *priv)
+{
+ const unsigned _len = len;
+ long num = 0;
+
+ ENTER();
+
+ for (;;) {
+ int ret;
+
+ if (num == count)
+ data = NULL;
+
+ /* Record "descriptor" entitny */
+ ret = entity(FFS_DESCRIPTOR, (u8 *)num, (void *)data, priv);
+ if (unlikely(ret < 0)) {
+ FDBG("entity DESCRIPTOR(%02x); ret = %d", num, ret);
+ return ret;
+ }
+
+ if (!data)
+ return _len - len;
+
+ ret = ffs_do_desc(data, len, entity, priv);
+ if (unlikely(ret < 0)) {
+ FDBG("%s returns %d", __func__, ret);
+ return ret;
+ }
+
+ len -= ret;
+ data += ret;
+ ++num;
+ }
+}
+
+
+static int __ffs_data_do_entity(enum ffs_entity_type type,
+ u8 *valuep, struct usb_descriptor_header *desc,
+ void *priv)
+{
+ struct ffs_data *ffs = priv;
+
+ ENTER();
+
+ switch (type) {
+ case FFS_DESCRIPTOR:
+ break;
+
+ case FFS_INTERFACE:
+ /* Interfaces are indexed from zero so if we
+ * encountered interface "n" then there are at least
+ * "n+1" interfaces. */
+ if (*valuep >= ffs->interfaces_count)
+ ffs->interfaces_count = *valuep + 1;
+ break;
+
+ case FFS_STRING:
+ /* Strings are indexed from 1 (0 is magic ;) reserved
+ * for languages list or some such) */
+ if (*valuep > ffs->strings_count)
+ ffs->strings_count = *valuep;
+ break;
+
+ case FFS_ENDPOINT:
+ /* Endpoints are indexed from 1 as well. */
+ if ((*valuep & USB_ENDPOINT_NUMBER_MASK) > ffs->eps_count)
+ ffs->eps_count = (*valuep & USB_ENDPOINT_NUMBER_MASK);
+ break;
+ }
+
+ return 0;
+}
+
+
+static int __ffs_data_got_descs(struct ffs_data *ffs,
+ char *const _data, size_t len)
+{
+ unsigned fs_count, hs_count;
+ int fs_len, ret = -EINVAL;
+ char *data = _data;
+
+ ENTER();
+
+ if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_DESCRIPTORS_MAGIC ||
+ get_unaligned_le32(data + 4) != len))
+ goto error;
+ fs_count = get_unaligned_le32(data + 8);
+ hs_count = get_unaligned_le32(data + 12);
+
+ if (!fs_count && !hs_count)
+ goto einval;
+
+ data += 16;
+ len -= 16;
+
+ if (likely(fs_count)) {
+ fs_len = ffs_do_descs(fs_count, data, len,
+ __ffs_data_do_entity, ffs);
+ if (unlikely(fs_len < 0)) {
+ ret = fs_len;
+ goto error;
+ }
+
+ data += fs_len;
+ len -= fs_len;
+ } else {
+ fs_len = 0;
+ }
+
+ if (likely(hs_count)) {
+ ret = ffs_do_descs(hs_count, data, len,
+ __ffs_data_do_entity, ffs);
+ if (unlikely(ret < 0))
+ goto error;
+ } else {
+ ret = 0;
+ }
+
+ if (unlikely(len != ret))
+ goto einval;
+
+ ffs->raw_fs_descs_length = fs_len;
+ ffs->raw_descs_length = fs_len + ret;
+ ffs->raw_descs = _data;
+ ffs->fs_descs_count = fs_count;
+ ffs->hs_descs_count = hs_count;
+
+ return 0;
+
+einval:
+ ret = -EINVAL;
+error:
+ kfree(_data);
+ return ret;
+}
+
+
+
+static int __ffs_data_got_strings(struct ffs_data *ffs,
+ char *const _data, size_t len)
+{
+ u32 str_count, needed_count, lang_count;
+ struct usb_gadget_strings **stringtabs, *t;
+ struct usb_string *strings, *s;
+ const char *data = _data;
+
+ ENTER();
+
+ if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_STRINGS_MAGIC ||
+ get_unaligned_le32(data + 4) != len))
+ goto error;
+ str_count = get_unaligned_le32(data + 8);
+ lang_count = get_unaligned_le32(data + 12);
+
+ /* if one is zero the other must be zero */
+ if (unlikely(!str_count != !lang_count))
+ goto error;
+
+ /* Do we have at least as many strings as descriptors need? */
+ needed_count = ffs->strings_count;
+ if (unlikely(str_count < needed_count))
+ goto error;
+
+ /* If we don't need any strings just return and free all
+ * memory */
+ if (!needed_count) {
+ kfree(_data);
+ return 0;
+ }
+
+ /* Allocate */
+ {
+ /* Allocate everything in one chunk so there's less
+ * maintanance. */
+ struct {
+ struct usb_gadget_strings *stringtabs[lang_count + 1];
+ struct usb_gadget_strings stringtab[lang_count];
+ struct usb_string strings[lang_count*(needed_count+1)];
+ } *d;
+ unsigned i = 0;
+
+ d = kmalloc(sizeof *d, GFP_KERNEL);
+ if (unlikely(!d)) {
+ kfree(_data);
+ return -ENOMEM;
+ }
+
+ stringtabs = d->stringtabs;
+ t = d->stringtab;
+ i = lang_count;
+ do {
+ *stringtabs++ = t++;
+ } while (--i);
+ *stringtabs = NULL;
+
+ stringtabs = d->stringtabs;
+ t = d->stringtab;
+ s = d->strings;
+ strings = s;
+ }
+
+ /* For each language */
+ data += 16;
+ len -= 16;
+
+ do { /* lang_count > 0 so we can use do-while */
+ unsigned needed = needed_count;
+
+ if (unlikely(len < 3))
+ goto error_free;
+ t->language = get_unaligned_le16(data);
+ t->strings = s;
+ ++t;
+
+ data += 2;
+ len -= 2;
+
+ /* For each string */
+ do { /* str_count > 0 so we can use do-while */
+ size_t length = strnlen(data, len);
+
+ if (unlikely(length == len))
+ goto error_free;
+
+ /* user may provide more strings then we need,
+ * if that's the case we simply ingore the
+ * rest */
+ if (likely(needed)) {
+ /* s->id will be set while adding
+ * function to configuration so for
+ * now just leave garbage here. */
+ s->s = data;
+ --needed;
+ ++s;
+ }
+
+ data += length + 1;
+ len -= length + 1;
+ } while (--str_count);
+
+ s->id = 0; /* terminator */
+ s->s = NULL;
+ ++s;
+
+ } while (--lang_count);
+
+ /* Some garbage left? */
+ if (unlikely(len))
+ goto error_free;
+
+ /* Done! */
+ ffs->stringtabs = stringtabs;
+ ffs->raw_strings = _data;
+
+ return 0;
+
+error_free:
+ kfree(stringtabs);
+error:
+ kfree(_data);
+ return -EINVAL;
+}
+
+
+
+
+/* Events handling and management *******************************************/
+
+static void __ffs_event_add(struct ffs_data *ffs,
+ enum usb_functionfs_event_type type)
+{
+ enum usb_functionfs_event_type rem_type1 = type;
+ enum usb_functionfs_event_type rem_type2 = type;
+ int neg = 0;
+
+ /* Abort any unhandled setup */
+ if (ffs->setup_state == FFS_SETUP_PENDING)
+ ffs->setup_state = FFS_SETUP_CANCELED;
+
+ switch (type) {
+ case FUNCTIONFS_RESUME:
+ rem_type2 = FUNCTIONFS_SUSPEND;
+ /* FALL THGOUTH */
+ case FUNCTIONFS_SUSPEND:
+ case FUNCTIONFS_SETUP:
+ /* discard all similar events */
+ break;
+
+ case FUNCTIONFS_BIND:
+ case FUNCTIONFS_UNBIND:
+ case FUNCTIONFS_DISABLE:
+ case FUNCTIONFS_ENABLE:
+ /* discard everything other then power management. */
+ rem_type1 = FUNCTIONFS_SUSPEND;
+ rem_type2 = FUNCTIONFS_RESUME;
+ neg = 1;
+ break;
+
+ default:
+ BUG();
+ }
+
+ {
+ u8 *ev = ffs->ev.types, *out = ev;
+ unsigned n = ffs->ev.count;
+ for (; n; --n, ++ev)
+ if ((*ev == rem_type1 || *ev == rem_type2) == neg)
+ *out++ = *ev;
+ ffs->ev.count = out - ffs->ev.types;
+ }
+
+ ffs->ev.types[ffs->ev.count++] = type;
+ wake_up_locked(&ffs->ev.waitq);
+}
+
+static void ffs_event_add(struct ffs_data *ffs,
+ enum usb_functionfs_event_type type)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&ffs->ev.waitq.lock, flags);
+ __ffs_event_add(ffs, type);
+ spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags);
+}
+
+
+/* Bind/unbind USB function hooks *******************************************/
+
+static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
+ struct usb_descriptor_header *desc,
+ void *priv)
+{
+ struct usb_endpoint_descriptor *ds = (void *)desc;
+ struct ffs_function *func = priv;
+ struct ffs_ep *ffs_ep;
+
+ /* If hs_descriptors is not NULL then we are reading hs
+ * descriptors now */
+ const int isHS = func->function.hs_descriptors != NULL;
+ unsigned idx;
+
+ if (type != FFS_DESCRIPTOR)
+ return 0;
+
+ if (isHS)
+ func->function.hs_descriptors[(long)valuep] = desc;
+ else
+ func->function.descriptors[(long)valuep] = desc;
+
+ if (!desc || desc->bDescriptorType != USB_DT_ENDPOINT)
+ return 0;
+
+ idx = (ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) - 1;
+ ffs_ep = func->eps + idx;
+
+ if (unlikely(ffs_ep->descs[isHS]))
+ return -EINVAL;
+ ffs_ep->descs[isHS] = ds;
+
+ ffs_dump_mem(": Original ep desc", ds, ds->bLength);
+ if (ffs_ep->ep) {
+ ds->bEndpointAddress = ffs_ep->descs[0]->bEndpointAddress;
+ if (!ds->wMaxPacketSize)
+ ds->wMaxPacketSize = ffs_ep->descs[0]->wMaxPacketSize;
+ } else {
+ struct usb_request *req;
+ struct usb_ep *ep;
+
+ FVDBG("autoconfig");
+ ep = usb_ep_autoconfig(func->gadget, ds);
+ if (unlikely(!ep))
+ return -ENOTSUPP;
+ ep->driver_data = func->eps + idx;;
+
+ req = usb_ep_alloc_request(ep, GFP_KERNEL);
+ if (unlikely(!req))
+ return -ENOMEM;
+
+ ffs_ep->ep = ep;
+ ffs_ep->req = req;
+ func->eps_revmap[ds->bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK] = idx + 1;
+ }
+ ffs_dump_mem(": Rewritten ep desc", ds, ds->bLength);
+
+ return 0;
+}
+
+
+static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep,
+ struct usb_descriptor_header *desc,
+ void *priv)
+{
+ struct ffs_function *func = priv;
+ unsigned idx;
+ u8 newValue;
+
+ switch (type) {
+ default:
+ case FFS_DESCRIPTOR:
+ /* Handled in previous pass by __ffs_func_bind_do_descs() */
+ return 0;
+
+ case FFS_INTERFACE:
+ idx = *valuep;
+ if (func->interfaces_nums[idx] < 0) {
+ int id = usb_interface_id(func->conf, &func->function);
+ if (unlikely(id < 0))
+ return id;
+ func->interfaces_nums[idx] = id;
+ }
+ newValue = func->interfaces_nums[idx];
+ break;
+
+ case FFS_STRING:
+ /* String' IDs are allocated when fsf_data is bound to cdev */
+ newValue = func->ffs->stringtabs[0]->strings[*valuep - 1].id;
+ break;
+
+ case FFS_ENDPOINT:
+ /* USB_DT_ENDPOINT are handled in
+ * __ffs_func_bind_do_descs(). */
+ if (desc->bDescriptorType == USB_DT_ENDPOINT)
+ return 0;
+
+ idx = (*valuep & USB_ENDPOINT_NUMBER_MASK) - 1;
+ if (unlikely(!func->eps[idx].ep))
+ return -EINVAL;
+
+ {
+ struct usb_endpoint_descriptor **descs;
+ descs = func->eps[idx].descs;
+ newValue = descs[descs[0] ? 0 : 1]->bEndpointAddress;
+ }
+ break;
+ }
+
+ FVDBG("%02x -> %02x", *valuep, newValue);
+ *valuep = newValue;
+ return 0;
+}
+
+static int ffs_func_bind(struct usb_configuration *c,
+ struct usb_function *f)
+{
+ struct ffs_function *func = ffs_func_from_usb(f);
+ struct ffs_data *ffs = func->ffs;
+
+ const int full = !!func->ffs->fs_descs_count;
+ const int high = gadget_is_dualspeed(func->gadget) &&
+ func->ffs->hs_descs_count;
+
+ int ret;
+
+ /* Make it a single chunk, less management later on */
+ struct {
+ struct ffs_ep eps[ffs->eps_count];
+ struct usb_descriptor_header
+ *fs_descs[full ? ffs->fs_descs_count + 1 : 0];
+ struct usb_descriptor_header
+ *hs_descs[high ? ffs->hs_descs_count + 1 : 0];
+ short inums[ffs->interfaces_count];
+ char raw_descs[high ? ffs->raw_descs_length
+ : ffs->raw_fs_descs_length];
+ } *data;
+
+ ENTER();
+
+ /* Only high speed but not supported by gadget? */
+ if (unlikely(!(full | high)))
+ return -ENOTSUPP;
+
+ /* Allocate */
+ data = kmalloc(sizeof *data, GFP_KERNEL);
+ if (unlikely(!data))
+ return -ENOMEM;
+
+ /* Zero */
+ memset(data->eps, 0, sizeof data->eps);
+ memcpy(data->raw_descs, ffs->raw_descs + 16, sizeof data->raw_descs);
+ memset(data->inums, 0xff, sizeof data->inums);
+ for (ret = ffs->eps_count; ret; --ret)
+ data->eps[ret].num = -1;
+
+ /* Save pointers */
+ func->eps = data->eps;
+ func->interfaces_nums = data->inums;
+
+ /* Go throught all the endpoint descriptors and allocate
+ * endpoints first, so that later we can rewrite the endpoint
+ * numbers without worying that it may be described later on. */
+ if (likely(full)) {
+ func->function.descriptors = data->fs_descs;
+ ret = ffs_do_descs(ffs->fs_descs_count,
+ data->raw_descs,
+ sizeof data->raw_descs,
+ __ffs_func_bind_do_descs, func);
+ if (unlikely(ret < 0))
+ goto error;
+ } else {
+ ret = 0;
+ }
+
+ if (likely(high)) {
+ func->function.hs_descriptors = data->hs_descs;
+ ret = ffs_do_descs(ffs->hs_descs_count,
+ data->raw_descs + ret,
+ (sizeof data->raw_descs) - ret,
+ __ffs_func_bind_do_descs, func);
+ }
+
+ /* Now handle interface numbers allocation and interface and
+ * enpoint numbers rewritting. We can do that in one go
+ * now. */
+ ret = ffs_do_descs(ffs->fs_descs_count +
+ (high ? ffs->hs_descs_count : 0),
+ data->raw_descs, sizeof data->raw_descs,
+ __ffs_func_bind_do_nums, func);
+ if (unlikely(ret < 0))
+ goto error;
+
+ /* And we're done */
+ ffs_event_add(ffs, FUNCTIONFS_BIND);
+ return 0;
+
+error:
+ /* XXX Do we need to release all claimed endpoints here? */
+ return ret;
+}
+
+
+/* Other USB function hooks *************************************************/
+
+static void ffs_func_unbind(struct usb_configuration *c,
+ struct usb_function *f)
+{
+ struct ffs_function *func = ffs_func_from_usb(f);
+ struct ffs_data *ffs = func->ffs;
+
+ ENTER();
+
+ if (ffs->func == func) {
+ ffs_func_eps_disable(func);
+ ffs->func = NULL;
+ }
+
+ ffs_event_add(ffs, FUNCTIONFS_UNBIND);
+
+ ffs_func_free(func);
+}
+
+
+static int ffs_func_set_alt(struct usb_function *f,
+ unsigned interface, unsigned alt)
+{
+ struct ffs_function *func = ffs_func_from_usb(f);
+ struct ffs_data *ffs = func->ffs;
+ int ret = 0, intf;
+
+ if (alt != (unsigned)-1) {
+ intf = ffs_func_revmap_intf(func, interface);
+ if (unlikely(intf < 0))
+ return intf;
+ }
+
+ if (ffs->func)
+ ffs_func_eps_disable(ffs->func);
+
+ if (ffs->state != FFS_ACTIVE)
+ return -ENODEV;
+
+ if (alt == (unsigned)-1) {
+ ffs->func = NULL;
+ ffs_event_add(ffs, FUNCTIONFS_DISABLE);
+ return 0;
+ }
+
+ ffs->func = func;
+ ret = ffs_func_eps_enable(func);
+ if (likely(ret >= 0))
+ ffs_event_add(ffs, FUNCTIONFS_ENABLE);
+ return ret;
+}
+
+static void ffs_func_disable(struct usb_function *f)
+{
+ ffs_func_set_alt(f, 0, (unsigned)-1);
+}
+
+static int ffs_func_setup(struct usb_function *f,
+ const struct usb_ctrlrequest *creq)
+{
+ struct ffs_function *func = ffs_func_from_usb(f);
+ struct ffs_data *ffs = func->ffs;
+ unsigned long flags;
+ int ret;
+
+ ENTER();
+
+ FVDBG("creq->bRequestType = %02x", creq->bRequestType);
+ FVDBG("creq->bRequest = %02x", creq->bRequest);
+ FVDBG("creq->wValue = %04x", le16_to_cpu(creq->wValue));
+ FVDBG("creq->wIndex = %04x", le16_to_cpu(creq->wIndex));
+ FVDBG("creq->wLength = %04x", le16_to_cpu(creq->wLength));
+
+ /* Most requests directed to interface go throught here
+ * (notable exceptions are set/get interface) so we need to
+ * handle them. All other either handled by composite or
+ * passed to usb_configuration->setup() (if one is set). No
+ * matter, we will handle requests directed to endpoint here
+ * as well (as it's straightforward) but what to do with any
+ * other request? */
+
+ if (ffs->state != FFS_ACTIVE)
+ return -ENODEV;
+
+ switch (creq->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_INTERFACE:
+ ret = ffs_func_revmap_intf(func, le16_to_cpu(creq->wIndex));
+ if (0) /* FALL THROUGHT but skip next instruction */
+ case USB_RECIP_ENDPOINT:
+ ret = ffs_func_revmap_ep(func,
+ le16_to_cpu(creq->wIndex));
+ if (unlikely(ret < 0))
+ return ret;
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ spin_lock_irqsave(&ffs->ev.waitq.lock, flags);
+ ffs->ev.setup = *creq;
+ ffs->ev.setup.wIndex = cpu_to_le16(ret);
+ __ffs_event_add(ffs, FUNCTIONFS_SETUP);
+ spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags);
+
+ return 0;
+}
+
+static void ffs_func_suspend(struct usb_function *f)
+{
+ ENTER();
+ ffs_event_add(ffs_func_from_usb(f)->ffs, FUNCTIONFS_SUSPEND);
+}
+
+static void ffs_func_resume(struct usb_function *f)
+{
+ ENTER();
+ ffs_event_add(ffs_func_from_usb(f)->ffs, FUNCTIONFS_RESUME);
+}
+
+
+
+/* Enpoint and interface numbers reverse mapping ****************************/
+
+static int ffs_func_revmap_ep(struct ffs_function *func, u8 num)
+{
+ num = func->eps_revmap[num & USB_ENDPOINT_NUMBER_MASK];
+ return num ? num : -EDOM;
+}
+
+static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf)
+{
+ short *nums = func->interfaces_nums;
+ unsigned count = func->ffs->interfaces_count;
+
+ for (; count; --count, ++nums) {
+ if (*nums >= 0 && *nums == intf)
+ return nums - func->interfaces_nums;
+ }
+
+ return -EDOM;
+}
+
+
+/* Misc helper functions ****************************************************/
+
+static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock)
+{
+ return nonblock
+ ? likely(mutex_trylock(mutex)) ? 0 : -EAGAIN
+ : mutex_lock_interruptible(mutex);
+}
+
+
+static char *ffs_prepare_buffer(const char * __user buf, size_t len)
+{
+ char *data;
+
+ if (unlikely(!len))
+ return NULL;
+
+ data = kmalloc(len, GFP_KERNEL);
+ if (unlikely(!data))
+ return ERR_PTR(-ENOMEM);
+
+ if (likely(buf != NULL) && unlikely(__copy_from_user(data, buf, len))) {
+ kfree(data);
+ return ERR_PTR(-EFAULT);
+ }
+
+ if (likely(buf)) {
+ FVDBG("Buffer from user space:");
+ ffs_dump_mem("", data, len);
+ }
+
+ return data;
+}
diff --git a/include/linux/usb/functionfs.h b/include/linux/usb/functionfs.h
new file mode 100644
index 0000000..a34a2a0
--- /dev/null
+++ b/include/linux/usb/functionfs.h
@@ -0,0 +1,199 @@
+#ifndef __LINUX_FUNCTIONFS_H__
+#define __LINUX_FUNCTIONFS_H__ 1
+
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#include <linux/usb/ch9.h>
+
+
+enum {
+ FUNCTIONFS_DESCRIPTORS_MAGIC = 1,
+ FUNCTIONFS_STRINGS_MAGIC = 2
+};
+
+
+#ifndef __KERNEL__
+
+/* Descriptor of an non-audio endpoint */
+struct usb_endpoint_descriptor_no_audio {
+ __u8 bLength;
+ __u8 bDescriptorType;
+
+ __u8 bEndpointAddress;
+ __u8 bmAttributes;
+ __le16 wMaxPacketSize;
+ __u8 bInterval;
+} __attribute__((packed));
+
+
+/*
+ * All numbers must be in little endian order.
+ */
+
+struct usb_functionfs_descs_head {
+ __le32 magic;
+ __le32 length;
+ __le32 fs_count;
+ __le32 hs_count;
+} __attribute__((packed));
+
+/*
+ * Descriptors format:
+ *
+ * | off | name | type | description |
+ * |-----+-----------+--------------+--------------------------------------|
+ * | 0 | magic | LE32 | FUNCTIONFS_{FS,HS}_DESCRIPTORS_MAGIC |
+ * | 4 | lenght | LE32 | length of the whole data chunk |
+ * | 8 | fs_count | LE32 | number of full-speed descriptors |
+ * | 12 | hs_count | LE32 | number of high-speed descriptors |
+ * | 16 | fs_descrs | Descriptor[] | list of full-speed descriptors |
+ * | | hs_descrs | Descriptor[] | list of high-speed descriptors |
+ *
+ * descs are just valid USB descriptors and have the following format:
+ *
+ * | off | name | type | description |
+ * |-----+-----------------+------+--------------------------|
+ * | 0 | bLength | U8 | length of the descriptor |
+ * | 1 | bDescriptorType | U8 | descriptor type |
+ * | 2 | payload | | descriptor's payload |
+ */
+
+struct usb_functionfs_strings_head {
+ __le32 magic;
+ __le32 length;
+ __le32 str_count;
+ __le32 lang_count;
+} __attribute__((packed));
+
+/*
+ * Strings format:
+ *
+ * | off | name | type | description |
+ * |-----+------------+-----------------------+----------------------------|
+ * | 0 | magic | LE32 | FUNCTIONFS_STRINGS_MAGIC |
+ * | 4 | length | LE32 | length of the data chunk |
+ * | 8 | str_count | LE32 | number of strings |
+ * | 12 | lang_count | LE32 | number of languages |
+ * | 16 | stringtab | StringTab[lang_count] | table of strings per lang |
+ *
+ * For each language there is one stringtab entry (ie. there are lang_count
+ * stringtab entires). Each StringTab has following format:
+ *
+ * | off | name | type | description |
+ * |-----+---------+-------------------+------------------------------------|
+ * | 0 | lang | LE16 | language code |
+ * | 2 | strings | String[str_count] | array of strings in given language |
+ *
+ * For each string ther is one strings entry (ie. there are str_count
+ * string entries). Each String is a NUL terminated string encoded in
+ * UTF-8.
+ */
+
+#endif
+
+
+/*
+ * Events are delivered on the ep0 file descriptor, when the user mode driver
+ * reads from this file descriptor after writing the descriptors. Don't
+ * stop polling this descriptor.
+ */
+
+enum usb_functionfs_event_type {
+ FUNCTIONFS_BIND,
+ FUNCTIONFS_UNBIND,
+
+ FUNCTIONFS_ENABLE,
+ FUNCTIONFS_DISABLE,
+
+ FUNCTIONFS_SETUP,
+
+ FUNCTIONFS_SUSPEND,
+ FUNCTIONFS_RESUME
+};
+
+/* NOTE: this structure must stay the same size and layout on
+ * both 32-bit and 64-bit kernels.
+ */
+struct usb_functionfs_event {
+ union {
+ /* SETUP: packet; DATA phase i/o precedes next event
+ *(setup.bmRequestType & USB_DIR_IN) flags direction */
+ struct usb_ctrlrequest setup;
+ } __attribute__((packed)) u;
+
+ /* enum usb_functionfs_event_type */
+ __u8 type;
+ __u8 _pad[3];
+} __attribute__((packed));
+
+
+/* Endpoint ioctls */
+/* The same as in gadgetfs */
+
+/* IN transfers may be reported to the gadget driver as complete
+ * when the fifo is loaded, before the host reads the data;
+ * OUT transfers may be reported to the host's "client" driver as
+ * complete when they're sitting in the FIFO unread.
+ * THIS returns how many bytes are "unclaimed" in the endpoint fifo
+ * (needed for precise fault handling, when the hardware allows it)
+ */
+#define FUNCTIONFS_FIFO_STATUS _IO('g', 1)
+
+/* discards any unclaimed data in the fifo. */
+#define FUNCTIONFS_FIFO_FLUSH _IO('g', 2)
+
+/* resets endpoint halt+toggle; used to implement set_interface.
+ * some hardware (like pxa2xx) can't support this.
+ */
+#define FUNCTIONFS_CLEAR_HALT _IO('g', 3)
+
+/* Specific for functionfs */
+
+/*
+ * Returns reverse mapping of an interface. Called on EP0. If there
+ * is no such interface returns -EDOM. If function is not active
+ * returns -ENODEV.
+ */
+#define FUNCTIONFS_INTERFACE_REVMAP _IO('g', 128)
+
+/*
+ * Returns real bEndpointAddress of an endpoint. If function is not
+ * active returns -ENODEV.
+ */
+#define FUNCTIONFS_ENDPOINT_REVMAP _IO('g', 129)
+
+
+#ifdef __KERNEL__
+
+struct ffs_data;
+struct usb_composite_dev;
+struct usb_configuration;
+
+
+static int functionfs_init(void) __attribute__((warn_unused_result));
+static void functionfs_cleanup(void);
+
+static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev)
+ __attribute__((warn_unused_result, nonnull));
+static void functionfs_unbind(struct ffs_data *ffs)
+ __attribute__((nonnull));
+
+static int functionfs_add(struct usb_composite_dev *cdev,
+ struct usb_configuration *c,
+ struct ffs_data *ffs)
+ __attribute__((warn_unused_result, nonnull));
+
+
+static int functionfs_ready_callback(struct ffs_data *ffs)
+ __attribute__((warn_unused_result, nonnull));
+static void functionfs_closed_callback(struct ffs_data *ffs)
+ __attribute__((nonnull));
+static int functionfs_check_dev_callback(const char *dev_name)
+ __attribute__((warn_unused_result, nonnull));
+
+
+#endif
+
+#endif
--
1.7.0
The testusb application can be used to test various USB gadget
that implment the source/sink intrerface.
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..28e25ca
--- /dev/null
+++ b/tools/usb/testusb.c
@@ -0,0 +1,427 @@
+/* $(CROSS_COMPILE)cc -Wall -g -lpthread -o testusb testusb.c */
+
+/*
+ * Copyright (c) 2002 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ftw.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <sys/ioctl.h>
+#include <linux/usbdevice_fs.h>
+
+/*-------------------------------------------------------------------------*/
+
+#define TEST_CASES 30
+
+// FIXME make these public somewhere; usbdevfs.h?
+
+struct usbtest_param {
+ // inputs
+ unsigned test_num; /* 0..(TEST_CASES-1) */
+ unsigned iterations;
+ unsigned length;
+ unsigned vary;
+ unsigned sglen;
+
+ // outputs
+ struct timeval duration;
+};
+#define USBTEST_REQUEST _IOWR('U', 100, struct usbtest_param)
+
+/*-------------------------------------------------------------------------*/
+
+/* #include <linux/usb_ch9.h> */
+
+struct usb_device_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u16 bcdUSB;
+ __u8 bDeviceClass;
+ __u8 bDeviceSubClass;
+ __u8 bDeviceProtocol;
+ __u8 bMaxPacketSize0;
+ __u16 idVendor;
+ __u16 idProduct;
+ __u16 bcdDevice;
+ __u8 iManufacturer;
+ __u8 iProduct;
+ __u8 iSerialNumber;
+ __u8 bNumConfigurations;
+} __attribute__ ((packed));
+
+enum usb_device_speed {
+ USB_SPEED_UNKNOWN = 0, /* enumerating */
+ USB_SPEED_LOW, USB_SPEED_FULL, /* usb 1.1 */
+ USB_SPEED_HIGH /* usb 2.0 */
+};
+
+/*-------------------------------------------------------------------------*/
+
+static char *speed (enum usb_device_speed s)
+{
+ switch (s) {
+ case USB_SPEED_UNKNOWN: return "unknown";
+ case USB_SPEED_LOW: return "low";
+ case USB_SPEED_FULL: return "full";
+ case USB_SPEED_HIGH: return "high";
+ default: return "??";
+ }
+}
+
+struct testdev {
+ struct testdev *next;
+ char *name;
+ pthread_t thread;
+ enum usb_device_speed speed;
+ unsigned ifnum : 8;
+ unsigned forever : 1;
+ int test;
+
+ struct usbtest_param param;
+};
+static struct testdev *testdevs;
+
+static int is_testdev (struct usb_device_descriptor *dev)
+{
+ /* FX2 with (tweaked) bulksrc firmware */
+ if (dev->idVendor == 0x0547 && dev->idProduct == 0x1002)
+ return 1;
+
+ /*----------------------------------------------------*/
+
+ /* devices that start up using the EZ-USB default device and
+ * which we can use after loading simple firmware. hotplug
+ * can fxload it, and then run this test driver.
+ *
+ * we return false positives in two cases:
+ * - the device has a "real" driver (maybe usb-serial) that
+ * renumerates. the device should vanish quickly.
+ * - the device doesn't have the test firmware installed.
+ */
+
+ /* generic EZ-USB FX controller */
+ if (dev->idVendor == 0x0547 && dev->idProduct == 0x2235)
+ return 1;
+
+ /* generic EZ-USB FX2 controller */
+ if (dev->idVendor == 0x04b4 && dev->idProduct == 0x8613)
+ return 1;
+
+ /* CY3671 development board with EZ-USB FX */
+ if (dev->idVendor == 0x0547 && dev->idProduct == 0x0080)
+ return 1;
+
+ /* Keyspan 19Qi uses an21xx (original EZ-USB) */
+ if (dev->idVendor == 0x06cd && dev->idProduct == 0x010b)
+ return 1;
+
+ /*----------------------------------------------------*/
+
+ /* "gadget zero", Linux-USB test software */
+ if (dev->idVendor == 0x0525 && dev->idProduct == 0xa4a0)
+ return 1;
+
+ /* user mode subset of that */
+ if (dev->idVendor == 0x0525 && dev->idProduct == 0xa4a4)
+ return 1;
+
+ /* iso version of usermode code */
+ if (dev->idVendor == 0x0525 && dev->idProduct == 0xa4a3)
+ return 1;
+
+ /* some GPL'd test firmware uses these IDs */
+
+ if (dev->idVendor == 0xfff0 && dev->idProduct == 0xfff0)
+ return 1;
+
+ /*----------------------------------------------------*/
+
+ /* iBOT2 high speed webcam */
+ if (dev->idVendor == 0x0b62 && dev->idProduct == 0x0059)
+ return 1;
+
+ return 0;
+}
+
+static int find_testdev (const char *name, const struct stat *sb, int flag)
+{
+ int fd;
+ struct usb_device_descriptor dev;
+
+ if (flag != FTW_F)
+ return 0;
+ /* ignore /proc/bus/usb/{devices,drivers} */
+ if (strrchr (name, '/')[1] == 'd')
+ return 0;
+
+ if ((fd = open (name, O_RDONLY)) < 0) {
+ perror ("can't open dev file r/o");
+ return 0;
+ }
+ if (read (fd, &dev, sizeof dev) != sizeof dev)
+ fputs ("short devfile read!\n", stderr);
+ else if (is_testdev (&dev)) {
+ struct testdev *entry;
+
+ if ((entry = calloc (1, sizeof *entry)) == 0) {
+ fputs ("no mem!\n", stderr);
+ goto done;
+ }
+ entry->name = strdup (name);
+ if (!entry->name) {
+ free (entry);
+ goto done;
+ }
+
+ // FIXME better to look at each interface and ask if it's
+ // bound to 'usbtest', rather than assume interface 0
+ entry->ifnum = 0;
+
+ // FIXME ask usbfs what speed; update USBDEVFS_CONNECTINFO
+ // so it tells about high speed etc
+
+ fprintf (stderr, "%s speed\t%s\n",
+ speed (entry->speed), entry->name);
+
+ entry->next = testdevs;
+ testdevs = entry;
+ }
+
+done:
+ close (fd);
+ return 0;
+}
+
+static int
+usbdev_ioctl (int fd, int ifno, unsigned request, void *param)
+{
+ struct usbdevfs_ioctl wrapper;
+
+ wrapper.ifno = ifno;
+ wrapper.ioctl_code = request;
+ wrapper.data = param;
+
+ return ioctl (fd, USBDEVFS_IOCTL, &wrapper);
+}
+
+static void *handle_testdev (void *arg)
+{
+ struct testdev *dev = arg;
+ int fd, i;
+ int status;
+
+ if ((fd = open (dev->name, O_RDWR)) < 0) {
+ perror ("can't open dev file r/w");
+ return 0;
+ }
+
+restart:
+ for (i = 0; i < TEST_CASES; i++) {
+ if (dev->test != -1 && dev->test != i)
+ continue;
+ dev->param.test_num = i;
+
+ status = usbdev_ioctl (fd, dev->ifnum,
+ USBTEST_REQUEST, &dev->param);
+ if (status < 0 && errno == EOPNOTSUPP)
+ continue;
+
+ /* FIXME need a "syslog it" option for background testing */
+
+ /* NOTE: each thread emits complete lines; no fragments! */
+ if (status < 0) {
+ char buf [80];
+ int err = errno;
+
+ if (strerror_r (errno, buf, sizeof buf)) {
+ snprintf (buf, sizeof buf, "error %d", err);
+ errno = err;
+ }
+ printf ("%s test %d --> %d (%s)\n",
+ dev->name, i, errno, buf);
+ } else
+ printf ("%s test %d, %4d.%.06d secs\n", dev->name, i,
+ (int) dev->param.duration.tv_sec,
+ (int) dev->param.duration.tv_usec);
+
+ fflush (stdout);
+ }
+ if (dev->forever)
+ goto restart;
+
+ close (fd);
+ return arg;
+}
+
+int main (int argc, char **argv)
+{
+ int c;
+ struct testdev *entry;
+ char *device;
+ int all = 0, forever = 0, not = 0;
+ int test = -1 /* all */;
+ struct usbtest_param param;
+
+ /* pick defaults that works with all speeds, without short packets.
+ *
+ * Best per-frame data rates:
+ * high speed, bulk 512 * 13 * 8 = 53248
+ * interrupt 1024 * 3 * 8 = 24576
+ * full speed, bulk/intr 64 * 19 = 1216
+ * interrupt 64 * 1 = 64
+ * low speed, interrupt 8 * 1 = 8
+ */
+ param.iterations = 1000;
+ param.length = 512;
+ param.vary = 512;
+ param.sglen = 32;
+
+ /* for easy use when hotplugging */
+ device = getenv ("DEVICE");
+
+ while ((c = getopt (argc, argv, "D:ac:g:hns:t:v:")) != EOF)
+ switch (c) {
+ case 'D': /* device, if only one */
+ device = optarg;
+ continue;
+ case 'a': /* use all devices */
+ device = 0;
+ all = 1;
+ continue;
+ case 'c': /* count iterations */
+ param.iterations = atoi (optarg);
+ if (param.iterations < 0)
+ goto usage;
+ continue;
+ case 'g': /* scatter/gather entries */
+ param.sglen = atoi (optarg);
+ if (param.sglen < 0)
+ goto usage;
+ continue;
+ case 'l': /* loop forever */
+ forever = 1;
+ continue;
+ case 'n': /* no test running! */
+ not = 1;
+ continue;
+ case 's': /* size of packet */
+ param.length = atoi (optarg);
+ if (param.length < 0)
+ goto usage;
+ continue;
+ case 't': /* run just one test */
+ test = atoi (optarg);
+ if (test < 0)
+ goto usage;
+ continue;
+ case 'v': /* vary packet size by ... */
+ param.vary = atoi (optarg);
+ if (param.vary < 0)
+ goto usage;
+ continue;
+ case '?':
+ case 'h':
+ default:
+usage:
+ fprintf (stderr, "usage: %s [-an] [-D dev]\n"
+ "\t[-c iterations] [-t testnum]\n"
+ "\t[-s packetsize] [-g sglen] [-v vary]\n",
+ argv [0]);
+ return 1;
+ }
+ if (optind != argc)
+ goto usage;
+ if (!all && !device) {
+ fprintf (stderr, "must specify '-a' or '-D dev', "
+ "or DEVICE=/proc/bus/usb/BBB/DDD in env\n");
+ goto usage;
+ }
+
+ if ((c = open ("/proc/bus/usb/devices", O_RDONLY)) < 0) {
+ fputs ("usbfs files are missing\n", stderr);
+ return -1;
+ }
+
+ /* collect and list the test devices */
+ if (ftw ("/proc/bus/usb", find_testdev, 3) != 0) {
+ fputs ("ftw failed; is usbfs missing?\n", stderr);
+ return -1;
+ }
+
+ /* quit, run single test, or create test threads */
+ if (!testdevs && !device) {
+ fputs ("no test devices recognized\n", stderr);
+ return -1;
+ }
+ if (not)
+ return 0;
+ if (testdevs && testdevs->next == 0 && !device)
+ device = testdevs->name;
+ for (entry = testdevs; entry; entry = entry->next) {
+ int status;
+
+ entry->param = param;
+ entry->forever = forever;
+ entry->test = test;
+
+ if (device) {
+ if (strcmp (entry->name, device))
+ continue;
+ return handle_testdev (entry) != entry;
+ }
+ status = pthread_create (&entry->thread, 0, handle_testdev, entry);
+ if (status) {
+ perror ("pthread_create");
+ continue;
+ }
+ }
+ if (device) {
+ struct testdev dev;
+
+ /* kernel can recognize test devices we don't */
+ fprintf (stderr, "%s: %s may see only control tests\n",
+ argv [0], device);
+
+ memset (&dev, 0, sizeof dev);
+ dev.name = device;
+ dev.param = param;
+ dev.forever = forever;
+ dev.test = test;
+ return handle_testdev (&dev) != &dev;
+ }
+
+ /* wait for tests to complete */
+ for (entry = testdevs; entry; entry = entry->next) {
+ void *retval;
+
+ if (pthread_join (entry->thread, &retval))
+ perror ("pthread_join");
+ /* testing errors discarded! */
+ }
+
+ return 0;
+}
--
1.7.0
On Wed, Apr 07, 2010 at 03:41:28PM +0200, Michal Nazarewicz wrote:
> The composite framework has been written using __init and __exit tags
> to mark init and exit functions as such. This works with most of the
> composite gadgets however some may need to call init/exit functions
> during normal operations. One example is mass storage gadget which
> needs to call exit functions.
>
> This patch allows gadgets to define USB_NO_INIT_SEGMENT or
> USB_NO_EXIT_SEGMENT to remove the __init and __exit declarations
> from composite framework.
Ick ick ick.
How about we just drop the __init and __exit tags completly and then
we don't have to propagate this mess any further?
How much memory do they really save anyway?
I don't like this at all...
thanks,
greg k-h
On Wed, Apr 07, 2010 at 03:41:29PM +0200, Michal Nazarewicz wrote:
> The __wake_up_locked() function has been exported in case modules need it.
What module needs it?
Why is it needed?
Why is it needed now, and was not previously?
Are you sure that you really need it?
thanks,
greg k-h
> On Wed, Apr 07, 2010 at 03:41:28PM +0200, Michal Nazarewicz wrote:
>> The composite framework has been written using __init and __exit tags
>> to mark init and exit functions as such. This works with most of the
>> composite gadgets however some may need to call init/exit functions
>> during normal operations. One example is mass storage gadget which
>> needs to call exit functions.
>>
>> This patch allows gadgets to define USB_NO_INIT_SEGMENT or
>> USB_NO_EXIT_SEGMENT to remove the __init and __exit declarations
>> from composite framework.
On Wed, 07 Apr 2010 17:28:52 +0200, Greg KH <[email protected]> wrote:
> How about we just drop the __init and __exit tags completly and then
> we don't have to propagate this mess any further?
Personally, I don't care, ;) so if you think it's better to drop it all
together I can change it in a future patch.
--
Best regards, _ _
.o. | Liege of Serenely Enlightened Majesty of o' \,=./ `o
..o | Computer Science, Michał "mina86" Nazarewicz (o o)
ooo +---[[email protected]]---[[email protected]]---ooO--(_)--Ooo--
> On Wed, Apr 07, 2010 at 03:41:29PM +0200, Michal Nazarewicz wrote:
>> The __wake_up_locked() function has been exported in case modules need it.
On Wed, 07 Apr 2010 17:29:22 +0200, Greg KH <[email protected]> wrote:
> What module needs it?
FunctionFS (f_fs) uses it (thus FunctionFS Gadget (g_ffs) uses it).
> Why is it needed?
The FunctionFS uses wait_queue_head_t's spinlock to protect a data
structure (a queue) used by FunctionFS.
In an earlier version of the code there was another spinlock for
the queue alone thus when waiting for an event the following had
to be used:
#v+
spin_lock(&queue.lock);
while (queue.empty) {
spin_unlock(&queue.lock);
wait_event(&queue.wait_queue, !queue.empty);
spin_lock(&queue.lock);
}
...
spin_unlock(&queue.lock);
#v-
I disliked this code very much and at first hoped that there's some
"wait_event_holding_lock()" macro which would define the loop shown
above (similar to user-space condition variables; see
pthread_cond_wait(3) <http://linux.die.net/man/3/pthread_cond_wait>).
What makes matter worse is that wait_event() calls prepare_to_wait()
which locks the wait_queue_head_t's spinlock so in the end we have
unlock one spinlock prior to locking another in situation where one
spinlock would suffice.
In searching for a better solution I stumbled across fs/timerfd.c which
used the wait_queue_head_t's spinlock to lock it's own structures:
* http://lxr.linux.no/#linux+v2.6.33/fs/timerfd.c#L39
* http://lxr.linux.no/#linux+v2.6.33/fs/timerfd.c#L106
So, in the end, I decided to use the same approach in FunctionFS and
hence by using wait_queue_head_t's spinlock I removed one (unneeded)
spinlock and decreased number of spin_lock and spin_unlock operations,
ie. to something like (simplified code):
#v+
spin_lock(&queue.wait_queue.lock);
my_wait_event_locked(&queue.wait_queue, !queue.empty);
...
spin_unlock(&queue.lock);
#v-
where my_wait_event_locked() is a function that does what wait_event()
does but assumes the wait_queue_head_t's lock is held when entering
and leaves it held when exiting.
> Are you sure that you really need it?
I could live without it but I strongly believe the code is somehow
cleaner and more optimised when __wake_up_locked() is used.
To be more concrete I attach the actual code (parts not important in current
discussion have been stripped). The most important parts are
__ffs_ep0_read_wait_for_events() function, the FFS_NO_SETUP case in
ffs_ep0_read() and ffs_event_add().
#v+
/****************************** Waiting ******************************/
static int __ffs_ep0_read_wait_for_events(struct ffs_data *ffs)
{
/* We are holding ffs->ev.waitq.lock */
DEFINE_WAIT(wait);
int ret = 0;
wait.flags |= WQ_FLAG_EXCLUSIVE;
__add_wait_queue_tail(&ffs->ev.waitq, &wait);
do {
set_current_state(TASK_INTERRUPTIBLE);
if (signal_pending(current)) {
ret = -ERESTARTSYS;
break;
}
spin_unlock_irq(&ffs->ev.waitq.lock);
schedule();
spin_lock_irq(&ffs->ev.waitq.lock);
} while (!ffs->ev.count);
__remove_wait_queue(&ffs->ev.waitq, &wait);
__set_current_state(TASK_RUNNING);
return ret;
}
/****************************** Popping ******************************/
static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
size_t n)
{
/* We are holding ffs->ev.waitq.lock and ffs->mutex and we need
* to release them. */
struct usb_functionfs_event events[n];
unsigned i = 0;
do {
events[i].type = ffs->ev.types[i];
/* ... */
} while (++i < n);
ffs->ev.count = 0;
spin_unlock_irq(&ffs->ev.waitq.lock);
mutex_unlock(&ffs->mutex);
return unlikely(__copy_to_user(buf, events, sizeof events))
? -EFAULT : sizeof events;
}
/****************************** Entry from user space ******************************/
static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
size_t len, loff_t *ptr)
{
struct ffs_data *ffs = file->private_data;
size_t n;
char *data;
int ret;
/* Acquire mutex */
ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
if (unlikely(ret < 0))
return ret;
spin_lock_irq(&ffs->ev.waitq.lock);
switch (FFS_SETUP_STATE(ffs)) {
case FFS_SETUP_CANCELED:
ret = -EIDRM;
break;
case FFS_NO_SETUP:
n = len / sizeof(struct usb_functionfs_event);
if (unlikely(!n)) {
ret = -EINVAL;
break;
}
if ((file->f_flags & O_NONBLOCK) && !ffs->ev.count) {
ret = -EAGAIN;
break;
}
if (!ffs->ev.count &&
unlikely(__ffs_ep0_read_wait_for_events(ffs))) {
ret = -EINTR;
break;
}
return __ffs_ep0_read_events(ffs, buf,
min(n, (size_t)ffs->ev.count));
case FFS_SETUP_PENDING:
if (ffs->ev.setup.bRequestType & USB_DIR_IN) {
ret = __ffs_ep0_stall(ffs);
break;
}
len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength));
spin_unlock_irq(&ffs->ev.waitq.lock);
data = ffs_prepare_buffer(NULL, len); /* basically kmalloc() */
if (unlikely(IS_ERR(data))) {
ret = PTR_ERR(data);
goto done_mutex;
}
spin_lock_irq(&ffs->ev.waitq.lock);
/* the state of setup could have changed from
* FFS_SETUP_PENDING to FFS_SETUP_CANCELED so we need
* to check for that. */
if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) {
kfree(data);
ret = -EIDRM;
break;
}
/* unlocks spinlock */
ret = __ffs_ep0_queue_wait(ffs, data, len);
if (likely(ret > 0) && unlikely(__copy_to_user(buf, data, len)))
ret = -EFAULT;
kfree(data);
goto done_mutex;
}
spin_unlock_irq(&ffs->ev.waitq.lock);
done_mutex:
mutex_unlock(&ffs->mutex);
return ret;
}
/****************************** Adding ******************************/
static void __ffs_event_add(struct ffs_data *ffs,
enum usb_functionfs_event_type type)
{
/* Abort any unhandled setup */
if (ffs->setup_state == FFS_SETUP_PENDING)
ffs->setup_state = FFS_SETUP_CANCELED;
/* ... some events may get nuked here */
ffs->ev.types[ffs->ev.count++] = type;
wake_up_locked(&ffs->ev.waitq);
}
static void ffs_event_add(struct ffs_data *ffs,
enum usb_functionfs_event_type type)
{
unsigned long flags;
spin_lock_irqsave(&ffs->ev.waitq.lock, flags);
__ffs_event_add(ffs, type);
spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags);
}
#v-
--
Best regards, _ _
.o. | Liege of Serenely Enlightened Majesty of o' \,=./ `o
..o | Computer Science, Michał "mina86" Nazarewicz (o o)
ooo +---[[email protected]]---[[email protected]]---ooO--(_)--Ooo--
On Wed, 07 Apr 2010 15:41:30 +0200, Michal Nazarewicz <[email protected]> wrote:
> +static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
> + size_t n)
> +{
> + /* We are holding ffs->ev.waitq.lock and ffs->mutex and we need
> + * to release them. */
> +
> + struct usb_functionfs_event events[n];
> + unsigned i = 0;
> +
> + memset(events, 0, sizeof events);
> +
> + do {
> + events[i].type = ffs->ev.types[i];
> + if (events[i].type == FUNCTIONFS_SETUP) {
> + events[i].u.setup = ffs->ev.setup;
> + ffs->setup_state = FFS_SETUP_PENDING;
> + }
> + } while (++i < n);
> +
> + ffs->ev.count = 0;
I've just noticed. This line should instead read:
+ if (n < ffs->ev.count) {
+ ffs->ev.count -= n;
+ memmove(ffs->ev.types, ffs->ev.types + n,
+ ffs->ev.count * sizeof *ffs->ev.types);
+ } else {
+ ffs->ev.count = 0;
+ }
This is a minor bug which should be rather hard to observe (and only
if user-space reads() less then 4 * sizeof(struct usb_functionfs_event)
bytes when reading from "ep0") so posted code is good for testing.
(I'll post a fixed version later on with possibly other fixes and
changes.)
> +
> + spin_unlock_irq(&ffs->ev.waitq.lock);
> + mutex_unlock(&ffs->mutex);
> +
> + return unlikely(__copy_to_user(buf, events, sizeof events))
> + ? -EFAULT : sizeof events;
> +}
--
Best regards, _ _
.o. | Liege of Serenely Enlightened Majesty of o' \,=./ `o
..o | Computer Science, Michał "mina86" Nazarewicz (o o)
ooo +---[[email protected]]---[[email protected]]---ooO--(_)--Ooo--
On Wed, Apr 07, 2010 at 07:11:05PM +0200, Michał Nazarewicz wrote:
> >On Wed, Apr 07, 2010 at 03:41:29PM +0200, Michal Nazarewicz wrote:
> >>The __wake_up_locked() function has been exported in case modules need it.
>
> On Wed, 07 Apr 2010 17:29:22 +0200, Greg KH <[email protected]> wrote:
> >What module needs it?
>
> FunctionFS (f_fs) uses it (thus FunctionFS Gadget (g_ffs) uses it).
>
> >Why is it needed?
>
> The FunctionFS uses wait_queue_head_t's spinlock to protect a data
> structure (a queue) used by FunctionFS.
>
> In an earlier version of the code there was another spinlock for
> the queue alone thus when waiting for an event the following had
> to be used:
>
> #v+
> spin_lock(&queue.lock);
> while (queue.empty) {
> spin_unlock(&queue.lock);
> wait_event(&queue.wait_queue, !queue.empty);
> spin_lock(&queue.lock);
> }
> ...
> spin_unlock(&queue.lock);
> #v-
>
> I disliked this code very much and at first hoped that there's some
> "wait_event_holding_lock()" macro which would define the loop shown
> above (similar to user-space condition variables; see
> pthread_cond_wait(3) <http://linux.die.net/man/3/pthread_cond_wait>).
>
> What makes matter worse is that wait_event() calls prepare_to_wait()
> which locks the wait_queue_head_t's spinlock so in the end we have
> unlock one spinlock prior to locking another in situation where one
> spinlock would suffice.
>
> In searching for a better solution I stumbled across fs/timerfd.c which
> used the wait_queue_head_t's spinlock to lock it's own structures:
>
> * http://lxr.linux.no/#linux+v2.6.33/fs/timerfd.c#L39
> * http://lxr.linux.no/#linux+v2.6.33/fs/timerfd.c#L106
>
> So, in the end, I decided to use the same approach in FunctionFS and
> hence by using wait_queue_head_t's spinlock I removed one (unneeded)
> spinlock and decreased number of spin_lock and spin_unlock operations,
> ie. to something like (simplified code):
>
> #v+
> spin_lock(&queue.wait_queue.lock);
> my_wait_event_locked(&queue.wait_queue, !queue.empty);
> ...
> spin_unlock(&queue.lock);
> #v-
>
> where my_wait_event_locked() is a function that does what wait_event()
> does but assumes the wait_queue_head_t's lock is held when entering
> and leaves it held when exiting.
>
> >Are you sure that you really need it?
>
> I could live without it but I strongly believe the code is somehow
> cleaner and more optimised when __wake_up_locked() is used.
Ok, thanks for the detailed description, that makes more sense.
Perhaps you should put this into the patch description next time :)
Oh, and maybe we should move this type of functionality into the core
kernel so that the two users don't have to open-code it both times? If
there are 2 users, odds are someone else will want to also do the same
thing in the near future.
thanks,
greg k-h
On Wed, Apr 07, 2010 at 05:39:35PM +0200, Michał Nazarewicz wrote:
> >On Wed, Apr 07, 2010 at 03:41:28PM +0200, Michal Nazarewicz wrote:
> >>The composite framework has been written using __init and __exit tags
> >>to mark init and exit functions as such. This works with most of the
> >>composite gadgets however some may need to call init/exit functions
> >>during normal operations. One example is mass storage gadget which
> >>needs to call exit functions.
> >>
> >>This patch allows gadgets to define USB_NO_INIT_SEGMENT or
> >>USB_NO_EXIT_SEGMENT to remove the __init and __exit declarations
> >>from composite framework.
>
> On Wed, 07 Apr 2010 17:28:52 +0200, Greg KH <[email protected]> wrote:
> >How about we just drop the __init and __exit tags completly and then
> >we don't have to propagate this mess any further?
>
> Personally, I don't care, ;) so if you think it's better to drop it all
> together I can change it in a future patch.
That would be great.
thanks,
greg k-h
On Wed, Apr 07, 2010 at 03:41:35PM +0200, Michal Nazarewicz wrote:
> The FunctionFS gadget may provide the source/sink interface
> not as the first interface (with id == 0) but some different
> interface hence a code to find the interface number is
> required.
>
> (Note that you will still configure the gadget to report
> idProduct == 0xa4a4 (an "echo 0xa4a4
> >/sys/module/g_ffs/parameters/usb_product" should suffice) or
> configure host to handle 0x0525:0xa4ac devices using the
> usbtest driver.)
>
> Signed-off-by: Michal Nazarewicz <[email protected]>
> Cc: Kyungmin Park <[email protected]>
> Cc: Marek Szyprowski <[email protected]>
Ah, kernel code with a userspace test program. If only all patches came
with stuff like this.
Very nice job, I like it a lot. Care to take the minor comments so far,
and redo it? I'll be glad to take the patches into my tree then.
thanks,
greg k-h
On Wed, Apr 07, 2010 at 03:41:34PM +0200, Michal Nazarewicz wrote:
> The testusb application can be used to test various USB gadget
> that implment the source/sink intrerface.
>
> Signed-off-by: Michal Nazarewicz <[email protected]>
> Cc: Kyungmin Park <[email protected]>
> Cc: Marek Szyprowski <[email protected]>
You should put a:
From: David Brownell <[email protected]>
as the first line of the body of this patch, so it properly shows up as
David's code.
I also would like to get an ack from David that he doesn't mind his code
moving into the kernel here (personally I think it's a good thing to be
here.)
thanks,
greg k-h
Hi,
If this is going into kernel then..
On Wed, Apr 07, 2010 at 03:41:34PM +0200, ext Michal Nazarewicz wrote:
...
> + if ((c = open ("/proc/bus/usb/devices", O_RDONLY)) < 0) {
> + fputs ("usbfs files are missing\n", stderr);
> + return -1;
> + }
Is this needed?
> +
> + /* collect and list the test devices */
> + if (ftw ("/proc/bus/usb", find_testdev, 3) != 0) {
How about "/dev/bus/usb" instead? Some distributions do not enable
sysfs in their default kernels anymore.
> + fputs ("ftw failed; is usbfs missing?\n", stderr);
> + return -1;
> + }
br,
--
heikki
On Thu, Apr 08, 2010 at 09:10:00AM +0300, Heikki Krogerus wrote:
> Hi,
>
> If this is going into kernel then..
>
> On Wed, Apr 07, 2010 at 03:41:34PM +0200, ext Michal Nazarewicz wrote:
>
> ...
>
> > + if ((c = open ("/proc/bus/usb/devices", O_RDONLY)) < 0) {
> > + fputs ("usbfs files are missing\n", stderr);
> > + return -1;
> > + }
>
> Is this needed?
>
> > +
> > + /* collect and list the test devices */
> > + if (ftw ("/proc/bus/usb", find_testdev, 3) != 0) {
>
> How about "/dev/bus/usb" instead? Some distributions do not enable
> sysfs in their default kernels anymore.
You mean 'usbfs' not 'sysfs', right?
And yes, this probably should be fixed, patches are always welcome :)
thanks,
greg k-h
On Thu, Apr 08, 2010 at 08:18:46AM +0200, ext Greg KH wrote:
> On Thu, Apr 08, 2010 at 09:10:00AM +0300, Heikki Krogerus wrote:
> > Hi,
> >
> > If this is going into kernel then..
> >
> > On Wed, Apr 07, 2010 at 03:41:34PM +0200, ext Michal Nazarewicz wrote:
> >
> > ...
> >
> > > + if ((c = open ("/proc/bus/usb/devices", O_RDONLY)) < 0) {
> > > + fputs ("usbfs files are missing\n", stderr);
> > > + return -1;
> > > + }
> >
> > Is this needed?
> >
> > > +
> > > + /* collect and list the test devices */
> > > + if (ftw ("/proc/bus/usb", find_testdev, 3) != 0) {
> >
> > How about "/dev/bus/usb" instead? Some distributions do not enable
> > sysfs in their default kernels anymore.
>
> You mean 'usbfs' not 'sysfs', right?
Yes, usbfs of cource :)
--
heikki
On Wednesday 07 April 2010, Greg KH wrote:
> You should put a:
> ????????From: David Brownell <[email protected]>
> as the first line of the body of this patch, so it properly shows up as
> David's code.
>
> I also would like to get an ack from David that he doesn't mind his code
> moving into the kernel here (personally I think it's a good thing to be
> here.)
>
> thanks,
ISTR sending an ack .... but, not with a "From:" like that. I did author the
code, but the *patch* is not from me.
Omitting all the instructions and the test plan seems like a big mistake, too...
On Wednesday 07 April 2010, Michal Nazarewicz wrote:
>
> The testusb application can be used to test various USB gadget
> that implment the source/sink intrerface.
That comment is woefully incomplete. It's not just gadgets it exercises,
and a lot of thought went into testing streaming modes too (within limitations
of a the trivial test harness).
And more significantly ... It's part of a fairly complete test suite which exercises
- all four types of USB 2.0 data transfer,
on both peripheral and host sides
- - Good coverage of observed hardware and software failure modes,
(to help ensure routine fauilts are handled well)
- Decent coverage of Linux-USB programmming interfaces ... both
in-kernel and user-visible.
- - Stress test modes
For more info, whvih *SHOULD* be referenced from wherever the
kernel tree includes any of this code:
See: http://www.linux-usb.org/usbtest/
Just throwing tools at someone without instructions can be rather
counter-productive .... they get misused, important issues ignored,
results mis-interpreteed, etc.
Note that there's a basic test plan, letting folk put
drivers (and hardware) through their paces. THe evidence is that when
drivers behave on that whole suite, Linux won't misbehave much at all.
In fact, without such tools and a test plan, it'd be hard to have mch faith
in the drivr quality ... except as a weak and scattershot "this combination of
drivers and hardware seems OK for now".
futile
At one point there were allegedly folk working on Linux testng frameworks, but
they never seem to have looked at this (even on specific request); I'm not sure
if it was just general weakness in driver testing efforts (they're not easy to test),
lack of background (/interest?) in USB, or something else. (As many of us know, it's
a sad truth that testing isn't all that glamorous, for all that it's essential).
> On Wednesday 07 April 2010, Michal Nazarewicz wrote:
>> The testusb application can be used to test various USB gadget
>> that implment the source/sink intrerface.
On Wed, 14 Apr 2010 11:41:47 +0200, David Brownell <[email protected]> wrote:
> That comment is woefully incomplete. It's not just gadgets it
> exercises, and a lot of thought went into testing streaming modes
> too (within limitations of a the trivial test harness).
>
> It's part of a fairly complete test suite which exercises:
> - all four types of USB 2.0 data transfer, on both peripheral
> and host sides,
> - good coverage of observed hardware and software failure modes,
> - decent coverage of Linux-USB programming interfaces, and
> - stress test modes
>
> For more info, which *SHOULD* be referenced from wherever the
> kernel tree includes any of this code:
>
> See: http://www.linux-usb.org/usbtest/
>
> Just throwing tools at someone without instructions can be rather
> counter-productive .... they get misused, important issues ignored,
> results mis-interpreteed, etc.
>
> Note that there's a basic test plan, letting folk put drivers
> (and hardware) through their paces. The evidence is that when
> drivers behave on that whole suite, Linux won't misbehave much
> at all.
>
> In fact, without such tools and a test plan, it'd be hard to have
> much faith in the driver quality... except as a weak and scattershot
> "this combination of drivers and hardware seems OK for now". futile
>
> At one point there were allegedly folk working on Linux testing
> frameworks, but they never seem to have looked at this (even on
> specific request); I'm not sure if it was just general weakness
> in driver testing efforts (they're not easy to test), lack of
> background (/interest?) in USB, or something else.
Let me start with explaining that my original intent was not to get the
testing code to the Linux kernel and thus I limited documentation to
minimum. I just wanted to put some code that may be used to test the
FunctionFS out into the air so that anyone who stumbles across the
patchset can test the FunctionFS code even though the test code may be
not inside the mainline kernel.
However, if Greg thinks it might be a good idea to put some testing code
into the tree, then it's fine by me. This may even lead to more testing
tools be submitted and maybe after some time an unified testing framework
may emerge.
Bottom line is, if community (represented by the maintainer ;) ) wants the
patches in the kernel, then let them have it!
Obviously, the higher the quality patches are the better, so all the constructive
criticism is welcomed. Thus I accept your comments and agree with them. What
I'd like to ask though is what you are proposing?
Should I put a short comment saying this file is part of a greater test suite
with a link to the linux-usb.org/usbtest site? Or maybe more code and/or
documentation should be included in the kernel? Or maybe the whole idea of
including this code in the kernel was not so good?
I'm happy either way so I'd like to do what's the best for Linux and community
thus am asking for your comments (and I mean "your" as in plural :) ).
> On Wednesday 07 April 2010, Greg KH wrote:
>> You should put a:
>> From: David Brownell <[email protected]>
>> as the first line of the body of this patch, so it properly shows up as
>> David's code.
On Wed, 14 Apr 2010 11:30:52 +0200, David Brownell <[email protected]> wrote:
> ISTR sending an ack .... but, not with a "From:" like that. I did author the
> code, but the *patch* is not from me.
Do you think a commit with me as an author and a clear indication in the
message that it imports your code would be better?
--
Best regards, _ _
.o. | Liege of Serenely Enlightened Majesty of o' \,=./ `o
..o | Computer Science, Michał "mina86" Nazarewicz (o o)
ooo +---[[email protected]]---[[email protected]]---ooO--(_)--Ooo--
On Wed, Apr 14, 2010 at 02:30:52AM -0700, David Brownell wrote:
> On Wednesday 07 April 2010, Greg KH wrote:
> > You should put a:
> > ????????From: David Brownell <[email protected]>
> > as the first line of the body of this patch, so it properly shows up as
> > David's code.
> >
> > I also would like to get an ack from David that he doesn't mind his code
> > moving into the kernel here (personally I think it's a good thing to be
> > here.)
> >
> > thanks,
>
> ISTR sending an ack .... but, not with a "From:" like that. I did author the
> code, but the *patch* is not from me.
The "From:" shows authorship, which is the correct statement here, we
want to preserve it. I know you didn't generate the patch, but that's
ok :)
thanks,
greg k-h
On Wed, Apr 14, 2010 at 02:30:52AM -0700, David Brownell wrote:
> On Wednesday 07 April 2010, Greg KH wrote:
> > You should put a:
> > ????????From: David Brownell <[email protected]>
> > as the first line of the body of this patch, so it properly shows up as
> > David's code.
> >
> > I also would like to get an ack from David that he doesn't mind his code
> > moving into the kernel here (personally I think it's a good thing to be
> > here.)
> >
> > thanks,
>
> ISTR sending an ack .... but, not with a "From:" like that. I did author the
> code, but the *patch* is not from me.
>
>
> Omitting all the instructions and the test plan seems like a big mistake, too...
Agreed, it should be there as well, as you decribed in the follow-on to
the patch.
thanks,
greg k-h
On Wed, Apr 14, 2010 at 02:50:41PM +0200, Micha?? Nazarewicz wrote:
> However, if Greg thinks it might be a good idea to put some testing code
> into the tree, then it's fine by me. This may even lead to more testing
> tools be submitted and maybe after some time an unified testing framework
> may emerge.
>
> Bottom line is, if community (represented by the maintainer ;) ) wants the
> patches in the kernel, then let them have it!
Yes, I would like the code in the kernel.
thanks,
greg k-h