]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
usb: gadget: Gadget directory cleanup - group usb functions
authorAndrzej Pietrasiewicz <andrzej.p@samsung.com>
Tue, 15 Jul 2014 11:09:46 +0000 (13:09 +0200)
committerFelipe Balbi <balbi@ti.com>
Wed, 16 Jul 2014 17:50:36 +0000 (12:50 -0500)
The drivers/usb/gadget directory contains many files.
Files which are related can be distributed into separate directories.
This patch moves the USB functions implementations into a separate directory.

Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
93 files changed:
drivers/usb/gadget/Makefile
drivers/usb/gadget/f_acm.c [deleted file]
drivers/usb/gadget/f_ecm.c [deleted file]
drivers/usb/gadget/f_eem.c [deleted file]
drivers/usb/gadget/f_fs.c [deleted file]
drivers/usb/gadget/f_hid.c [deleted file]
drivers/usb/gadget/f_loopback.c [deleted file]
drivers/usb/gadget/f_mass_storage.c [deleted file]
drivers/usb/gadget/f_mass_storage.h [deleted file]
drivers/usb/gadget/f_midi.c [deleted file]
drivers/usb/gadget/f_ncm.c [deleted file]
drivers/usb/gadget/f_obex.c [deleted file]
drivers/usb/gadget/f_phonet.c [deleted file]
drivers/usb/gadget/f_rndis.c [deleted file]
drivers/usb/gadget/f_serial.c [deleted file]
drivers/usb/gadget/f_sourcesink.c [deleted file]
drivers/usb/gadget/f_subset.c [deleted file]
drivers/usb/gadget/f_uac1.c [deleted file]
drivers/usb/gadget/f_uac2.c [deleted file]
drivers/usb/gadget/f_uvc.c [deleted file]
drivers/usb/gadget/f_uvc.h [deleted file]
drivers/usb/gadget/function/Makefile [new file with mode: 0644]
drivers/usb/gadget/function/f_acm.c [new file with mode: 0644]
drivers/usb/gadget/function/f_ecm.c [new file with mode: 0644]
drivers/usb/gadget/function/f_eem.c [new file with mode: 0644]
drivers/usb/gadget/function/f_fs.c [new file with mode: 0644]
drivers/usb/gadget/function/f_hid.c [new file with mode: 0644]
drivers/usb/gadget/function/f_loopback.c [new file with mode: 0644]
drivers/usb/gadget/function/f_mass_storage.c [new file with mode: 0644]
drivers/usb/gadget/function/f_mass_storage.h [new file with mode: 0644]
drivers/usb/gadget/function/f_midi.c [new file with mode: 0644]
drivers/usb/gadget/function/f_ncm.c [new file with mode: 0644]
drivers/usb/gadget/function/f_obex.c [new file with mode: 0644]
drivers/usb/gadget/function/f_phonet.c [new file with mode: 0644]
drivers/usb/gadget/function/f_rndis.c [new file with mode: 0644]
drivers/usb/gadget/function/f_serial.c [new file with mode: 0644]
drivers/usb/gadget/function/f_sourcesink.c [new file with mode: 0644]
drivers/usb/gadget/function/f_subset.c [new file with mode: 0644]
drivers/usb/gadget/function/f_uac1.c [new file with mode: 0644]
drivers/usb/gadget/function/f_uac2.c [new file with mode: 0644]
drivers/usb/gadget/function/f_uvc.c [new file with mode: 0644]
drivers/usb/gadget/function/f_uvc.h [new file with mode: 0644]
drivers/usb/gadget/function/g_zero.h [new file with mode: 0644]
drivers/usb/gadget/function/ndis.h [new file with mode: 0644]
drivers/usb/gadget/function/rndis.c [new file with mode: 0644]
drivers/usb/gadget/function/rndis.h [new file with mode: 0644]
drivers/usb/gadget/function/storage_common.c [new file with mode: 0644]
drivers/usb/gadget/function/storage_common.h [new file with mode: 0644]
drivers/usb/gadget/function/u_ecm.h [new file with mode: 0644]
drivers/usb/gadget/function/u_eem.h [new file with mode: 0644]
drivers/usb/gadget/function/u_ether.c [new file with mode: 0644]
drivers/usb/gadget/function/u_ether.h [new file with mode: 0644]
drivers/usb/gadget/function/u_ether_configfs.h [new file with mode: 0644]
drivers/usb/gadget/function/u_fs.h [new file with mode: 0644]
drivers/usb/gadget/function/u_gether.h [new file with mode: 0644]
drivers/usb/gadget/function/u_ncm.h [new file with mode: 0644]
drivers/usb/gadget/function/u_phonet.h [new file with mode: 0644]
drivers/usb/gadget/function/u_rndis.h [new file with mode: 0644]
drivers/usb/gadget/function/u_serial.c [new file with mode: 0644]
drivers/usb/gadget/function/u_serial.h [new file with mode: 0644]
drivers/usb/gadget/function/u_uac1.c [new file with mode: 0644]
drivers/usb/gadget/function/u_uac1.h [new file with mode: 0644]
drivers/usb/gadget/function/uvc.h [new file with mode: 0644]
drivers/usb/gadget/function/uvc_queue.c [new file with mode: 0644]
drivers/usb/gadget/function/uvc_queue.h [new file with mode: 0644]
drivers/usb/gadget/function/uvc_v4l2.c [new file with mode: 0644]
drivers/usb/gadget/function/uvc_video.c [new file with mode: 0644]
drivers/usb/gadget/g_zero.h [deleted file]
drivers/usb/gadget/legacy/Makefile
drivers/usb/gadget/ndis.h [deleted file]
drivers/usb/gadget/rndis.c [deleted file]
drivers/usb/gadget/rndis.h [deleted file]
drivers/usb/gadget/storage_common.c [deleted file]
drivers/usb/gadget/storage_common.h [deleted file]
drivers/usb/gadget/u_ecm.h [deleted file]
drivers/usb/gadget/u_eem.h [deleted file]
drivers/usb/gadget/u_ether.c [deleted file]
drivers/usb/gadget/u_ether.h [deleted file]
drivers/usb/gadget/u_ether_configfs.h [deleted file]
drivers/usb/gadget/u_fs.h [deleted file]
drivers/usb/gadget/u_gether.h [deleted file]
drivers/usb/gadget/u_ncm.h [deleted file]
drivers/usb/gadget/u_phonet.h [deleted file]
drivers/usb/gadget/u_rndis.h [deleted file]
drivers/usb/gadget/u_serial.c [deleted file]
drivers/usb/gadget/u_serial.h [deleted file]
drivers/usb/gadget/u_uac1.c [deleted file]
drivers/usb/gadget/u_uac1.h [deleted file]
drivers/usb/gadget/uvc.h [deleted file]
drivers/usb/gadget/uvc_queue.c [deleted file]
drivers/usb/gadget/uvc_queue.h [deleted file]
drivers/usb/gadget/uvc_v4l2.c [deleted file]
drivers/usb/gadget/uvc_video.c [deleted file]

index c144102ea793541c7e8dad1d42072bb62e71ed4f..a186afeaa7001f3f81abb4f95d9edd3099477867 100644 (file)
@@ -9,32 +9,4 @@ obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o
 libcomposite-y                 := usbstring.o config.o epautoconf.o
 libcomposite-y                 += composite.o functions.o configfs.o u_f.o
 
-# USB Functions
-usb_f_acm-y                    := f_acm.o
-obj-$(CONFIG_USB_F_ACM)                += usb_f_acm.o
-usb_f_ss_lb-y                  := f_loopback.o f_sourcesink.o
-obj-$(CONFIG_USB_F_SS_LB)      += usb_f_ss_lb.o
-obj-$(CONFIG_USB_U_SERIAL)     += u_serial.o
-usb_f_serial-y                 := f_serial.o
-obj-$(CONFIG_USB_F_SERIAL)     += usb_f_serial.o
-usb_f_obex-y                   := f_obex.o
-obj-$(CONFIG_USB_F_OBEX)       += usb_f_obex.o
-obj-$(CONFIG_USB_U_ETHER)      += u_ether.o
-usb_f_ncm-y                    := f_ncm.o
-obj-$(CONFIG_USB_F_NCM)                += usb_f_ncm.o
-usb_f_ecm-y                    := f_ecm.o
-obj-$(CONFIG_USB_F_ECM)                += usb_f_ecm.o
-usb_f_phonet-y                 := f_phonet.o
-obj-$(CONFIG_USB_F_PHONET)     += usb_f_phonet.o
-usb_f_eem-y                    := f_eem.o
-obj-$(CONFIG_USB_F_EEM)                += usb_f_eem.o
-usb_f_ecm_subset-y             := f_subset.o
-obj-$(CONFIG_USB_F_SUBSET)     += usb_f_ecm_subset.o
-usb_f_rndis-y                  := f_rndis.o rndis.o
-obj-$(CONFIG_USB_F_RNDIS)      += usb_f_rndis.o
-usb_f_mass_storage-y           := f_mass_storage.o storage_common.o
-obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o
-usb_f_fs-y                     := f_fs.o
-obj-$(CONFIG_USB_F_FS)         += usb_f_fs.o
-
-obj-$(CONFIG_USB_GADGET)       += udc/ legacy/
+obj-$(CONFIG_USB_GADGET)       += udc/ function/ legacy/
diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c
deleted file mode 100644 (file)
index ab1065a..0000000
+++ /dev/null
@@ -1,848 +0,0 @@
-/*
- * f_acm.c -- USB CDC serial (ACM) function driver
- *
- * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
- * Copyright (C) 2008 by David Brownell
- * Copyright (C) 2008 by Nokia Corporation
- * Copyright (C) 2009 by Samsung Electronics
- * Author: Michal Nazarewicz (mina86@mina86.com)
- *
- * This software is distributed under the terms of the GNU General
- * Public License ("GPL") as published by the Free Software Foundation,
- * either version 2 of that License or (at your option) any later version.
- */
-
-/* #define VERBOSE_DEBUG */
-
-#include <linux/slab.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/err.h>
-
-#include "u_serial.h"
-#include "gadget_chips.h"
-
-
-/*
- * This CDC ACM function support just wraps control functions and
- * notifications around the generic serial-over-usb code.
- *
- * Because CDC ACM is standardized by the USB-IF, many host operating
- * systems have drivers for it.  Accordingly, ACM is the preferred
- * interop solution for serial-port type connections.  The control
- * models are often not necessary, and in any case don't do much in
- * this bare-bones implementation.
- *
- * Note that even MS-Windows has some support for ACM.  However, that
- * support is somewhat broken because when you use ACM in a composite
- * device, having multiple interfaces confuses the poor OS.  It doesn't
- * seem to understand CDC Union descriptors.  The new "association"
- * descriptors (roughly equivalent to CDC Unions) may sometimes help.
- */
-
-struct f_acm {
-       struct gserial                  port;
-       u8                              ctrl_id, data_id;
-       u8                              port_num;
-
-       u8                              pending;
-
-       /* lock is mostly for pending and notify_req ... they get accessed
-        * by callbacks both from tty (open/close/break) under its spinlock,
-        * and notify_req.complete() which can't use that lock.
-        */
-       spinlock_t                      lock;
-
-       struct usb_ep                   *notify;
-       struct usb_request              *notify_req;
-
-       struct usb_cdc_line_coding      port_line_coding;       /* 8-N-1 etc */
-
-       /* SetControlLineState request -- CDC 1.1 section 6.2.14 (INPUT) */
-       u16                             port_handshake_bits;
-#define ACM_CTRL_RTS   (1 << 1)        /* unused with full duplex */
-#define ACM_CTRL_DTR   (1 << 0)        /* host is ready for data r/w */
-
-       /* SerialState notification -- CDC 1.1 section 6.3.5 (OUTPUT) */
-       u16                             serial_state;
-#define ACM_CTRL_OVERRUN       (1 << 6)
-#define ACM_CTRL_PARITY                (1 << 5)
-#define ACM_CTRL_FRAMING       (1 << 4)
-#define ACM_CTRL_RI            (1 << 3)
-#define ACM_CTRL_BRK           (1 << 2)
-#define ACM_CTRL_DSR           (1 << 1)
-#define ACM_CTRL_DCD           (1 << 0)
-};
-
-static inline struct f_acm *func_to_acm(struct usb_function *f)
-{
-       return container_of(f, struct f_acm, port.func);
-}
-
-static inline struct f_acm *port_to_acm(struct gserial *p)
-{
-       return container_of(p, struct f_acm, port);
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* notification endpoint uses smallish and infrequent fixed-size messages */
-
-#define GS_NOTIFY_INTERVAL_MS          32
-#define GS_NOTIFY_MAXPACKET            10      /* notification + 2 bytes */
-
-/* interface and class descriptors: */
-
-static struct usb_interface_assoc_descriptor
-acm_iad_descriptor = {
-       .bLength =              sizeof acm_iad_descriptor,
-       .bDescriptorType =      USB_DT_INTERFACE_ASSOCIATION,
-
-       /* .bFirstInterface =   DYNAMIC, */
-       .bInterfaceCount =      2,      // control + data
-       .bFunctionClass =       USB_CLASS_COMM,
-       .bFunctionSubClass =    USB_CDC_SUBCLASS_ACM,
-       .bFunctionProtocol =    USB_CDC_ACM_PROTO_AT_V25TER,
-       /* .iFunction =         DYNAMIC */
-};
-
-
-static struct usb_interface_descriptor acm_control_interface_desc = {
-       .bLength =              USB_DT_INTERFACE_SIZE,
-       .bDescriptorType =      USB_DT_INTERFACE,
-       /* .bInterfaceNumber = DYNAMIC */
-       .bNumEndpoints =        1,
-       .bInterfaceClass =      USB_CLASS_COMM,
-       .bInterfaceSubClass =   USB_CDC_SUBCLASS_ACM,
-       .bInterfaceProtocol =   USB_CDC_ACM_PROTO_AT_V25TER,
-       /* .iInterface = DYNAMIC */
-};
-
-static struct usb_interface_descriptor acm_data_interface_desc = {
-       .bLength =              USB_DT_INTERFACE_SIZE,
-       .bDescriptorType =      USB_DT_INTERFACE,
-       /* .bInterfaceNumber = DYNAMIC */
-       .bNumEndpoints =        2,
-       .bInterfaceClass =      USB_CLASS_CDC_DATA,
-       .bInterfaceSubClass =   0,
-       .bInterfaceProtocol =   0,
-       /* .iInterface = DYNAMIC */
-};
-
-static struct usb_cdc_header_desc acm_header_desc = {
-       .bLength =              sizeof(acm_header_desc),
-       .bDescriptorType =      USB_DT_CS_INTERFACE,
-       .bDescriptorSubType =   USB_CDC_HEADER_TYPE,
-       .bcdCDC =               cpu_to_le16(0x0110),
-};
-
-static struct usb_cdc_call_mgmt_descriptor
-acm_call_mgmt_descriptor = {
-       .bLength =              sizeof(acm_call_mgmt_descriptor),
-       .bDescriptorType =      USB_DT_CS_INTERFACE,
-       .bDescriptorSubType =   USB_CDC_CALL_MANAGEMENT_TYPE,
-       .bmCapabilities =       0,
-       /* .bDataInterface = DYNAMIC */
-};
-
-static struct usb_cdc_acm_descriptor acm_descriptor = {
-       .bLength =              sizeof(acm_descriptor),
-       .bDescriptorType =      USB_DT_CS_INTERFACE,
-       .bDescriptorSubType =   USB_CDC_ACM_TYPE,
-       .bmCapabilities =       USB_CDC_CAP_LINE,
-};
-
-static struct usb_cdc_union_desc acm_union_desc = {
-       .bLength =              sizeof(acm_union_desc),
-       .bDescriptorType =      USB_DT_CS_INTERFACE,
-       .bDescriptorSubType =   USB_CDC_UNION_TYPE,
-       /* .bMasterInterface0 = DYNAMIC */
-       /* .bSlaveInterface0 =  DYNAMIC */
-};
-
-/* full speed support: */
-
-static struct usb_endpoint_descriptor acm_fs_notify_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_INT,
-       .wMaxPacketSize =       cpu_to_le16(GS_NOTIFY_MAXPACKET),
-       .bInterval =            GS_NOTIFY_INTERVAL_MS,
-};
-
-static struct usb_endpoint_descriptor acm_fs_in_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-};
-
-static struct usb_endpoint_descriptor acm_fs_out_desc = {
-       .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[] = {
-       (struct usb_descriptor_header *) &acm_iad_descriptor,
-       (struct usb_descriptor_header *) &acm_control_interface_desc,
-       (struct usb_descriptor_header *) &acm_header_desc,
-       (struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
-       (struct usb_descriptor_header *) &acm_descriptor,
-       (struct usb_descriptor_header *) &acm_union_desc,
-       (struct usb_descriptor_header *) &acm_fs_notify_desc,
-       (struct usb_descriptor_header *) &acm_data_interface_desc,
-       (struct usb_descriptor_header *) &acm_fs_in_desc,
-       (struct usb_descriptor_header *) &acm_fs_out_desc,
-       NULL,
-};
-
-/* high speed support: */
-static struct usb_endpoint_descriptor acm_hs_notify_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_INT,
-       .wMaxPacketSize =       cpu_to_le16(GS_NOTIFY_MAXPACKET),
-       .bInterval =            USB_MS_TO_HS_INTERVAL(GS_NOTIFY_INTERVAL_MS),
-};
-
-static struct usb_endpoint_descriptor acm_hs_in_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(512),
-};
-
-static struct usb_endpoint_descriptor acm_hs_out_desc = {
-       .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[] = {
-       (struct usb_descriptor_header *) &acm_iad_descriptor,
-       (struct usb_descriptor_header *) &acm_control_interface_desc,
-       (struct usb_descriptor_header *) &acm_header_desc,
-       (struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
-       (struct usb_descriptor_header *) &acm_descriptor,
-       (struct usb_descriptor_header *) &acm_union_desc,
-       (struct usb_descriptor_header *) &acm_hs_notify_desc,
-       (struct usb_descriptor_header *) &acm_data_interface_desc,
-       (struct usb_descriptor_header *) &acm_hs_in_desc,
-       (struct usb_descriptor_header *) &acm_hs_out_desc,
-       NULL,
-};
-
-static struct usb_endpoint_descriptor acm_ss_in_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(1024),
-};
-
-static struct usb_endpoint_descriptor acm_ss_out_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(1024),
-};
-
-static struct usb_ss_ep_comp_descriptor acm_ss_bulk_comp_desc = {
-       .bLength =              sizeof acm_ss_bulk_comp_desc,
-       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
-};
-
-static struct usb_descriptor_header *acm_ss_function[] = {
-       (struct usb_descriptor_header *) &acm_iad_descriptor,
-       (struct usb_descriptor_header *) &acm_control_interface_desc,
-       (struct usb_descriptor_header *) &acm_header_desc,
-       (struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
-       (struct usb_descriptor_header *) &acm_descriptor,
-       (struct usb_descriptor_header *) &acm_union_desc,
-       (struct usb_descriptor_header *) &acm_hs_notify_desc,
-       (struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
-       (struct usb_descriptor_header *) &acm_data_interface_desc,
-       (struct usb_descriptor_header *) &acm_ss_in_desc,
-       (struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
-       (struct usb_descriptor_header *) &acm_ss_out_desc,
-       (struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
-       NULL,
-};
-
-/* string descriptors: */
-
-#define ACM_CTRL_IDX   0
-#define ACM_DATA_IDX   1
-#define ACM_IAD_IDX    2
-
-/* static strings, in UTF-8 */
-static struct usb_string acm_string_defs[] = {
-       [ACM_CTRL_IDX].s = "CDC Abstract Control Model (ACM)",
-       [ACM_DATA_IDX].s = "CDC ACM Data",
-       [ACM_IAD_IDX ].s = "CDC Serial",
-       {  } /* end of list */
-};
-
-static struct usb_gadget_strings acm_string_table = {
-       .language =             0x0409, /* en-us */
-       .strings =              acm_string_defs,
-};
-
-static struct usb_gadget_strings *acm_strings[] = {
-       &acm_string_table,
-       NULL,
-};
-
-/*-------------------------------------------------------------------------*/
-
-/* ACM control ... data handling is delegated to tty library code.
- * The main task of this function is to activate and deactivate
- * that code based on device state; track parameters like line
- * speed, handshake state, and so on; and issue notifications.
- */
-
-static void acm_complete_set_line_coding(struct usb_ep *ep,
-               struct usb_request *req)
-{
-       struct f_acm    *acm = ep->driver_data;
-       struct usb_composite_dev *cdev = acm->port.func.config->cdev;
-
-       if (req->status != 0) {
-               DBG(cdev, "acm ttyGS%d completion, err %d\n",
-                               acm->port_num, req->status);
-               return;
-       }
-
-       /* normal completion */
-       if (req->actual != sizeof(acm->port_line_coding)) {
-               DBG(cdev, "acm ttyGS%d short resp, len %d\n",
-                               acm->port_num, req->actual);
-               usb_ep_set_halt(ep);
-       } else {
-               struct usb_cdc_line_coding      *value = req->buf;
-
-               /* REVISIT:  we currently just remember this data.
-                * If we change that, (a) validate it first, then
-                * (b) update whatever hardware needs updating,
-                * (c) worry about locking.  This is information on
-                * the order of 9600-8-N-1 ... most of which means
-                * nothing unless we control a real RS232 line.
-                */
-               acm->port_line_coding = *value;
-       }
-}
-
-static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
-{
-       struct f_acm            *acm = func_to_acm(f);
-       struct usb_composite_dev *cdev = f->config->cdev;
-       struct usb_request      *req = cdev->req;
-       int                     value = -EOPNOTSUPP;
-       u16                     w_index = le16_to_cpu(ctrl->wIndex);
-       u16                     w_value = le16_to_cpu(ctrl->wValue);
-       u16                     w_length = le16_to_cpu(ctrl->wLength);
-
-       /* composite driver infrastructure handles everything except
-        * CDC class messages; interface activation uses set_alt().
-        *
-        * Note CDC spec table 4 lists the ACM request profile.  It requires
-        * encapsulated command support ... we don't handle any, and respond
-        * to them by stalling.  Options include get/set/clear comm features
-        * (not that useful) and SEND_BREAK.
-        */
-       switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
-
-       /* SET_LINE_CODING ... just read and save what the host sends */
-       case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
-                       | USB_CDC_REQ_SET_LINE_CODING:
-               if (w_length != sizeof(struct usb_cdc_line_coding)
-                               || w_index != acm->ctrl_id)
-                       goto invalid;
-
-               value = w_length;
-               cdev->gadget->ep0->driver_data = acm;
-               req->complete = acm_complete_set_line_coding;
-               break;
-
-       /* GET_LINE_CODING ... return what host sent, or initial value */
-       case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
-                       | USB_CDC_REQ_GET_LINE_CODING:
-               if (w_index != acm->ctrl_id)
-                       goto invalid;
-
-               value = min_t(unsigned, w_length,
-                               sizeof(struct usb_cdc_line_coding));
-               memcpy(req->buf, &acm->port_line_coding, value);
-               break;
-
-       /* SET_CONTROL_LINE_STATE ... save what the host sent */
-       case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
-                       | USB_CDC_REQ_SET_CONTROL_LINE_STATE:
-               if (w_index != acm->ctrl_id)
-                       goto invalid;
-
-               value = 0;
-
-               /* FIXME we should not allow data to flow until the
-                * host sets the ACM_CTRL_DTR bit; and when it clears
-                * that bit, we should return to that no-flow state.
-                */
-               acm->port_handshake_bits = w_value;
-               break;
-
-       default:
-invalid:
-               VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
-                       ctrl->bRequestType, ctrl->bRequest,
-                       w_value, w_index, w_length);
-       }
-
-       /* respond with data transfer or status phase? */
-       if (value >= 0) {
-               DBG(cdev, "acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n",
-                       acm->port_num, ctrl->bRequestType, ctrl->bRequest,
-                       w_value, w_index, w_length);
-               req->zero = 0;
-               req->length = value;
-               value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
-               if (value < 0)
-                       ERROR(cdev, "acm response on ttyGS%d, err %d\n",
-                                       acm->port_num, value);
-       }
-
-       /* device either stalls (value < 0) or reports success */
-       return value;
-}
-
-static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
-{
-       struct f_acm            *acm = func_to_acm(f);
-       struct usb_composite_dev *cdev = f->config->cdev;
-
-       /* we know alt == 0, so this is an activation or a reset */
-
-       if (intf == acm->ctrl_id) {
-               if (acm->notify->driver_data) {
-                       VDBG(cdev, "reset acm control interface %d\n", intf);
-                       usb_ep_disable(acm->notify);
-               } else {
-                       VDBG(cdev, "init acm ctrl interface %d\n", intf);
-                       if (config_ep_by_speed(cdev->gadget, f, acm->notify))
-                               return -EINVAL;
-               }
-               usb_ep_enable(acm->notify);
-               acm->notify->driver_data = acm;
-
-       } else if (intf == acm->data_id) {
-               if (acm->port.in->driver_data) {
-                       DBG(cdev, "reset acm ttyGS%d\n", acm->port_num);
-                       gserial_disconnect(&acm->port);
-               }
-               if (!acm->port.in->desc || !acm->port.out->desc) {
-                       DBG(cdev, "activate acm ttyGS%d\n", acm->port_num);
-                       if (config_ep_by_speed(cdev->gadget, f,
-                                              acm->port.in) ||
-                           config_ep_by_speed(cdev->gadget, f,
-                                              acm->port.out)) {
-                               acm->port.in->desc = NULL;
-                               acm->port.out->desc = NULL;
-                               return -EINVAL;
-                       }
-               }
-               gserial_connect(&acm->port, acm->port_num);
-
-       } else
-               return -EINVAL;
-
-       return 0;
-}
-
-static void acm_disable(struct usb_function *f)
-{
-       struct f_acm    *acm = func_to_acm(f);
-       struct usb_composite_dev *cdev = f->config->cdev;
-
-       DBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num);
-       gserial_disconnect(&acm->port);
-       usb_ep_disable(acm->notify);
-       acm->notify->driver_data = NULL;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/**
- * acm_cdc_notify - issue CDC notification to host
- * @acm: wraps host to be notified
- * @type: notification type
- * @value: Refer to cdc specs, wValue field.
- * @data: data to be sent
- * @length: size of data
- * Context: irqs blocked, acm->lock held, acm_notify_req non-null
- *
- * Returns zero on success or a negative errno.
- *
- * See section 6.3.5 of the CDC 1.1 specification for information
- * about the only notification we issue:  SerialState change.
- */
-static int acm_cdc_notify(struct f_acm *acm, u8 type, u16 value,
-               void *data, unsigned length)
-{
-       struct usb_ep                   *ep = acm->notify;
-       struct usb_request              *req;
-       struct usb_cdc_notification     *notify;
-       const unsigned                  len = sizeof(*notify) + length;
-       void                            *buf;
-       int                             status;
-
-       req = acm->notify_req;
-       acm->notify_req = NULL;
-       acm->pending = false;
-
-       req->length = len;
-       notify = req->buf;
-       buf = notify + 1;
-
-       notify->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
-                       | USB_RECIP_INTERFACE;
-       notify->bNotificationType = type;
-       notify->wValue = cpu_to_le16(value);
-       notify->wIndex = cpu_to_le16(acm->ctrl_id);
-       notify->wLength = cpu_to_le16(length);
-       memcpy(buf, data, length);
-
-       /* ep_queue() can complete immediately if it fills the fifo... */
-       spin_unlock(&acm->lock);
-       status = usb_ep_queue(ep, req, GFP_ATOMIC);
-       spin_lock(&acm->lock);
-
-       if (status < 0) {
-               ERROR(acm->port.func.config->cdev,
-                               "acm ttyGS%d can't notify serial state, %d\n",
-                               acm->port_num, status);
-               acm->notify_req = req;
-       }
-
-       return status;
-}
-
-static int acm_notify_serial_state(struct f_acm *acm)
-{
-       struct usb_composite_dev *cdev = acm->port.func.config->cdev;
-       int                     status;
-
-       spin_lock(&acm->lock);
-       if (acm->notify_req) {
-               DBG(cdev, "acm ttyGS%d serial state %04x\n",
-                               acm->port_num, acm->serial_state);
-               status = acm_cdc_notify(acm, USB_CDC_NOTIFY_SERIAL_STATE,
-                               0, &acm->serial_state, sizeof(acm->serial_state));
-       } else {
-               acm->pending = true;
-               status = 0;
-       }
-       spin_unlock(&acm->lock);
-       return status;
-}
-
-static void acm_cdc_notify_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       struct f_acm            *acm = req->context;
-       u8                      doit = false;
-
-       /* on this call path we do NOT hold the port spinlock,
-        * which is why ACM needs its own spinlock
-        */
-       spin_lock(&acm->lock);
-       if (req->status != -ESHUTDOWN)
-               doit = acm->pending;
-       acm->notify_req = req;
-       spin_unlock(&acm->lock);
-
-       if (doit)
-               acm_notify_serial_state(acm);
-}
-
-/* connect == the TTY link is open */
-
-static void acm_connect(struct gserial *port)
-{
-       struct f_acm            *acm = port_to_acm(port);
-
-       acm->serial_state |= ACM_CTRL_DSR | ACM_CTRL_DCD;
-       acm_notify_serial_state(acm);
-}
-
-static void acm_disconnect(struct gserial *port)
-{
-       struct f_acm            *acm = port_to_acm(port);
-
-       acm->serial_state &= ~(ACM_CTRL_DSR | ACM_CTRL_DCD);
-       acm_notify_serial_state(acm);
-}
-
-static int acm_send_break(struct gserial *port, int duration)
-{
-       struct f_acm            *acm = port_to_acm(port);
-       u16                     state;
-
-       state = acm->serial_state;
-       state &= ~ACM_CTRL_BRK;
-       if (duration)
-               state |= ACM_CTRL_BRK;
-
-       acm->serial_state = state;
-       return acm_notify_serial_state(acm);
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* ACM function driver setup/binding */
-static int
-acm_bind(struct usb_configuration *c, struct usb_function *f)
-{
-       struct usb_composite_dev *cdev = c->cdev;
-       struct f_acm            *acm = func_to_acm(f);
-       struct usb_string       *us;
-       int                     status;
-       struct usb_ep           *ep;
-
-       /* REVISIT might want instance-specific strings to help
-        * distinguish instances ...
-        */
-
-       /* maybe allocate device-global string IDs, and patch descriptors */
-       us = usb_gstrings_attach(cdev, acm_strings,
-                       ARRAY_SIZE(acm_string_defs));
-       if (IS_ERR(us))
-               return PTR_ERR(us);
-       acm_control_interface_desc.iInterface = us[ACM_CTRL_IDX].id;
-       acm_data_interface_desc.iInterface = us[ACM_DATA_IDX].id;
-       acm_iad_descriptor.iFunction = us[ACM_IAD_IDX].id;
-
-       /* allocate instance-specific interface IDs, and patch descriptors */
-       status = usb_interface_id(c, f);
-       if (status < 0)
-               goto fail;
-       acm->ctrl_id = status;
-       acm_iad_descriptor.bFirstInterface = status;
-
-       acm_control_interface_desc.bInterfaceNumber = status;
-       acm_union_desc .bMasterInterface0 = status;
-
-       status = usb_interface_id(c, f);
-       if (status < 0)
-               goto fail;
-       acm->data_id = status;
-
-       acm_data_interface_desc.bInterfaceNumber = status;
-       acm_union_desc.bSlaveInterface0 = status;
-       acm_call_mgmt_descriptor.bDataInterface = status;
-
-       status = -ENODEV;
-
-       /* allocate instance-specific endpoints */
-       ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_in_desc);
-       if (!ep)
-               goto fail;
-       acm->port.in = ep;
-       ep->driver_data = cdev; /* claim */
-
-       ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc);
-       if (!ep)
-               goto fail;
-       acm->port.out = ep;
-       ep->driver_data = cdev; /* claim */
-
-       ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc);
-       if (!ep)
-               goto fail;
-       acm->notify = ep;
-       ep->driver_data = cdev; /* claim */
-
-       /* allocate notification */
-       acm->notify_req = gs_alloc_req(ep,
-                       sizeof(struct usb_cdc_notification) + 2,
-                       GFP_KERNEL);
-       if (!acm->notify_req)
-               goto fail;
-
-       acm->notify_req->complete = acm_cdc_notify_complete;
-       acm->notify_req->context = acm;
-
-       /* support all relevant hardware speeds... we expect that when
-        * hardware is dual speed, all bulk-capable endpoints work at
-        * both speeds
-        */
-       acm_hs_in_desc.bEndpointAddress = acm_fs_in_desc.bEndpointAddress;
-       acm_hs_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress;
-       acm_hs_notify_desc.bEndpointAddress =
-               acm_fs_notify_desc.bEndpointAddress;
-
-       acm_ss_in_desc.bEndpointAddress = acm_fs_in_desc.bEndpointAddress;
-       acm_ss_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress;
-
-       status = usb_assign_descriptors(f, acm_fs_function, acm_hs_function,
-                       acm_ss_function);
-       if (status)
-               goto fail;
-
-       DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n",
-                       acm->port_num,
-                       gadget_is_superspeed(c->cdev->gadget) ? "super" :
-                       gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
-                       acm->port.in->name, acm->port.out->name,
-                       acm->notify->name);
-       return 0;
-
-fail:
-       if (acm->notify_req)
-               gs_free_req(acm->notify, acm->notify_req);
-
-       /* we might as well release our claims on endpoints */
-       if (acm->notify)
-               acm->notify->driver_data = NULL;
-       if (acm->port.out)
-               acm->port.out->driver_data = NULL;
-       if (acm->port.in)
-               acm->port.in->driver_data = NULL;
-
-       ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status);
-
-       return status;
-}
-
-static void acm_unbind(struct usb_configuration *c, struct usb_function *f)
-{
-       struct f_acm            *acm = func_to_acm(f);
-
-       acm_string_defs[0].id = 0;
-       usb_free_all_descriptors(f);
-       if (acm->notify_req)
-               gs_free_req(acm->notify, acm->notify_req);
-}
-
-static void acm_free_func(struct usb_function *f)
-{
-       struct f_acm            *acm = func_to_acm(f);
-
-       kfree(acm);
-}
-
-static struct usb_function *acm_alloc_func(struct usb_function_instance *fi)
-{
-       struct f_serial_opts *opts;
-       struct f_acm *acm;
-
-       acm = kzalloc(sizeof(*acm), GFP_KERNEL);
-       if (!acm)
-               return ERR_PTR(-ENOMEM);
-
-       spin_lock_init(&acm->lock);
-
-       acm->port.connect = acm_connect;
-       acm->port.disconnect = acm_disconnect;
-       acm->port.send_break = acm_send_break;
-
-       acm->port.func.name = "acm";
-       acm->port.func.strings = acm_strings;
-       /* descriptors are per-instance copies */
-       acm->port.func.bind = acm_bind;
-       acm->port.func.set_alt = acm_set_alt;
-       acm->port.func.setup = acm_setup;
-       acm->port.func.disable = acm_disable;
-
-       opts = container_of(fi, struct f_serial_opts, func_inst);
-       acm->port_num = opts->port_num;
-       acm->port.func.unbind = acm_unbind;
-       acm->port.func.free_func = acm_free_func;
-
-       return &acm->port.func;
-}
-
-static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item)
-{
-       return container_of(to_config_group(item), struct f_serial_opts,
-                       func_inst.group);
-}
-
-CONFIGFS_ATTR_STRUCT(f_serial_opts);
-static ssize_t f_acm_attr_show(struct config_item *item,
-                                struct configfs_attribute *attr,
-                                char *page)
-{
-       struct f_serial_opts *opts = to_f_serial_opts(item);
-       struct f_serial_opts_attribute *f_serial_opts_attr =
-               container_of(attr, struct f_serial_opts_attribute, attr);
-       ssize_t ret = 0;
-
-       if (f_serial_opts_attr->show)
-               ret = f_serial_opts_attr->show(opts, page);
-       return ret;
-}
-
-static void acm_attr_release(struct config_item *item)
-{
-       struct f_serial_opts *opts = to_f_serial_opts(item);
-
-       usb_put_function_instance(&opts->func_inst);
-}
-
-static struct configfs_item_operations acm_item_ops = {
-       .release                = acm_attr_release,
-       .show_attribute         = f_acm_attr_show,
-};
-
-static ssize_t f_acm_port_num_show(struct f_serial_opts *opts, char *page)
-{
-       return sprintf(page, "%u\n", opts->port_num);
-}
-
-static struct f_serial_opts_attribute f_acm_port_num =
-       __CONFIGFS_ATTR_RO(port_num, f_acm_port_num_show);
-
-
-static struct configfs_attribute *acm_attrs[] = {
-       &f_acm_port_num.attr,
-       NULL,
-};
-
-static struct config_item_type acm_func_type = {
-       .ct_item_ops    = &acm_item_ops,
-       .ct_attrs       = acm_attrs,
-       .ct_owner       = THIS_MODULE,
-};
-
-static void acm_free_instance(struct usb_function_instance *fi)
-{
-       struct f_serial_opts *opts;
-
-       opts = container_of(fi, struct f_serial_opts, func_inst);
-       gserial_free_line(opts->port_num);
-       kfree(opts);
-}
-
-static struct usb_function_instance *acm_alloc_instance(void)
-{
-       struct f_serial_opts *opts;
-       int ret;
-
-       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
-       if (!opts)
-               return ERR_PTR(-ENOMEM);
-       opts->func_inst.free_func_inst = acm_free_instance;
-       ret = gserial_alloc_line(&opts->port_num);
-       if (ret) {
-               kfree(opts);
-               return ERR_PTR(ret);
-       }
-       config_group_init_type_name(&opts->func_inst.group, "",
-                       &acm_func_type);
-       return &opts->func_inst;
-}
-DECLARE_USB_FUNCTION_INIT(acm, acm_alloc_instance, acm_alloc_func);
-MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c
deleted file mode 100644 (file)
index 798760f..0000000
+++ /dev/null
@@ -1,973 +0,0 @@
-/*
- * f_ecm.c -- USB CDC Ethernet (ECM) link function driver
- *
- * Copyright (C) 2003-2005,2008 David Brownell
- * Copyright (C) 2008 Nokia Corporation
- *
- * 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.
- */
-
-/* #define VERBOSE_DEBUG */
-
-#include <linux/slab.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/etherdevice.h>
-
-#include "u_ether.h"
-#include "u_ether_configfs.h"
-#include "u_ecm.h"
-
-
-/*
- * This function is a "CDC Ethernet Networking Control Model" (CDC ECM)
- * Ethernet link.  The data transfer model is simple (packets sent and
- * received over bulk endpoints using normal short packet termination),
- * and the control model exposes various data and optional notifications.
- *
- * ECM is well standardized and (except for Microsoft) supported by most
- * operating systems with USB host support.  It's the preferred interop
- * solution for Ethernet over USB, at least for firmware based solutions.
- * (Hardware solutions tend to be more minimalist.)  A newer and simpler
- * "Ethernet Emulation Model" (CDC EEM) hasn't yet caught on.
- *
- * Note that ECM requires the use of "alternate settings" for its data
- * interface.  This means that the set_alt() method has real work to do,
- * and also means that a get_alt() method is required.
- */
-
-
-enum ecm_notify_state {
-       ECM_NOTIFY_NONE,                /* don't notify */
-       ECM_NOTIFY_CONNECT,             /* issue CONNECT next */
-       ECM_NOTIFY_SPEED,               /* issue SPEED_CHANGE next */
-};
-
-struct f_ecm {
-       struct gether                   port;
-       u8                              ctrl_id, data_id;
-
-       char                            ethaddr[14];
-
-       struct usb_ep                   *notify;
-       struct usb_request              *notify_req;
-       u8                              notify_state;
-       bool                            is_open;
-
-       /* FIXME is_open needs some irq-ish locking
-        * ... possibly the same as port.ioport
-        */
-};
-
-static inline struct f_ecm *func_to_ecm(struct usb_function *f)
-{
-       return container_of(f, struct f_ecm, port.func);
-}
-
-/* peak (theoretical) bulk transfer rate in bits-per-second */
-static inline unsigned ecm_bitrate(struct usb_gadget *g)
-{
-       if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
-               return 13 * 1024 * 8 * 1000 * 8;
-       else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
-               return 13 * 512 * 8 * 1000 * 8;
-       else
-               return 19 * 64 * 1 * 1000 * 8;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * Include the status endpoint if we can, even though it's optional.
- *
- * Use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one
- * packet, to simplify cancellation; and a big transfer interval, to
- * waste less bandwidth.
- *
- * Some drivers (like Linux 2.4 cdc-ether!) "need" it to exist even
- * if they ignore the connect/disconnect notifications that real aether
- * can provide.  More advanced cdc configurations might want to support
- * encapsulated commands (vendor-specific, using control-OUT).
- */
-
-#define ECM_STATUS_INTERVAL_MS         32
-#define ECM_STATUS_BYTECOUNT           16      /* 8 byte header + data */
-
-
-/* interface descriptor: */
-
-static struct usb_interface_assoc_descriptor
-ecm_iad_descriptor = {
-       .bLength =              sizeof ecm_iad_descriptor,
-       .bDescriptorType =      USB_DT_INTERFACE_ASSOCIATION,
-
-       /* .bFirstInterface =   DYNAMIC, */
-       .bInterfaceCount =      2,      /* control + data */
-       .bFunctionClass =       USB_CLASS_COMM,
-       .bFunctionSubClass =    USB_CDC_SUBCLASS_ETHERNET,
-       .bFunctionProtocol =    USB_CDC_PROTO_NONE,
-       /* .iFunction =         DYNAMIC */
-};
-
-
-static struct usb_interface_descriptor ecm_control_intf = {
-       .bLength =              sizeof ecm_control_intf,
-       .bDescriptorType =      USB_DT_INTERFACE,
-
-       /* .bInterfaceNumber = DYNAMIC */
-       /* status endpoint is optional; this could be patched later */
-       .bNumEndpoints =        1,
-       .bInterfaceClass =      USB_CLASS_COMM,
-       .bInterfaceSubClass =   USB_CDC_SUBCLASS_ETHERNET,
-       .bInterfaceProtocol =   USB_CDC_PROTO_NONE,
-       /* .iInterface = DYNAMIC */
-};
-
-static struct usb_cdc_header_desc ecm_header_desc = {
-       .bLength =              sizeof ecm_header_desc,
-       .bDescriptorType =      USB_DT_CS_INTERFACE,
-       .bDescriptorSubType =   USB_CDC_HEADER_TYPE,
-
-       .bcdCDC =               cpu_to_le16(0x0110),
-};
-
-static struct usb_cdc_union_desc ecm_union_desc = {
-       .bLength =              sizeof(ecm_union_desc),
-       .bDescriptorType =      USB_DT_CS_INTERFACE,
-       .bDescriptorSubType =   USB_CDC_UNION_TYPE,
-       /* .bMasterInterface0 = DYNAMIC */
-       /* .bSlaveInterface0 =  DYNAMIC */
-};
-
-static struct usb_cdc_ether_desc ecm_desc = {
-       .bLength =              sizeof ecm_desc,
-       .bDescriptorType =      USB_DT_CS_INTERFACE,
-       .bDescriptorSubType =   USB_CDC_ETHERNET_TYPE,
-
-       /* this descriptor actually adds value, surprise! */
-       /* .iMACAddress = DYNAMIC */
-       .bmEthernetStatistics = cpu_to_le32(0), /* no statistics */
-       .wMaxSegmentSize =      cpu_to_le16(ETH_FRAME_LEN),
-       .wNumberMCFilters =     cpu_to_le16(0),
-       .bNumberPowerFilters =  0,
-};
-
-/* the default data interface has no endpoints ... */
-
-static struct usb_interface_descriptor ecm_data_nop_intf = {
-       .bLength =              sizeof ecm_data_nop_intf,
-       .bDescriptorType =      USB_DT_INTERFACE,
-
-       .bInterfaceNumber =     1,
-       .bAlternateSetting =    0,
-       .bNumEndpoints =        0,
-       .bInterfaceClass =      USB_CLASS_CDC_DATA,
-       .bInterfaceSubClass =   0,
-       .bInterfaceProtocol =   0,
-       /* .iInterface = DYNAMIC */
-};
-
-/* ... but the "real" data interface has two bulk endpoints */
-
-static struct usb_interface_descriptor ecm_data_intf = {
-       .bLength =              sizeof ecm_data_intf,
-       .bDescriptorType =      USB_DT_INTERFACE,
-
-       .bInterfaceNumber =     1,
-       .bAlternateSetting =    1,
-       .bNumEndpoints =        2,
-       .bInterfaceClass =      USB_CLASS_CDC_DATA,
-       .bInterfaceSubClass =   0,
-       .bInterfaceProtocol =   0,
-       /* .iInterface = DYNAMIC */
-};
-
-/* full speed support: */
-
-static struct usb_endpoint_descriptor fs_ecm_notify_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_INT,
-       .wMaxPacketSize =       cpu_to_le16(ECM_STATUS_BYTECOUNT),
-       .bInterval =            ECM_STATUS_INTERVAL_MS,
-};
-
-static struct usb_endpoint_descriptor fs_ecm_in_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-};
-
-static struct usb_endpoint_descriptor fs_ecm_out_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_OUT,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-};
-
-static struct usb_descriptor_header *ecm_fs_function[] = {
-       /* CDC ECM control descriptors */
-       (struct usb_descriptor_header *) &ecm_iad_descriptor,
-       (struct usb_descriptor_header *) &ecm_control_intf,
-       (struct usb_descriptor_header *) &ecm_header_desc,
-       (struct usb_descriptor_header *) &ecm_union_desc,
-       (struct usb_descriptor_header *) &ecm_desc,
-
-       /* NOTE: status endpoint might need to be removed */
-       (struct usb_descriptor_header *) &fs_ecm_notify_desc,
-
-       /* data interface, altsettings 0 and 1 */
-       (struct usb_descriptor_header *) &ecm_data_nop_intf,
-       (struct usb_descriptor_header *) &ecm_data_intf,
-       (struct usb_descriptor_header *) &fs_ecm_in_desc,
-       (struct usb_descriptor_header *) &fs_ecm_out_desc,
-       NULL,
-};
-
-/* high speed support: */
-
-static struct usb_endpoint_descriptor hs_ecm_notify_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_INT,
-       .wMaxPacketSize =       cpu_to_le16(ECM_STATUS_BYTECOUNT),
-       .bInterval =            USB_MS_TO_HS_INTERVAL(ECM_STATUS_INTERVAL_MS),
-};
-
-static struct usb_endpoint_descriptor hs_ecm_in_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(512),
-};
-
-static struct usb_endpoint_descriptor hs_ecm_out_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_OUT,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(512),
-};
-
-static struct usb_descriptor_header *ecm_hs_function[] = {
-       /* CDC ECM control descriptors */
-       (struct usb_descriptor_header *) &ecm_iad_descriptor,
-       (struct usb_descriptor_header *) &ecm_control_intf,
-       (struct usb_descriptor_header *) &ecm_header_desc,
-       (struct usb_descriptor_header *) &ecm_union_desc,
-       (struct usb_descriptor_header *) &ecm_desc,
-
-       /* NOTE: status endpoint might need to be removed */
-       (struct usb_descriptor_header *) &hs_ecm_notify_desc,
-
-       /* data interface, altsettings 0 and 1 */
-       (struct usb_descriptor_header *) &ecm_data_nop_intf,
-       (struct usb_descriptor_header *) &ecm_data_intf,
-       (struct usb_descriptor_header *) &hs_ecm_in_desc,
-       (struct usb_descriptor_header *) &hs_ecm_out_desc,
-       NULL,
-};
-
-/* super speed support: */
-
-static struct usb_endpoint_descriptor ss_ecm_notify_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_INT,
-       .wMaxPacketSize =       cpu_to_le16(ECM_STATUS_BYTECOUNT),
-       .bInterval =            USB_MS_TO_HS_INTERVAL(ECM_STATUS_INTERVAL_MS),
-};
-
-static struct usb_ss_ep_comp_descriptor ss_ecm_intr_comp_desc = {
-       .bLength =              sizeof ss_ecm_intr_comp_desc,
-       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
-
-       /* the following 3 values can be tweaked if necessary */
-       /* .bMaxBurst =         0, */
-       /* .bmAttributes =      0, */
-       .wBytesPerInterval =    cpu_to_le16(ECM_STATUS_BYTECOUNT),
-};
-
-static struct usb_endpoint_descriptor ss_ecm_in_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(1024),
-};
-
-static struct usb_endpoint_descriptor ss_ecm_out_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_OUT,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(1024),
-};
-
-static struct usb_ss_ep_comp_descriptor ss_ecm_bulk_comp_desc = {
-       .bLength =              sizeof ss_ecm_bulk_comp_desc,
-       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
-
-       /* the following 2 values can be tweaked if necessary */
-       /* .bMaxBurst =         0, */
-       /* .bmAttributes =      0, */
-};
-
-static struct usb_descriptor_header *ecm_ss_function[] = {
-       /* CDC ECM control descriptors */
-       (struct usb_descriptor_header *) &ecm_iad_descriptor,
-       (struct usb_descriptor_header *) &ecm_control_intf,
-       (struct usb_descriptor_header *) &ecm_header_desc,
-       (struct usb_descriptor_header *) &ecm_union_desc,
-       (struct usb_descriptor_header *) &ecm_desc,
-
-       /* NOTE: status endpoint might need to be removed */
-       (struct usb_descriptor_header *) &ss_ecm_notify_desc,
-       (struct usb_descriptor_header *) &ss_ecm_intr_comp_desc,
-
-       /* data interface, altsettings 0 and 1 */
-       (struct usb_descriptor_header *) &ecm_data_nop_intf,
-       (struct usb_descriptor_header *) &ecm_data_intf,
-       (struct usb_descriptor_header *) &ss_ecm_in_desc,
-       (struct usb_descriptor_header *) &ss_ecm_bulk_comp_desc,
-       (struct usb_descriptor_header *) &ss_ecm_out_desc,
-       (struct usb_descriptor_header *) &ss_ecm_bulk_comp_desc,
-       NULL,
-};
-
-/* string descriptors: */
-
-static struct usb_string ecm_string_defs[] = {
-       [0].s = "CDC Ethernet Control Model (ECM)",
-       [1].s = "",
-       [2].s = "CDC Ethernet Data",
-       [3].s = "CDC ECM",
-       {  } /* end of list */
-};
-
-static struct usb_gadget_strings ecm_string_table = {
-       .language =             0x0409, /* en-us */
-       .strings =              ecm_string_defs,
-};
-
-static struct usb_gadget_strings *ecm_strings[] = {
-       &ecm_string_table,
-       NULL,
-};
-
-/*-------------------------------------------------------------------------*/
-
-static void ecm_do_notify(struct f_ecm *ecm)
-{
-       struct usb_request              *req = ecm->notify_req;
-       struct usb_cdc_notification     *event;
-       struct usb_composite_dev        *cdev = ecm->port.func.config->cdev;
-       __le32                          *data;
-       int                             status;
-
-       /* notification already in flight? */
-       if (!req)
-               return;
-
-       event = req->buf;
-       switch (ecm->notify_state) {
-       case ECM_NOTIFY_NONE:
-               return;
-
-       case ECM_NOTIFY_CONNECT:
-               event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
-               if (ecm->is_open)
-                       event->wValue = cpu_to_le16(1);
-               else
-                       event->wValue = cpu_to_le16(0);
-               event->wLength = 0;
-               req->length = sizeof *event;
-
-               DBG(cdev, "notify connect %s\n",
-                               ecm->is_open ? "true" : "false");
-               ecm->notify_state = ECM_NOTIFY_SPEED;
-               break;
-
-       case ECM_NOTIFY_SPEED:
-               event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE;
-               event->wValue = cpu_to_le16(0);
-               event->wLength = cpu_to_le16(8);
-               req->length = ECM_STATUS_BYTECOUNT;
-
-               /* SPEED_CHANGE data is up/down speeds in bits/sec */
-               data = req->buf + sizeof *event;
-               data[0] = cpu_to_le32(ecm_bitrate(cdev->gadget));
-               data[1] = data[0];
-
-               DBG(cdev, "notify speed %d\n", ecm_bitrate(cdev->gadget));
-               ecm->notify_state = ECM_NOTIFY_NONE;
-               break;
-       }
-       event->bmRequestType = 0xA1;
-       event->wIndex = cpu_to_le16(ecm->ctrl_id);
-
-       ecm->notify_req = NULL;
-       status = usb_ep_queue(ecm->notify, req, GFP_ATOMIC);
-       if (status < 0) {
-               ecm->notify_req = req;
-               DBG(cdev, "notify --> %d\n", status);
-       }
-}
-
-static void ecm_notify(struct f_ecm *ecm)
-{
-       /* NOTE on most versions of Linux, host side cdc-ethernet
-        * won't listen for notifications until its netdevice opens.
-        * The first notification then sits in the FIFO for a long
-        * time, and the second one is queued.
-        */
-       ecm->notify_state = ECM_NOTIFY_CONNECT;
-       ecm_do_notify(ecm);
-}
-
-static void ecm_notify_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       struct f_ecm                    *ecm = req->context;
-       struct usb_composite_dev        *cdev = ecm->port.func.config->cdev;
-       struct usb_cdc_notification     *event = req->buf;
-
-       switch (req->status) {
-       case 0:
-               /* no fault */
-               break;
-       case -ECONNRESET:
-       case -ESHUTDOWN:
-               ecm->notify_state = ECM_NOTIFY_NONE;
-               break;
-       default:
-               DBG(cdev, "event %02x --> %d\n",
-                       event->bNotificationType, req->status);
-               break;
-       }
-       ecm->notify_req = req;
-       ecm_do_notify(ecm);
-}
-
-static int ecm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
-{
-       struct f_ecm            *ecm = func_to_ecm(f);
-       struct usb_composite_dev *cdev = f->config->cdev;
-       struct usb_request      *req = cdev->req;
-       int                     value = -EOPNOTSUPP;
-       u16                     w_index = le16_to_cpu(ctrl->wIndex);
-       u16                     w_value = le16_to_cpu(ctrl->wValue);
-       u16                     w_length = le16_to_cpu(ctrl->wLength);
-
-       /* composite driver infrastructure handles everything except
-        * CDC class messages; interface activation uses set_alt().
-        */
-       switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
-       case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
-                       | USB_CDC_SET_ETHERNET_PACKET_FILTER:
-               /* see 6.2.30: no data, wIndex = interface,
-                * wValue = packet filter bitmap
-                */
-               if (w_length != 0 || w_index != ecm->ctrl_id)
-                       goto invalid;
-               DBG(cdev, "packet filter %02x\n", w_value);
-               /* REVISIT locking of cdc_filter.  This assumes the UDC
-                * driver won't have a concurrent packet TX irq running on
-                * another CPU; or that if it does, this write is atomic...
-                */
-               ecm->port.cdc_filter = w_value;
-               value = 0;
-               break;
-
-       /* and optionally:
-        * case USB_CDC_SEND_ENCAPSULATED_COMMAND:
-        * case USB_CDC_GET_ENCAPSULATED_RESPONSE:
-        * case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS:
-        * case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER:
-        * case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER:
-        * case USB_CDC_GET_ETHERNET_STATISTIC:
-        */
-
-       default:
-invalid:
-               DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
-                       ctrl->bRequestType, ctrl->bRequest,
-                       w_value, w_index, w_length);
-       }
-
-       /* respond with data transfer or status phase? */
-       if (value >= 0) {
-               DBG(cdev, "ecm req%02x.%02x v%04x i%04x l%d\n",
-                       ctrl->bRequestType, ctrl->bRequest,
-                       w_value, w_index, w_length);
-               req->zero = 0;
-               req->length = value;
-               value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
-               if (value < 0)
-                       ERROR(cdev, "ecm req %02x.%02x response err %d\n",
-                                       ctrl->bRequestType, ctrl->bRequest,
-                                       value);
-       }
-
-       /* device either stalls (value < 0) or reports success */
-       return value;
-}
-
-
-static int ecm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
-{
-       struct f_ecm            *ecm = func_to_ecm(f);
-       struct usb_composite_dev *cdev = f->config->cdev;
-
-       /* Control interface has only altsetting 0 */
-       if (intf == ecm->ctrl_id) {
-               if (alt != 0)
-                       goto fail;
-
-               if (ecm->notify->driver_data) {
-                       VDBG(cdev, "reset ecm control %d\n", intf);
-                       usb_ep_disable(ecm->notify);
-               }
-               if (!(ecm->notify->desc)) {
-                       VDBG(cdev, "init ecm ctrl %d\n", intf);
-                       if (config_ep_by_speed(cdev->gadget, f, ecm->notify))
-                               goto fail;
-               }
-               usb_ep_enable(ecm->notify);
-               ecm->notify->driver_data = ecm;
-
-       /* Data interface has two altsettings, 0 and 1 */
-       } else if (intf == ecm->data_id) {
-               if (alt > 1)
-                       goto fail;
-
-               if (ecm->port.in_ep->driver_data) {
-                       DBG(cdev, "reset ecm\n");
-                       gether_disconnect(&ecm->port);
-               }
-
-               if (!ecm->port.in_ep->desc ||
-                   !ecm->port.out_ep->desc) {
-                       DBG(cdev, "init ecm\n");
-                       if (config_ep_by_speed(cdev->gadget, f,
-                                              ecm->port.in_ep) ||
-                           config_ep_by_speed(cdev->gadget, f,
-                                              ecm->port.out_ep)) {
-                               ecm->port.in_ep->desc = NULL;
-                               ecm->port.out_ep->desc = NULL;
-                               goto fail;
-                       }
-               }
-
-               /* CDC Ethernet only sends data in non-default altsettings.
-                * Changing altsettings resets filters, statistics, etc.
-                */
-               if (alt == 1) {
-                       struct net_device       *net;
-
-                       /* Enable zlps by default for ECM conformance;
-                        * override for musb_hdrc (avoids txdma ovhead).
-                        */
-                       ecm->port.is_zlp_ok = !(gadget_is_musbhdrc(cdev->gadget)
-                               );
-                       ecm->port.cdc_filter = DEFAULT_FILTER;
-                       DBG(cdev, "activate ecm\n");
-                       net = gether_connect(&ecm->port);
-                       if (IS_ERR(net))
-                               return PTR_ERR(net);
-               }
-
-               /* NOTE this can be a minor disagreement with the ECM spec,
-                * which says speed notifications will "always" follow
-                * connection notifications.  But we allow one connect to
-                * follow another (if the first is in flight), and instead
-                * just guarantee that a speed notification is always sent.
-                */
-               ecm_notify(ecm);
-       } else
-               goto fail;
-
-       return 0;
-fail:
-       return -EINVAL;
-}
-
-/* Because the data interface supports multiple altsettings,
- * this ECM function *MUST* implement a get_alt() method.
- */
-static int ecm_get_alt(struct usb_function *f, unsigned intf)
-{
-       struct f_ecm            *ecm = func_to_ecm(f);
-
-       if (intf == ecm->ctrl_id)
-               return 0;
-       return ecm->port.in_ep->driver_data ? 1 : 0;
-}
-
-static void ecm_disable(struct usb_function *f)
-{
-       struct f_ecm            *ecm = func_to_ecm(f);
-       struct usb_composite_dev *cdev = f->config->cdev;
-
-       DBG(cdev, "ecm deactivated\n");
-
-       if (ecm->port.in_ep->driver_data)
-               gether_disconnect(&ecm->port);
-
-       if (ecm->notify->driver_data) {
-               usb_ep_disable(ecm->notify);
-               ecm->notify->driver_data = NULL;
-               ecm->notify->desc = NULL;
-       }
-}
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * Callbacks let us notify the host about connect/disconnect when the
- * net device is opened or closed.
- *
- * For testing, note that link states on this side include both opened
- * and closed variants of:
- *
- *   - disconnected/unconfigured
- *   - configured but inactive (data alt 0)
- *   - configured and active (data alt 1)
- *
- * Each needs to be tested with unplug, rmmod, SET_CONFIGURATION, and
- * SET_INTERFACE (altsetting).  Remember also that "configured" doesn't
- * imply the host is actually polling the notification endpoint, and
- * likewise that "active" doesn't imply it's actually using the data
- * endpoints for traffic.
- */
-
-static void ecm_open(struct gether *geth)
-{
-       struct f_ecm            *ecm = func_to_ecm(&geth->func);
-
-       DBG(ecm->port.func.config->cdev, "%s\n", __func__);
-
-       ecm->is_open = true;
-       ecm_notify(ecm);
-}
-
-static void ecm_close(struct gether *geth)
-{
-       struct f_ecm            *ecm = func_to_ecm(&geth->func);
-
-       DBG(ecm->port.func.config->cdev, "%s\n", __func__);
-
-       ecm->is_open = false;
-       ecm_notify(ecm);
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* ethernet function driver setup/binding */
-
-static int
-ecm_bind(struct usb_configuration *c, struct usb_function *f)
-{
-       struct usb_composite_dev *cdev = c->cdev;
-       struct f_ecm            *ecm = func_to_ecm(f);
-       struct usb_string       *us;
-       int                     status;
-       struct usb_ep           *ep;
-
-       struct f_ecm_opts       *ecm_opts;
-
-       if (!can_support_ecm(cdev->gadget))
-               return -EINVAL;
-
-       ecm_opts = container_of(f->fi, struct f_ecm_opts, func_inst);
-
-       /*
-        * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
-        * configurations are bound in sequence with list_for_each_entry,
-        * in each configuration its functions are bound in sequence
-        * with list_for_each_entry, so we assume no race condition
-        * with regard to ecm_opts->bound access
-        */
-       if (!ecm_opts->bound) {
-               mutex_lock(&ecm_opts->lock);
-               gether_set_gadget(ecm_opts->net, cdev->gadget);
-               status = gether_register_netdev(ecm_opts->net);
-               mutex_unlock(&ecm_opts->lock);
-               if (status)
-                       return status;
-               ecm_opts->bound = true;
-       }
-
-       us = usb_gstrings_attach(cdev, ecm_strings,
-                                ARRAY_SIZE(ecm_string_defs));
-       if (IS_ERR(us))
-               return PTR_ERR(us);
-       ecm_control_intf.iInterface = us[0].id;
-       ecm_data_intf.iInterface = us[2].id;
-       ecm_desc.iMACAddress = us[1].id;
-       ecm_iad_descriptor.iFunction = us[3].id;
-
-       /* allocate instance-specific interface IDs */
-       status = usb_interface_id(c, f);
-       if (status < 0)
-               goto fail;
-       ecm->ctrl_id = status;
-       ecm_iad_descriptor.bFirstInterface = status;
-
-       ecm_control_intf.bInterfaceNumber = status;
-       ecm_union_desc.bMasterInterface0 = status;
-
-       status = usb_interface_id(c, f);
-       if (status < 0)
-               goto fail;
-       ecm->data_id = status;
-
-       ecm_data_nop_intf.bInterfaceNumber = status;
-       ecm_data_intf.bInterfaceNumber = status;
-       ecm_union_desc.bSlaveInterface0 = status;
-
-       status = -ENODEV;
-
-       /* allocate instance-specific endpoints */
-       ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_in_desc);
-       if (!ep)
-               goto fail;
-       ecm->port.in_ep = ep;
-       ep->driver_data = cdev; /* claim */
-
-       ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_out_desc);
-       if (!ep)
-               goto fail;
-       ecm->port.out_ep = ep;
-       ep->driver_data = cdev; /* claim */
-
-       /* NOTE:  a status/notification endpoint is *OPTIONAL* but we
-        * don't treat it that way.  It's simpler, and some newer CDC
-        * profiles (wireless handsets) no longer treat it as optional.
-        */
-       ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_notify_desc);
-       if (!ep)
-               goto fail;
-       ecm->notify = ep;
-       ep->driver_data = cdev; /* claim */
-
-       status = -ENOMEM;
-
-       /* allocate notification request and buffer */
-       ecm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
-       if (!ecm->notify_req)
-               goto fail;
-       ecm->notify_req->buf = kmalloc(ECM_STATUS_BYTECOUNT, GFP_KERNEL);
-       if (!ecm->notify_req->buf)
-               goto fail;
-       ecm->notify_req->context = ecm;
-       ecm->notify_req->complete = ecm_notify_complete;
-
-       /* support all relevant hardware speeds... we expect that when
-        * hardware is dual speed, all bulk-capable endpoints work at
-        * both speeds
-        */
-       hs_ecm_in_desc.bEndpointAddress = fs_ecm_in_desc.bEndpointAddress;
-       hs_ecm_out_desc.bEndpointAddress = fs_ecm_out_desc.bEndpointAddress;
-       hs_ecm_notify_desc.bEndpointAddress =
-               fs_ecm_notify_desc.bEndpointAddress;
-
-       ss_ecm_in_desc.bEndpointAddress = fs_ecm_in_desc.bEndpointAddress;
-       ss_ecm_out_desc.bEndpointAddress = fs_ecm_out_desc.bEndpointAddress;
-       ss_ecm_notify_desc.bEndpointAddress =
-               fs_ecm_notify_desc.bEndpointAddress;
-
-       status = usb_assign_descriptors(f, ecm_fs_function, ecm_hs_function,
-                       ecm_ss_function);
-       if (status)
-               goto fail;
-
-       /* NOTE:  all that is done without knowing or caring about
-        * the network link ... which is unavailable to this code
-        * until we're activated via set_alt().
-        */
-
-       ecm->port.open = ecm_open;
-       ecm->port.close = ecm_close;
-
-       DBG(cdev, "CDC Ethernet: %s speed IN/%s OUT/%s NOTIFY/%s\n",
-                       gadget_is_superspeed(c->cdev->gadget) ? "super" :
-                       gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
-                       ecm->port.in_ep->name, ecm->port.out_ep->name,
-                       ecm->notify->name);
-       return 0;
-
-fail:
-       if (ecm->notify_req) {
-               kfree(ecm->notify_req->buf);
-               usb_ep_free_request(ecm->notify, ecm->notify_req);
-       }
-
-       /* we might as well release our claims on endpoints */
-       if (ecm->notify)
-               ecm->notify->driver_data = NULL;
-       if (ecm->port.out_ep)
-               ecm->port.out_ep->driver_data = NULL;
-       if (ecm->port.in_ep)
-               ecm->port.in_ep->driver_data = NULL;
-
-       ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
-
-       return status;
-}
-
-static inline struct f_ecm_opts *to_f_ecm_opts(struct config_item *item)
-{
-       return container_of(to_config_group(item), struct f_ecm_opts,
-                           func_inst.group);
-}
-
-/* f_ecm_item_ops */
-USB_ETHERNET_CONFIGFS_ITEM(ecm);
-
-/* f_ecm_opts_dev_addr */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ecm);
-
-/* f_ecm_opts_host_addr */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ecm);
-
-/* f_ecm_opts_qmult */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ecm);
-
-/* f_ecm_opts_ifname */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ecm);
-
-static struct configfs_attribute *ecm_attrs[] = {
-       &f_ecm_opts_dev_addr.attr,
-       &f_ecm_opts_host_addr.attr,
-       &f_ecm_opts_qmult.attr,
-       &f_ecm_opts_ifname.attr,
-       NULL,
-};
-
-static struct config_item_type ecm_func_type = {
-       .ct_item_ops    = &ecm_item_ops,
-       .ct_attrs       = ecm_attrs,
-       .ct_owner       = THIS_MODULE,
-};
-
-static void ecm_free_inst(struct usb_function_instance *f)
-{
-       struct f_ecm_opts *opts;
-
-       opts = container_of(f, struct f_ecm_opts, func_inst);
-       if (opts->bound)
-               gether_cleanup(netdev_priv(opts->net));
-       else
-               free_netdev(opts->net);
-       kfree(opts);
-}
-
-static struct usb_function_instance *ecm_alloc_inst(void)
-{
-       struct f_ecm_opts *opts;
-
-       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
-       if (!opts)
-               return ERR_PTR(-ENOMEM);
-       mutex_init(&opts->lock);
-       opts->func_inst.free_func_inst = ecm_free_inst;
-       opts->net = gether_setup_default();
-       if (IS_ERR(opts->net)) {
-               struct net_device *net = opts->net;
-               kfree(opts);
-               return ERR_CAST(net);
-       }
-
-       config_group_init_type_name(&opts->func_inst.group, "", &ecm_func_type);
-
-       return &opts->func_inst;
-}
-
-static void ecm_free(struct usb_function *f)
-{
-       struct f_ecm *ecm;
-       struct f_ecm_opts *opts;
-
-       ecm = func_to_ecm(f);
-       opts = container_of(f->fi, struct f_ecm_opts, func_inst);
-       kfree(ecm);
-       mutex_lock(&opts->lock);
-       opts->refcnt--;
-       mutex_unlock(&opts->lock);
-}
-
-static void ecm_unbind(struct usb_configuration *c, struct usb_function *f)
-{
-       struct f_ecm            *ecm = func_to_ecm(f);
-
-       DBG(c->cdev, "ecm unbind\n");
-
-       usb_free_all_descriptors(f);
-
-       kfree(ecm->notify_req->buf);
-       usb_ep_free_request(ecm->notify, ecm->notify_req);
-}
-
-static struct usb_function *ecm_alloc(struct usb_function_instance *fi)
-{
-       struct f_ecm    *ecm;
-       struct f_ecm_opts *opts;
-       int status;
-
-       /* allocate and initialize one new instance */
-       ecm = kzalloc(sizeof(*ecm), GFP_KERNEL);
-       if (!ecm)
-               return ERR_PTR(-ENOMEM);
-
-       opts = container_of(fi, struct f_ecm_opts, func_inst);
-       mutex_lock(&opts->lock);
-       opts->refcnt++;
-
-       /* export host's Ethernet address in CDC format */
-       status = gether_get_host_addr_cdc(opts->net, ecm->ethaddr,
-                                         sizeof(ecm->ethaddr));
-       if (status < 12) {
-               kfree(ecm);
-               mutex_unlock(&opts->lock);
-               return ERR_PTR(-EINVAL);
-       }
-       ecm_string_defs[1].s = ecm->ethaddr;
-
-       ecm->port.ioport = netdev_priv(opts->net);
-       mutex_unlock(&opts->lock);
-       ecm->port.cdc_filter = DEFAULT_FILTER;
-
-       ecm->port.func.name = "cdc_ethernet";
-       /* descriptors are per-instance copies */
-       ecm->port.func.bind = ecm_bind;
-       ecm->port.func.unbind = ecm_unbind;
-       ecm->port.func.set_alt = ecm_set_alt;
-       ecm->port.func.get_alt = ecm_get_alt;
-       ecm->port.func.setup = ecm_setup;
-       ecm->port.func.disable = ecm_disable;
-       ecm->port.func.free_func = ecm_free;
-
-       return &ecm->port.func;
-}
-
-DECLARE_USB_FUNCTION_INIT(ecm, ecm_alloc_inst, ecm_alloc);
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/f_eem.c b/drivers/usb/gadget/f_eem.c
deleted file mode 100644 (file)
index 4d8b236..0000000
+++ /dev/null
@@ -1,660 +0,0 @@
-/*
- * f_eem.c -- USB CDC Ethernet (EEM) link function driver
- *
- * Copyright (C) 2003-2005,2008 David Brownell
- * Copyright (C) 2008 Nokia Corporation
- * Copyright (C) 2009 EF Johnson 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.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/etherdevice.h>
-#include <linux/crc32.h>
-#include <linux/slab.h>
-
-#include "u_ether.h"
-#include "u_ether_configfs.h"
-#include "u_eem.h"
-
-#define EEM_HLEN 2
-
-/*
- * This function is a "CDC Ethernet Emulation Model" (CDC EEM)
- * Ethernet link.
- */
-
-struct f_eem {
-       struct gether                   port;
-       u8                              ctrl_id;
-};
-
-static inline struct f_eem *func_to_eem(struct usb_function *f)
-{
-       return container_of(f, struct f_eem, port.func);
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* interface descriptor: */
-
-static struct usb_interface_descriptor eem_intf = {
-       .bLength =              sizeof eem_intf,
-       .bDescriptorType =      USB_DT_INTERFACE,
-
-       /* .bInterfaceNumber = DYNAMIC */
-       .bNumEndpoints =        2,
-       .bInterfaceClass =      USB_CLASS_COMM,
-       .bInterfaceSubClass =   USB_CDC_SUBCLASS_EEM,
-       .bInterfaceProtocol =   USB_CDC_PROTO_EEM,
-       /* .iInterface = DYNAMIC */
-};
-
-/* full speed support: */
-
-static struct usb_endpoint_descriptor eem_fs_in_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-};
-
-static struct usb_endpoint_descriptor eem_fs_out_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_OUT,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-};
-
-static struct usb_descriptor_header *eem_fs_function[] = {
-       /* CDC EEM control descriptors */
-       (struct usb_descriptor_header *) &eem_intf,
-       (struct usb_descriptor_header *) &eem_fs_in_desc,
-       (struct usb_descriptor_header *) &eem_fs_out_desc,
-       NULL,
-};
-
-/* high speed support: */
-
-static struct usb_endpoint_descriptor eem_hs_in_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(512),
-};
-
-static struct usb_endpoint_descriptor eem_hs_out_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_OUT,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(512),
-};
-
-static struct usb_descriptor_header *eem_hs_function[] = {
-       /* CDC EEM control descriptors */
-       (struct usb_descriptor_header *) &eem_intf,
-       (struct usb_descriptor_header *) &eem_hs_in_desc,
-       (struct usb_descriptor_header *) &eem_hs_out_desc,
-       NULL,
-};
-
-/* super speed support: */
-
-static struct usb_endpoint_descriptor eem_ss_in_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(1024),
-};
-
-static struct usb_endpoint_descriptor eem_ss_out_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_OUT,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(1024),
-};
-
-static struct usb_ss_ep_comp_descriptor eem_ss_bulk_comp_desc = {
-       .bLength =              sizeof eem_ss_bulk_comp_desc,
-       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
-
-       /* the following 2 values can be tweaked if necessary */
-       /* .bMaxBurst =         0, */
-       /* .bmAttributes =      0, */
-};
-
-static struct usb_descriptor_header *eem_ss_function[] = {
-       /* CDC EEM control descriptors */
-       (struct usb_descriptor_header *) &eem_intf,
-       (struct usb_descriptor_header *) &eem_ss_in_desc,
-       (struct usb_descriptor_header *) &eem_ss_bulk_comp_desc,
-       (struct usb_descriptor_header *) &eem_ss_out_desc,
-       (struct usb_descriptor_header *) &eem_ss_bulk_comp_desc,
-       NULL,
-};
-
-/* string descriptors: */
-
-static struct usb_string eem_string_defs[] = {
-       [0].s = "CDC Ethernet Emulation Model (EEM)",
-       {  } /* end of list */
-};
-
-static struct usb_gadget_strings eem_string_table = {
-       .language =             0x0409, /* en-us */
-       .strings =              eem_string_defs,
-};
-
-static struct usb_gadget_strings *eem_strings[] = {
-       &eem_string_table,
-       NULL,
-};
-
-/*-------------------------------------------------------------------------*/
-
-static int eem_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
-{
-       struct usb_composite_dev *cdev = f->config->cdev;
-       int                     value = -EOPNOTSUPP;
-       u16                     w_index = le16_to_cpu(ctrl->wIndex);
-       u16                     w_value = le16_to_cpu(ctrl->wValue);
-       u16                     w_length = le16_to_cpu(ctrl->wLength);
-
-       DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
-               ctrl->bRequestType, ctrl->bRequest,
-               w_value, w_index, w_length);
-
-       /* device either stalls (value < 0) or reports success */
-       return value;
-}
-
-
-static int eem_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
-{
-       struct f_eem            *eem = func_to_eem(f);
-       struct usb_composite_dev *cdev = f->config->cdev;
-       struct net_device       *net;
-
-       /* we know alt == 0, so this is an activation or a reset */
-       if (alt != 0)
-               goto fail;
-
-       if (intf == eem->ctrl_id) {
-
-               if (eem->port.in_ep->driver_data) {
-                       DBG(cdev, "reset eem\n");
-                       gether_disconnect(&eem->port);
-               }
-
-               if (!eem->port.in_ep->desc || !eem->port.out_ep->desc) {
-                       DBG(cdev, "init eem\n");
-                       if (config_ep_by_speed(cdev->gadget, f,
-                                              eem->port.in_ep) ||
-                           config_ep_by_speed(cdev->gadget, f,
-                                              eem->port.out_ep)) {
-                               eem->port.in_ep->desc = NULL;
-                               eem->port.out_ep->desc = NULL;
-                               goto fail;
-                       }
-               }
-
-               /* zlps should not occur because zero-length EEM packets
-                * will be inserted in those cases where they would occur
-                */
-               eem->port.is_zlp_ok = 1;
-               eem->port.cdc_filter = DEFAULT_FILTER;
-               DBG(cdev, "activate eem\n");
-               net = gether_connect(&eem->port);
-               if (IS_ERR(net))
-                       return PTR_ERR(net);
-       } else
-               goto fail;
-
-       return 0;
-fail:
-       return -EINVAL;
-}
-
-static void eem_disable(struct usb_function *f)
-{
-       struct f_eem            *eem = func_to_eem(f);
-       struct usb_composite_dev *cdev = f->config->cdev;
-
-       DBG(cdev, "eem deactivated\n");
-
-       if (eem->port.in_ep->driver_data)
-               gether_disconnect(&eem->port);
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* EEM function driver setup/binding */
-
-static int eem_bind(struct usb_configuration *c, struct usb_function *f)
-{
-       struct usb_composite_dev *cdev = c->cdev;
-       struct f_eem            *eem = func_to_eem(f);
-       struct usb_string       *us;
-       int                     status;
-       struct usb_ep           *ep;
-
-       struct f_eem_opts       *eem_opts;
-
-       eem_opts = container_of(f->fi, struct f_eem_opts, func_inst);
-       /*
-        * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
-        * configurations are bound in sequence with list_for_each_entry,
-        * in each configuration its functions are bound in sequence
-        * with list_for_each_entry, so we assume no race condition
-        * with regard to eem_opts->bound access
-        */
-       if (!eem_opts->bound) {
-               mutex_lock(&eem_opts->lock);
-               gether_set_gadget(eem_opts->net, cdev->gadget);
-               status = gether_register_netdev(eem_opts->net);
-               mutex_unlock(&eem_opts->lock);
-               if (status)
-                       return status;
-               eem_opts->bound = true;
-       }
-
-       us = usb_gstrings_attach(cdev, eem_strings,
-                                ARRAY_SIZE(eem_string_defs));
-       if (IS_ERR(us))
-               return PTR_ERR(us);
-       eem_intf.iInterface = us[0].id;
-
-       /* allocate instance-specific interface IDs */
-       status = usb_interface_id(c, f);
-       if (status < 0)
-               goto fail;
-       eem->ctrl_id = status;
-       eem_intf.bInterfaceNumber = status;
-
-       status = -ENODEV;
-
-       /* allocate instance-specific endpoints */
-       ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_in_desc);
-       if (!ep)
-               goto fail;
-       eem->port.in_ep = ep;
-       ep->driver_data = cdev; /* claim */
-
-       ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_out_desc);
-       if (!ep)
-               goto fail;
-       eem->port.out_ep = ep;
-       ep->driver_data = cdev; /* claim */
-
-       status = -ENOMEM;
-
-       /* support all relevant hardware speeds... we expect that when
-        * hardware is dual speed, all bulk-capable endpoints work at
-        * both speeds
-        */
-       eem_hs_in_desc.bEndpointAddress = eem_fs_in_desc.bEndpointAddress;
-       eem_hs_out_desc.bEndpointAddress = eem_fs_out_desc.bEndpointAddress;
-
-       eem_ss_in_desc.bEndpointAddress = eem_fs_in_desc.bEndpointAddress;
-       eem_ss_out_desc.bEndpointAddress = eem_fs_out_desc.bEndpointAddress;
-
-       status = usb_assign_descriptors(f, eem_fs_function, eem_hs_function,
-                       eem_ss_function);
-       if (status)
-               goto fail;
-
-       DBG(cdev, "CDC Ethernet (EEM): %s speed IN/%s OUT/%s\n",
-                       gadget_is_superspeed(c->cdev->gadget) ? "super" :
-                       gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
-                       eem->port.in_ep->name, eem->port.out_ep->name);
-       return 0;
-
-fail:
-       usb_free_all_descriptors(f);
-       if (eem->port.out_ep)
-               eem->port.out_ep->driver_data = NULL;
-       if (eem->port.in_ep)
-               eem->port.in_ep->driver_data = NULL;
-
-       ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
-
-       return status;
-}
-
-static void eem_cmd_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       struct sk_buff *skb = (struct sk_buff *)req->context;
-
-       dev_kfree_skb_any(skb);
-}
-
-/*
- * Add the EEM header and ethernet checksum.
- * We currently do not attempt to put multiple ethernet frames
- * into a single USB transfer
- */
-static struct sk_buff *eem_wrap(struct gether *port, struct sk_buff *skb)
-{
-       struct sk_buff  *skb2 = NULL;
-       struct usb_ep   *in = port->in_ep;
-       int             padlen = 0;
-       u16             len = skb->len;
-
-       int headroom = skb_headroom(skb);
-       int tailroom = skb_tailroom(skb);
-
-       /* When (len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) is 0,
-        * stick two bytes of zero-length EEM packet on the end.
-        */
-       if (((len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) == 0)
-               padlen += 2;
-
-       if ((tailroom >= (ETH_FCS_LEN + padlen)) &&
-                       (headroom >= EEM_HLEN) && !skb_cloned(skb))
-               goto done;
-
-       skb2 = skb_copy_expand(skb, EEM_HLEN, ETH_FCS_LEN + padlen, GFP_ATOMIC);
-       dev_kfree_skb_any(skb);
-       skb = skb2;
-       if (!skb)
-               return skb;
-
-done:
-       /* use the "no CRC" option */
-       put_unaligned_be32(0xdeadbeef, skb_put(skb, 4));
-
-       /* EEM packet header format:
-        * b0..13:      length of ethernet frame
-        * b14:         bmCRC (0 == sentinel CRC)
-        * b15:         bmType (0 == data)
-        */
-       len = skb->len;
-       put_unaligned_le16(len & 0x3FFF, skb_push(skb, 2));
-
-       /* add a zero-length EEM packet, if needed */
-       if (padlen)
-               put_unaligned_le16(0, skb_put(skb, 2));
-
-       return skb;
-}
-
-/*
- * Remove the EEM header.  Note that there can be many EEM packets in a single
- * USB transfer, so we need to break them out and handle them independently.
- */
-static int eem_unwrap(struct gether *port,
-                       struct sk_buff *skb,
-                       struct sk_buff_head *list)
-{
-       struct usb_composite_dev        *cdev = port->func.config->cdev;
-       int                             status = 0;
-
-       do {
-               struct sk_buff  *skb2;
-               u16             header;
-               u16             len = 0;
-
-               if (skb->len < EEM_HLEN) {
-                       status = -EINVAL;
-                       DBG(cdev, "invalid EEM header\n");
-                       goto error;
-               }
-
-               /* remove the EEM header */
-               header = get_unaligned_le16(skb->data);
-               skb_pull(skb, EEM_HLEN);
-
-               /* EEM packet header format:
-                * b0..14:      EEM type dependent (data or command)
-                * b15:         bmType (0 == data, 1 == command)
-                */
-               if (header & BIT(15)) {
-                       struct usb_request      *req = cdev->req;
-                       u16                     bmEEMCmd;
-
-                       /* EEM command packet format:
-                        * b0..10:      bmEEMCmdParam
-                        * b11..13:     bmEEMCmd
-                        * b14:         reserved (must be zero)
-                        * b15:         bmType (1 == command)
-                        */
-                       if (header & BIT(14))
-                               continue;
-
-                       bmEEMCmd = (header >> 11) & 0x7;
-                       switch (bmEEMCmd) {
-                       case 0: /* echo */
-                               len = header & 0x7FF;
-                               if (skb->len < len) {
-                                       status = -EOVERFLOW;
-                                       goto error;
-                               }
-
-                               skb2 = skb_clone(skb, GFP_ATOMIC);
-                               if (unlikely(!skb2)) {
-                                       DBG(cdev, "EEM echo response error\n");
-                                       goto next;
-                               }
-                               skb_trim(skb2, len);
-                               put_unaligned_le16(BIT(15) | BIT(11) | len,
-                                                       skb_push(skb2, 2));
-                               skb_copy_bits(skb2, 0, req->buf, skb2->len);
-                               req->length = skb2->len;
-                               req->complete = eem_cmd_complete;
-                               req->zero = 1;
-                               req->context = skb2;
-                               if (usb_ep_queue(port->in_ep, req, GFP_ATOMIC))
-                                       DBG(cdev, "echo response queue fail\n");
-                               break;
-
-                       case 1:  /* echo response */
-                       case 2:  /* suspend hint */
-                       case 3:  /* response hint */
-                       case 4:  /* response complete hint */
-                       case 5:  /* tickle */
-                       default: /* reserved */
-                               continue;
-                       }
-               } else {
-                       u32             crc, crc2;
-                       struct sk_buff  *skb3;
-
-                       /* check for zero-length EEM packet */
-                       if (header == 0)
-                               continue;
-
-                       /* EEM data packet format:
-                        * b0..13:      length of ethernet frame
-                        * b14:         bmCRC (0 == sentinel, 1 == calculated)
-                        * b15:         bmType (0 == data)
-                        */
-                       len = header & 0x3FFF;
-                       if ((skb->len < len)
-                                       || (len < (ETH_HLEN + ETH_FCS_LEN))) {
-                               status = -EINVAL;
-                               goto error;
-                       }
-
-                       /* validate CRC */
-                       if (header & BIT(14)) {
-                               crc = get_unaligned_le32(skb->data + len
-                                                       - ETH_FCS_LEN);
-                               crc2 = ~crc32_le(~0,
-                                               skb->data, len - ETH_FCS_LEN);
-                       } else {
-                               crc = get_unaligned_be32(skb->data + len
-                                                       - ETH_FCS_LEN);
-                               crc2 = 0xdeadbeef;
-                       }
-                       if (crc != crc2) {
-                               DBG(cdev, "invalid EEM CRC\n");
-                               goto next;
-                       }
-
-                       skb2 = skb_clone(skb, GFP_ATOMIC);
-                       if (unlikely(!skb2)) {
-                               DBG(cdev, "unable to unframe EEM packet\n");
-                               continue;
-                       }
-                       skb_trim(skb2, len - ETH_FCS_LEN);
-
-                       skb3 = skb_copy_expand(skb2,
-                                               NET_IP_ALIGN,
-                                               0,
-                                               GFP_ATOMIC);
-                       if (unlikely(!skb3)) {
-                               DBG(cdev, "unable to realign EEM packet\n");
-                               dev_kfree_skb_any(skb2);
-                               continue;
-                       }
-                       dev_kfree_skb_any(skb2);
-                       skb_queue_tail(list, skb3);
-               }
-next:
-               skb_pull(skb, len);
-       } while (skb->len);
-
-error:
-       dev_kfree_skb_any(skb);
-       return status;
-}
-
-static inline struct f_eem_opts *to_f_eem_opts(struct config_item *item)
-{
-       return container_of(to_config_group(item), struct f_eem_opts,
-                           func_inst.group);
-}
-
-/* f_eem_item_ops */
-USB_ETHERNET_CONFIGFS_ITEM(eem);
-
-/* f_eem_opts_dev_addr */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(eem);
-
-/* f_eem_opts_host_addr */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(eem);
-
-/* f_eem_opts_qmult */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(eem);
-
-/* f_eem_opts_ifname */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(eem);
-
-static struct configfs_attribute *eem_attrs[] = {
-       &f_eem_opts_dev_addr.attr,
-       &f_eem_opts_host_addr.attr,
-       &f_eem_opts_qmult.attr,
-       &f_eem_opts_ifname.attr,
-       NULL,
-};
-
-static struct config_item_type eem_func_type = {
-       .ct_item_ops    = &eem_item_ops,
-       .ct_attrs       = eem_attrs,
-       .ct_owner       = THIS_MODULE,
-};
-
-static void eem_free_inst(struct usb_function_instance *f)
-{
-       struct f_eem_opts *opts;
-
-       opts = container_of(f, struct f_eem_opts, func_inst);
-       if (opts->bound)
-               gether_cleanup(netdev_priv(opts->net));
-       else
-               free_netdev(opts->net);
-       kfree(opts);
-}
-
-static struct usb_function_instance *eem_alloc_inst(void)
-{
-       struct f_eem_opts *opts;
-
-       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
-       if (!opts)
-               return ERR_PTR(-ENOMEM);
-       mutex_init(&opts->lock);
-       opts->func_inst.free_func_inst = eem_free_inst;
-       opts->net = gether_setup_default();
-       if (IS_ERR(opts->net)) {
-               struct net_device *net = opts->net;
-               kfree(opts);
-               return ERR_CAST(net);
-       }
-
-       config_group_init_type_name(&opts->func_inst.group, "", &eem_func_type);
-
-       return &opts->func_inst;
-}
-
-static void eem_free(struct usb_function *f)
-{
-       struct f_eem *eem;
-       struct f_eem_opts *opts;
-
-       eem = func_to_eem(f);
-       opts = container_of(f->fi, struct f_eem_opts, func_inst);
-       kfree(eem);
-       mutex_lock(&opts->lock);
-       opts->refcnt--;
-       mutex_unlock(&opts->lock);
-}
-
-static void eem_unbind(struct usb_configuration *c, struct usb_function *f)
-{
-       DBG(c->cdev, "eem unbind\n");
-
-       usb_free_all_descriptors(f);
-}
-
-static struct usb_function *eem_alloc(struct usb_function_instance *fi)
-{
-       struct f_eem    *eem;
-       struct f_eem_opts *opts;
-
-       /* allocate and initialize one new instance */
-       eem = kzalloc(sizeof(*eem), GFP_KERNEL);
-       if (!eem)
-               return ERR_PTR(-ENOMEM);
-
-       opts = container_of(fi, struct f_eem_opts, func_inst);
-       mutex_lock(&opts->lock);
-       opts->refcnt++;
-
-       eem->port.ioport = netdev_priv(opts->net);
-       mutex_unlock(&opts->lock);
-       eem->port.cdc_filter = DEFAULT_FILTER;
-
-       eem->port.func.name = "cdc_eem";
-       /* descriptors are per-instance copies */
-       eem->port.func.bind = eem_bind;
-       eem->port.func.unbind = eem_unbind;
-       eem->port.func.set_alt = eem_set_alt;
-       eem->port.func.setup = eem_setup;
-       eem->port.func.disable = eem_disable;
-       eem->port.func.free_func = eem_free;
-       eem->port.wrap = eem_wrap;
-       eem->port.unwrap = eem_unwrap;
-       eem->port.header_len = EEM_HLEN;
-
-       return &eem->port.func;
-}
-
-DECLARE_USB_FUNCTION_INIT(eem, eem_alloc_inst, eem_alloc);
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c
deleted file mode 100644 (file)
index fe45060..0000000
+++ /dev/null
@@ -1,3347 +0,0 @@
-/*
- * f_fs.c -- user mode file system API for USB composite function controllers
- *
- * Copyright (C) 2010 Samsung Electronics
- * Author: Michal Nazarewicz <mina86@mina86.com>
- *
- * Based on inode.c (GadgetFS) which was:
- * 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.
- */
-
-
-/* #define DEBUG */
-/* #define VERBOSE_DEBUG */
-
-#include <linux/blkdev.h>
-#include <linux/pagemap.h>
-#include <linux/export.h>
-#include <linux/hid.h>
-#include <linux/module.h>
-#include <asm/unaligned.h>
-
-#include <linux/usb/composite.h>
-#include <linux/usb/functionfs.h>
-
-#include <linux/aio.h>
-#include <linux/mmu_context.h>
-#include <linux/poll.h>
-
-#include "u_fs.h"
-#include "u_f.h"
-#include "u_os_desc.h"
-#include "configfs.h"
-
-#define FUNCTIONFS_MAGIC       0xa647361 /* Chosen by a honest dice roll ;) */
-
-/* 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 ownership 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 inline enum ffs_setup_state
-ffs_setup_state_clear_cancelled(struct ffs_data *ffs)
-{
-       return (enum ffs_setup_state)
-               cmpxchg(&ffs->setup_state, FFS_SETUP_CANCELLED, FFS_NO_SETUP);
-}
-
-
-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 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, [2]: super speed */
-       struct usb_endpoint_descriptor  *descs[3];
-
-       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;
-};
-
-/*  ffs_io_data structure ***************************************************/
-
-struct ffs_io_data {
-       bool aio;
-       bool read;
-
-       struct kiocb *kiocb;
-       const struct iovec *iovec;
-       unsigned long nr_segs;
-       char __user *buf;
-       size_t len;
-
-       struct mm_struct *mm;
-       struct work_struct work;
-
-       struct usb_ep *ep;
-       struct usb_request *req;
-};
-
-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);
-
-/* Devices management *******************************************************/
-
-DEFINE_MUTEX(ffs_lock);
-EXPORT_SYMBOL_GPL(ffs_lock);
-
-static struct ffs_dev *_ffs_find_dev(const char *name);
-static struct ffs_dev *_ffs_alloc_dev(void);
-static int _ffs_name_dev(struct ffs_dev *dev, const char *name);
-static void _ffs_free_dev(struct ffs_dev *dev);
-static void *ffs_acquire_dev(const char *dev_name);
-static void ffs_release_dev(struct ffs_data *ffs_data);
-static int ffs_ready(struct ffs_data *ffs);
-static void ffs_closed(struct ffs_data *ffs);
-
-/* Misc helper functions ****************************************************/
-
-static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock)
-       __attribute__((warn_unused_result, nonnull));
-static char *ffs_prepare_buffer(const char __user *buf, size_t len)
-       __attribute__((warn_unused_result, nonnull));
-
-
-/* Control file aka ep0 *****************************************************/
-
-static void ffs_ep0_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       struct ffs_data *ffs = req->context;
-
-       complete_all(&ffs->ep0req_completion);
-}
-
-static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
-{
-       struct usb_request *req = ffs->ep0req;
-       int ret;
-
-       req->zero     = len < le16_to_cpu(ffs->ev.setup.wLength);
-
-       spin_unlock_irq(&ffs->ev.waitq.lock);
-
-       req->buf      = data;
-       req->length   = len;
-
-       /*
-        * UDC layer requires to provide a buffer even for ZLP, but should
-        * not use it at all. Let's provide some poisoned pointer to catch
-        * possible bug in the driver.
-        */
-       if (req->buf == NULL)
-               req->buf = (void *)0xDEADBABE;
-
-       reinit_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 req->status ? req->status : req->actual;
-}
-
-static int __ffs_ep0_stall(struct ffs_data *ffs)
-{
-       if (ffs->ev.can_stall) {
-               pr_vdebug("ep0 stall\n");
-               usb_ep_set_halt(ffs->gadget->ep0);
-               ffs->setup_state = FFS_NO_SETUP;
-               return -EL2HLT;
-       } else {
-               pr_debug("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_clear_cancelled(ffs) == FFS_SETUP_CANCELLED)
-               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 (IS_ERR(data)) {
-                       ret = PTR_ERR(data);
-                       break;
-               }
-
-               /* Handle data */
-               if (ffs->state == FFS_READ_DESCRIPTORS) {
-                       pr_info("read descriptors\n");
-                       ret = __ffs_data_got_descs(ffs, data, len);
-                       if (unlikely(ret < 0))
-                               break;
-
-                       ffs->state = FFS_READ_STRINGS;
-                       ret = len;
-               } else {
-                       pr_info("read strings\n");
-                       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 = ffs_ready(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_clear_cancelled(ffs)) {
-               case FFS_SETUP_CANCELLED:
-                       ret = -EIDRM;
-                       goto done_spin;
-
-               case FFS_NO_SETUP:
-                       ret = -ESRCH;
-                       goto done_spin;
-
-               case FFS_SETUP_PENDING:
-                       break;
-               }
-
-               /* FFS_SETUP_PENDING */
-               if (!(ffs->ev.setup.bRequestType & USB_DIR_IN)) {
-                       spin_unlock_irq(&ffs->ev.waitq.lock);
-                       ret = __ffs_ep0_stall(ffs);
-                       break;
-               }
-
-               /* FFS_SETUP_PENDING and not stall */
-               len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength));
-
-               spin_unlock_irq(&ffs->ev.waitq.lock);
-
-               data = ffs_prepare_buffer(buf, len);
-               if (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_CANCELLED 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_clear_cancelled(ffs) ==
-                   FFS_SETUP_CANCELLED) {
-                       ret = -EIDRM;
-done_spin:
-                       spin_unlock_irq(&ffs->ev.waitq.lock);
-               } else {
-                       /* unlocks spinlock */
-                       ret = __ffs_ep0_queue_wait(ffs, data, len);
-               }
-               kfree(data);
-               break;
-
-       default:
-               ret = -EBADFD;
-               break;
-       }
-
-       mutex_unlock(&ffs->mutex);
-       return ret;
-}
-
-static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
-                                    size_t n)
-{
-       /*
-        * We are holding ffs->ev.waitq.lock and ffs->mutex and we need
-        * to release them.
-        */
-       struct usb_functionfs_event events[n];
-       unsigned i = 0;
-
-       memset(events, 0, sizeof events);
-
-       do {
-               events[i].type = ffs->ev.types[i];
-               if (events[i].type == FUNCTIONFS_SETUP) {
-                       events[i].u.setup = ffs->ev.setup;
-                       ffs->setup_state = FFS_SETUP_PENDING;
-               }
-       } while (++i < n);
-
-       if (n < ffs->ev.count) {
-               ffs->ev.count -= n;
-               memmove(ffs->ev.types, ffs->ev.types + n,
-                       ffs->ev.count * sizeof *ffs->ev.types);
-       } else {
-               ffs->ev.count = 0;
-       }
-
-       spin_unlock_irq(&ffs->ev.waitq.lock);
-       mutex_unlock(&ffs->mutex);
-
-       return unlikely(__copy_to_user(buf, events, sizeof events))
-               ? -EFAULT : sizeof events;
-}
-
-static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
-                           size_t len, loff_t *ptr)
-{
-       struct ffs_data *ffs = file->private_data;
-       char *data = NULL;
-       size_t n;
-       int ret;
-
-       ENTER();
-
-       /* Fast check if setup was canceled */
-       if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED)
-               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_clear_cancelled(ffs)) {
-       case FFS_SETUP_CANCELLED:
-               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 (wait_event_interruptible_exclusive_locked_irq(ffs->ev.waitq,
-                                                       ffs->ev.count)) {
-                       ret = -EINTR;
-                       break;
-               }
-
-               return __ffs_ep0_read_events(ffs, buf,
-                                            min(n, (size_t)ffs->ev.count));
-
-       case FFS_SETUP_PENDING:
-               if (ffs->ev.setup.bRequestType & USB_DIR_IN) {
-                       spin_unlock_irq(&ffs->ev.waitq.lock);
-                       ret = __ffs_ep0_stall(ffs);
-                       goto done_mutex;
-               }
-
-               len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength));
-
-               spin_unlock_irq(&ffs->ev.waitq.lock);
-
-               if (likely(len)) {
-                       data = kmalloc(len, GFP_KERNEL);
-                       if (unlikely(!data)) {
-                               ret = -ENOMEM;
-                               goto done_mutex;
-                       }
-               }
-
-               spin_lock_irq(&ffs->ev.waitq.lock);
-
-               /* See ffs_ep0_write() */
-               if (ffs_setup_state_clear_cancelled(ffs) ==
-                   FFS_SETUP_CANCELLED) {
-                       ret = -EIDRM;
-                       break;
-               }
-
-               /* unlocks spinlock */
-               ret = __ffs_ep0_queue_wait(ffs, data, len);
-               if (likely(ret > 0) && unlikely(__copy_to_user(buf, data, len)))
-                       ret = -EFAULT;
-               goto done_mutex;
-
-       default:
-               ret = -EBADFD;
-               break;
-       }
-
-       spin_unlock_irq(&ffs->ev.waitq.lock);
-done_mutex:
-       mutex_unlock(&ffs->mutex);
-       kfree(data);
-       return ret;
-}
-
-static int ffs_ep0_open(struct inode *inode, struct file *file)
-{
-       struct ffs_data *ffs = inode->i_private;
-
-       ENTER();
-
-       if (unlikely(ffs->state == FFS_CLOSING))
-               return -EBUSY;
-
-       file->private_data = ffs;
-       ffs_data_opened(ffs);
-
-       return 0;
-}
-
-static int ffs_ep0_release(struct inode *inode, struct file *file)
-{
-       struct ffs_data *ffs = file->private_data;
-
-       ENTER();
-
-       ffs_data_closed(ffs);
-
-       return 0;
-}
-
-static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value)
-{
-       struct ffs_data *ffs = file->private_data;
-       struct usb_gadget *gadget = ffs->gadget;
-       long ret;
-
-       ENTER();
-
-       if (code == FUNCTIONFS_INTERFACE_REVMAP) {
-               struct ffs_function *func = ffs->func;
-               ret = func ? ffs_func_revmap_intf(func, value) : -ENODEV;
-       } else if (gadget && gadget->ops->ioctl) {
-               ret = gadget->ops->ioctl(gadget, code, value);
-       } else {
-               ret = -ENOTTY;
-       }
-
-       return ret;
-}
-
-static unsigned int ffs_ep0_poll(struct file *file, poll_table *wait)
-{
-       struct ffs_data *ffs = file->private_data;
-       unsigned int mask = POLLWRNORM;
-       int ret;
-
-       poll_wait(file, &ffs->ev.waitq, wait);
-
-       ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
-       if (unlikely(ret < 0))
-               return mask;
-
-       switch (ffs->state) {
-       case FFS_READ_DESCRIPTORS:
-       case FFS_READ_STRINGS:
-               mask |= POLLOUT;
-               break;
-
-       case FFS_ACTIVE:
-               switch (ffs->setup_state) {
-               case FFS_NO_SETUP:
-                       if (ffs->ev.count)
-                               mask |= POLLIN;
-                       break;
-
-               case FFS_SETUP_PENDING:
-               case FFS_SETUP_CANCELLED:
-                       mask |= (POLLIN | POLLOUT);
-                       break;
-               }
-       case FFS_CLOSING:
-               break;
-       }
-
-       mutex_unlock(&ffs->mutex);
-
-       return mask;
-}
-
-static const struct file_operations ffs_ep0_operations = {
-       .llseek =       no_llseek,
-
-       .open =         ffs_ep0_open,
-       .write =        ffs_ep0_write,
-       .read =         ffs_ep0_read,
-       .release =      ffs_ep0_release,
-       .unlocked_ioctl =       ffs_ep0_ioctl,
-       .poll =         ffs_ep0_poll,
-};
-
-
-/* "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 void ffs_user_copy_worker(struct work_struct *work)
-{
-       struct ffs_io_data *io_data = container_of(work, struct ffs_io_data,
-                                                  work);
-       int ret = io_data->req->status ? io_data->req->status :
-                                        io_data->req->actual;
-
-       if (io_data->read && ret > 0) {
-               int i;
-               size_t pos = 0;
-               use_mm(io_data->mm);
-               for (i = 0; i < io_data->nr_segs; i++) {
-                       if (unlikely(copy_to_user(io_data->iovec[i].iov_base,
-                                                &io_data->buf[pos],
-                                                io_data->iovec[i].iov_len))) {
-                               ret = -EFAULT;
-                               break;
-                       }
-                       pos += io_data->iovec[i].iov_len;
-               }
-               unuse_mm(io_data->mm);
-       }
-
-       aio_complete(io_data->kiocb, ret, ret);
-
-       usb_ep_free_request(io_data->ep, io_data->req);
-
-       io_data->kiocb->private = NULL;
-       if (io_data->read)
-               kfree(io_data->iovec);
-       kfree(io_data->buf);
-       kfree(io_data);
-}
-
-static void ffs_epfile_async_io_complete(struct usb_ep *_ep,
-                                        struct usb_request *req)
-{
-       struct ffs_io_data *io_data = req->context;
-
-       ENTER();
-
-       INIT_WORK(&io_data->work, ffs_user_copy_worker);
-       schedule_work(&io_data->work);
-}
-
-static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
-{
-       struct ffs_epfile *epfile = file->private_data;
-       struct ffs_ep *ep;
-       char *data = NULL;
-       ssize_t ret, data_len;
-       int halt;
-
-       /* 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;
-               }
-
-               ret = wait_event_interruptible(epfile->wait, (ep = epfile->ep));
-               if (ret) {
-                       ret = -EINTR;
-                       goto error;
-               }
-       }
-
-       /* Do we halt? */
-       halt = (!io_data->read == !epfile->in);
-       if (halt && epfile->isoc) {
-               ret = -EINVAL;
-               goto error;
-       }
-
-       /* Allocate & copy */
-       if (!halt) {
-               /*
-                * if we _do_ wait above, the epfile->ffs->gadget might be NULL
-                * before the waiting completes, so do not assign to 'gadget' earlier
-                */
-               struct usb_gadget *gadget = epfile->ffs->gadget;
-
-               spin_lock_irq(&epfile->ffs->eps_lock);
-               /* In the meantime, endpoint got disabled or changed. */
-               if (epfile->ep != ep) {
-                       spin_unlock_irq(&epfile->ffs->eps_lock);
-                       return -ESHUTDOWN;
-               }
-               /*
-                * Controller may require buffer size to be aligned to
-                * maxpacketsize of an out endpoint.
-                */
-               data_len = io_data->read ?
-                          usb_ep_align_maybe(gadget, ep->ep, io_data->len) :
-                          io_data->len;
-               spin_unlock_irq(&epfile->ffs->eps_lock);
-
-               data = kmalloc(data_len, GFP_KERNEL);
-               if (unlikely(!data))
-                       return -ENOMEM;
-               if (io_data->aio && !io_data->read) {
-                       int i;
-                       size_t pos = 0;
-                       for (i = 0; i < io_data->nr_segs; i++) {
-                               if (unlikely(copy_from_user(&data[pos],
-                                            io_data->iovec[i].iov_base,
-                                            io_data->iovec[i].iov_len))) {
-                                       ret = -EFAULT;
-                                       goto error;
-                               }
-                               pos += io_data->iovec[i].iov_len;
-                       }
-               } else {
-                       if (!io_data->read &&
-                           unlikely(__copy_from_user(data, io_data->buf,
-                                                     io_data->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;
-
-       spin_lock_irq(&epfile->ffs->eps_lock);
-
-       if (epfile->ep != ep) {
-               /* In the meantime, endpoint got disabled or changed. */
-               ret = -ESHUTDOWN;
-               spin_unlock_irq(&epfile->ffs->eps_lock);
-       } else if (halt) {
-               /* 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 */
-               struct usb_request *req;
-
-               if (io_data->aio) {
-                       req = usb_ep_alloc_request(ep->ep, GFP_KERNEL);
-                       if (unlikely(!req))
-                               goto error_lock;
-
-                       req->buf      = data;
-                       req->length   = io_data->len;
-
-                       io_data->buf = data;
-                       io_data->ep = ep->ep;
-                       io_data->req = req;
-
-                       req->context  = io_data;
-                       req->complete = ffs_epfile_async_io_complete;
-
-                       ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
-                       if (unlikely(ret)) {
-                               usb_ep_free_request(ep->ep, req);
-                               goto error_lock;
-                       }
-                       ret = -EIOCBQUEUED;
-
-                       spin_unlock_irq(&epfile->ffs->eps_lock);
-               } else {
-                       DECLARE_COMPLETION_ONSTACK(done);
-
-                       req = ep->req;
-                       req->buf      = data;
-                       req->length   = io_data->len;
-
-                       req->context  = &done;
-                       req->complete = ffs_epfile_io_complete;
-
-                       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 {
-                               /*
-                                * XXX We may end up silently droping data
-                                * here.  Since data_len (i.e. req->length) may
-                                * be bigger than len (after being rounded up
-                                * to maxpacketsize), we may end up with more
-                                * data then user space has space for.
-                                */
-                               ret = ep->status;
-                               if (io_data->read && ret > 0) {
-                                       ret = min_t(size_t, ret, io_data->len);
-
-                                       if (unlikely(copy_to_user(io_data->buf,
-                                               data, ret)))
-                                               ret = -EFAULT;
-                               }
-                       }
-                       kfree(data);
-               }
-       }
-
-       mutex_unlock(&epfile->mutex);
-       return ret;
-
-error_lock:
-       spin_unlock_irq(&epfile->ffs->eps_lock);
-       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)
-{
-       struct ffs_io_data io_data;
-
-       ENTER();
-
-       io_data.aio = false;
-       io_data.read = false;
-       io_data.buf = (char * __user)buf;
-       io_data.len = len;
-
-       return ffs_epfile_io(file, &io_data);
-}
-
-static ssize_t
-ffs_epfile_read(struct file *file, char __user *buf, size_t len, loff_t *ptr)
-{
-       struct ffs_io_data io_data;
-
-       ENTER();
-
-       io_data.aio = false;
-       io_data.read = true;
-       io_data.buf = buf;
-       io_data.len = len;
-
-       return ffs_epfile_io(file, &io_data);
-}
-
-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_aio_cancel(struct kiocb *kiocb)
-{
-       struct ffs_io_data *io_data = kiocb->private;
-       struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
-       int value;
-
-       ENTER();
-
-       spin_lock_irq(&epfile->ffs->eps_lock);
-
-       if (likely(io_data && io_data->ep && io_data->req))
-               value = usb_ep_dequeue(io_data->ep, io_data->req);
-       else
-               value = -EINVAL;
-
-       spin_unlock_irq(&epfile->ffs->eps_lock);
-
-       return value;
-}
-
-static ssize_t ffs_epfile_aio_write(struct kiocb *kiocb,
-                                   const struct iovec *iovec,
-                                   unsigned long nr_segs, loff_t loff)
-{
-       struct ffs_io_data *io_data;
-
-       ENTER();
-
-       io_data = kmalloc(sizeof(*io_data), GFP_KERNEL);
-       if (unlikely(!io_data))
-               return -ENOMEM;
-
-       io_data->aio = true;
-       io_data->read = false;
-       io_data->kiocb = kiocb;
-       io_data->iovec = iovec;
-       io_data->nr_segs = nr_segs;
-       io_data->len = kiocb->ki_nbytes;
-       io_data->mm = current->mm;
-
-       kiocb->private = io_data;
-
-       kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
-
-       return ffs_epfile_io(kiocb->ki_filp, io_data);
-}
-
-static ssize_t ffs_epfile_aio_read(struct kiocb *kiocb,
-                                  const struct iovec *iovec,
-                                  unsigned long nr_segs, loff_t loff)
-{
-       struct ffs_io_data *io_data;
-       struct iovec *iovec_copy;
-
-       ENTER();
-
-       iovec_copy = kmalloc_array(nr_segs, sizeof(*iovec_copy), GFP_KERNEL);
-       if (unlikely(!iovec_copy))
-               return -ENOMEM;
-
-       memcpy(iovec_copy, iovec, sizeof(struct iovec)*nr_segs);
-
-       io_data = kmalloc(sizeof(*io_data), GFP_KERNEL);
-       if (unlikely(!io_data)) {
-               kfree(iovec_copy);
-               return -ENOMEM;
-       }
-
-       io_data->aio = true;
-       io_data->read = true;
-       io_data->kiocb = kiocb;
-       io_data->iovec = iovec_copy;
-       io_data->nr_segs = nr_segs;
-       io_data->len = kiocb->ki_nbytes;
-       io_data->mm = current->mm;
-
-       kiocb->private = io_data;
-
-       kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
-
-       return ffs_epfile_io(kiocb->ki_filp, io_data);
-}
-
-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 = {
-       .llseek =       no_llseek,
-
-       .open =         ffs_epfile_open,
-       .write =        ffs_epfile_write,
-       .read =         ffs_epfile_read,
-       .aio_write =    ffs_epfile_aio_write,
-       .aio_read =     ffs_epfile_aio_read,
-       .release =      ffs_epfile_release,
-       .unlocked_ioctl =       ffs_epfile_ioctl,
-};
-
-
-/* File system and super block operations ***********************************/
-
-/*
- * Mounting the file system 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_ino     = get_next_ino();
-               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;
-       struct ffs_data *ffs_data;
-};
-
-static int ffs_sb_fill(struct super_block *sb, void *_data, int silent)
-{
-       struct ffs_sb_fill_data *data = _data;
-       struct inode    *inode;
-       struct ffs_data *ffs = data->ffs_data;
-
-       ENTER();
-
-       ffs->sb              = sb;
-       data->ffs_data       = NULL;
-       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);
-       sb->s_root = d_make_root(inode);
-       if (unlikely(!sb->s_root))
-               return -ENOMEM;
-
-       /* EP0 file */
-       if (unlikely(!ffs_sb_create_file(sb, "ep0", ffs,
-                                        &ffs_ep0_operations, NULL)))
-               return -ENOMEM;
-
-       return 0;
-}
-
-static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
-{
-       ENTER();
-
-       if (!opts || !*opts)
-               return 0;
-
-       for (;;) {
-               unsigned long value;
-               char *eq, *comma;
-
-               /* Option limit */
-               comma = strchr(opts, ',');
-               if (comma)
-                       *comma = 0;
-
-               /* Value limit */
-               eq = strchr(opts, '=');
-               if (unlikely(!eq)) {
-                       pr_err("'=' missing in %s\n", opts);
-                       return -EINVAL;
-               }
-               *eq = 0;
-
-               /* Parse value */
-               if (kstrtoul(eq + 1, 0, &value)) {
-                       pr_err("%s: invalid value: %s\n", 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 = make_kuid(current_user_ns(), value);
-                               if (!uid_valid(data->perms.uid)) {
-                                       pr_err("%s: unmapped value: %lu\n", opts, value);
-                                       return -EINVAL;
-                               }
-                       } else if (!memcmp(opts, "gid", 3)) {
-                               data->perms.gid = make_kgid(current_user_ns(), value);
-                               if (!gid_valid(data->perms.gid)) {
-                                       pr_err("%s: unmapped value: %lu\n", opts, value);
-                                       return -EINVAL;
-                               }
-                       } else {
-                               goto invalid;
-                       }
-                       break;
-
-               default:
-invalid:
-                       pr_err("%s: invalid option\n", opts);
-                       return -EINVAL;
-               }
-
-               /* Next iteration */
-               if (!comma)
-                       break;
-               opts = comma + 1;
-       }
-
-       return 0;
-}
-
-/* "mount -t functionfs dev_name /dev/function" ends up here */
-
-static struct dentry *
-ffs_fs_mount(struct file_system_type *t, int flags,
-             const char *dev_name, void *opts)
-{
-       struct ffs_sb_fill_data data = {
-               .perms = {
-                       .mode = S_IFREG | 0600,
-                       .uid = GLOBAL_ROOT_UID,
-                       .gid = GLOBAL_ROOT_GID,
-               },
-               .root_mode = S_IFDIR | 0500,
-       };
-       struct dentry *rv;
-       int ret;
-       void *ffs_dev;
-       struct ffs_data *ffs;
-
-       ENTER();
-
-       ret = ffs_fs_parse_opts(&data, opts);
-       if (unlikely(ret < 0))
-               return ERR_PTR(ret);
-
-       ffs = ffs_data_new();
-       if (unlikely(!ffs))
-               return ERR_PTR(-ENOMEM);
-       ffs->file_perms = data.perms;
-
-       ffs->dev_name = kstrdup(dev_name, GFP_KERNEL);
-       if (unlikely(!ffs->dev_name)) {
-               ffs_data_put(ffs);
-               return ERR_PTR(-ENOMEM);
-       }
-
-       ffs_dev = ffs_acquire_dev(dev_name);
-       if (IS_ERR(ffs_dev)) {
-               ffs_data_put(ffs);
-               return ERR_CAST(ffs_dev);
-       }
-       ffs->private_data = ffs_dev;
-       data.ffs_data = ffs;
-
-       rv = mount_nodev(t, flags, &data, ffs_sb_fill);
-       if (IS_ERR(rv) && data.ffs_data) {
-               ffs_release_dev(data.ffs_data);
-               ffs_data_put(data.ffs_data);
-       }
-       return rv;
-}
-
-static void
-ffs_fs_kill_sb(struct super_block *sb)
-{
-       ENTER();
-
-       kill_litter_super(sb);
-       if (sb->s_fs_info) {
-               ffs_release_dev(sb->s_fs_info);
-               ffs_data_put(sb->s_fs_info);
-       }
-}
-
-static struct file_system_type ffs_fs_type = {
-       .owner          = THIS_MODULE,
-       .name           = "functionfs",
-       .mount          = ffs_fs_mount,
-       .kill_sb        = ffs_fs_kill_sb,
-};
-MODULE_ALIAS_FS("functionfs");
-
-
-/* Driver's main init/cleanup functions *************************************/
-
-static int functionfs_init(void)
-{
-       int ret;
-
-       ENTER();
-
-       ret = register_filesystem(&ffs_fs_type);
-       if (likely(!ret))
-               pr_info("file system registered\n");
-       else
-               pr_err("failed registering file system (%d)\n", ret);
-
-       return ret;
-}
-
-static void functionfs_cleanup(void)
-{
-       ENTER();
-
-       pr_info("unloading\n");
-       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))) {
-               pr_info("%s(): freeing\n", __func__);
-               ffs_data_clear(ffs);
-               BUG_ON(waitqueue_active(&ffs->ev.waitq) ||
-                      waitqueue_active(&ffs->ep0req_completion.wait));
-               kfree(ffs->dev_name);
-               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 NULL;
-
-       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))
-               ffs_closed(ffs);
-
-       BUG_ON(ffs->gadget);
-
-       if (ffs->epfiles)
-               ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count);
-
-       kfree(ffs->raw_descs_data);
-       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_data = NULL;
-       ffs->raw_descs = NULL;
-       ffs->raw_strings = NULL;
-       ffs->stringtabs = NULL;
-
-       ffs->raw_descs_length = 0;
-       ffs->fs_descs_count = 0;
-       ffs->hs_descs_count = 0;
-       ffs->ss_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)
-{
-       struct usb_gadget_strings **lang;
-       int first_id;
-
-       ENTER();
-
-       if (WARN_ON(ffs->state != FFS_ACTIVE
-                || test_and_set_bit(FFS_FL_BOUND, &ffs->flags)))
-               return -EBADFD;
-
-       first_id = usb_string_ids_n(cdev, ffs->strings_count);
-       if (unlikely(first_id < 0))
-               return first_id;
-
-       ffs->ep0req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL);
-       if (unlikely(!ffs->ep0req))
-               return -ENOMEM;
-       ffs->ep0req->complete = ffs_ep0_complete;
-       ffs->ep0req->context = ffs;
-
-       lang = ffs->stringtabs;
-       for (lang = ffs->stringtabs; *lang; ++lang) {
-               struct usb_string *str = (*lang)->strings;
-               int id = first_id;
-               for (; str->s; ++id, ++str)
-                       str->id = id;
-       }
-
-       ffs->gadget = cdev->gadget;
-       ffs_data_get(ffs);
-       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;
-               clear_bit(FFS_FL_BOUND, &ffs->flags);
-               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 = kcalloc(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 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;
-               int desc_idx;
-
-               if (ffs->gadget->speed == USB_SPEED_SUPER)
-                       desc_idx = 2;
-               else if (ffs->gadget->speed == USB_SPEED_HIGH)
-                       desc_idx = 1;
-               else
-                       desc_idx = 0;
-
-               /* fall-back to lower speed if desc missing for current speed */
-               do {
-                       ds = ep->descs[desc_idx];
-               } while (!ds && --desc_idx >= 0);
-
-               if (!ds) {
-                       ret = -EINVAL;
-                       break;
-               }
-
-               ep->ep->driver_data = ep;
-               ep->ep->desc = ds;
-               ret = usb_ep_enable(ep->ep);
-               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 after the
- * descriptor or NULL if data is invalid.
- */
-
-enum ffs_entity_type {
-       FFS_DESCRIPTOR, FFS_INTERFACE, FFS_STRING, FFS_ENDPOINT
-};
-
-enum ffs_os_desc_type {
-       FFS_OS_DESC, FFS_OS_DESC_EXT_COMPAT, FFS_OS_DESC_EXT_PROP
-};
-
-typedef int (*ffs_entity_callback)(enum ffs_entity_type entity,
-                                  u8 *valuep,
-                                  struct usb_descriptor_header *desc,
-                                  void *priv);
-
-typedef int (*ffs_os_desc_callback)(enum ffs_os_desc_type entity,
-                                   struct usb_os_desc_header *h, void *data,
-                                   unsigned len, void *priv);
-
-static int __must_check ffs_do_single_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) {
-               pr_vdebug("descriptor too short\n");
-               return -EINVAL;
-       }
-
-       /* If we have at least as many bytes as the descriptor takes? */
-       length = _ds->bLength;
-       if (len < length) {
-               pr_vdebug("descriptor longer then available data\n");
-               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 {                                       \
-               pr_vdebug("entity " #type "(%02x)\n", (val));           \
-               if (unlikely(!__entity_check_ ##type(val))) {           \
-                       pr_vdebug("invalid entity's value\n");          \
-                       return -EINVAL;                                 \
-               }                                                       \
-               ret = entity(FFS_ ##type, &val, _ds, priv);             \
-               if (unlikely(ret < 0)) {                                \
-                       pr_debug("entity " #type "(%02x); ret = %d\n",  \
-                                (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 */
-               pr_vdebug("descriptor reserved for gadget: %d\n",
-                     _ds->bDescriptorType);
-               return -EINVAL;
-
-       case USB_DT_INTERFACE: {
-               struct usb_interface_descriptor *ds = (void *)_ds;
-               pr_vdebug("interface descriptor\n");
-               if (length != sizeof *ds)
-                       goto inv_length;
-
-               __entity(INTERFACE, ds->bInterfaceNumber);
-               if (ds->iInterface)
-                       __entity(STRING, ds->iInterface);
-       }
-               break;
-
-       case USB_DT_ENDPOINT: {
-               struct usb_endpoint_descriptor *ds = (void *)_ds;
-               pr_vdebug("endpoint descriptor\n");
-               if (length != USB_DT_ENDPOINT_SIZE &&
-                   length != USB_DT_ENDPOINT_AUDIO_SIZE)
-                       goto inv_length;
-               __entity(ENDPOINT, ds->bEndpointAddress);
-       }
-               break;
-
-       case HID_DT_HID:
-               pr_vdebug("hid descriptor\n");
-               if (length != sizeof(struct hid_descriptor))
-                       goto inv_length;
-               break;
-
-       case USB_DT_OTG:
-               if (length != sizeof(struct usb_otg_descriptor))
-                       goto inv_length;
-               break;
-
-       case USB_DT_INTERFACE_ASSOCIATION: {
-               struct usb_interface_assoc_descriptor *ds = (void *)_ds;
-               pr_vdebug("interface association descriptor\n");
-               if (length != sizeof *ds)
-                       goto inv_length;
-               if (ds->iFunction)
-                       __entity(STRING, ds->iFunction);
-       }
-               break;
-
-       case USB_DT_SS_ENDPOINT_COMP:
-               pr_vdebug("EP SS companion descriptor\n");
-               if (length != sizeof(struct usb_ss_ep_comp_descriptor))
-                       goto inv_length;
-               break;
-
-       case USB_DT_OTHER_SPEED_CONFIG:
-       case USB_DT_INTERFACE_POWER:
-       case USB_DT_DEBUG:
-       case USB_DT_SECURITY:
-       case USB_DT_CS_RADIO_CONTROL:
-               /* TODO */
-               pr_vdebug("unimplemented descriptor: %d\n", _ds->bDescriptorType);
-               return -EINVAL;
-
-       default:
-               /* We should never be here */
-               pr_vdebug("unknown descriptor: %d\n", _ds->bDescriptorType);
-               return -EINVAL;
-
-inv_length:
-               pr_vdebug("invalid length: %d (descriptor %d)\n",
-                         _ds->bLength, _ds->bDescriptorType);
-               return -EINVAL;
-       }
-
-#undef __entity
-#undef __entity_check_DESCRIPTOR
-#undef __entity_check_INTERFACE
-#undef __entity_check_STRING
-#undef __entity_check_ENDPOINT
-
-       return length;
-}
-
-static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
-                                    ffs_entity_callback entity, void *priv)
-{
-       const unsigned _len = len;
-       unsigned long num = 0;
-
-       ENTER();
-
-       for (;;) {
-               int ret;
-
-               if (num == count)
-                       data = NULL;
-
-               /* Record "descriptor" entity */
-               ret = entity(FFS_DESCRIPTOR, (u8 *)num, (void *)data, priv);
-               if (unlikely(ret < 0)) {
-                       pr_debug("entity DESCRIPTOR(%02lx); ret = %d\n",
-                                num, ret);
-                       return ret;
-               }
-
-               if (!data)
-                       return _len - len;
-
-               ret = ffs_do_single_desc(data, len, entity, priv);
-               if (unlikely(ret < 0)) {
-                       pr_debug("%s returns %d\n", __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_do_os_desc_header(enum ffs_os_desc_type *next_type,
-                                  struct usb_os_desc_header *desc)
-{
-       u16 bcd_version = le16_to_cpu(desc->bcdVersion);
-       u16 w_index = le16_to_cpu(desc->wIndex);
-
-       if (bcd_version != 1) {
-               pr_vdebug("unsupported os descriptors version: %d",
-                         bcd_version);
-               return -EINVAL;
-       }
-       switch (w_index) {
-       case 0x4:
-               *next_type = FFS_OS_DESC_EXT_COMPAT;
-               break;
-       case 0x5:
-               *next_type = FFS_OS_DESC_EXT_PROP;
-               break;
-       default:
-               pr_vdebug("unsupported os descriptor type: %d", w_index);
-               return -EINVAL;
-       }
-
-       return sizeof(*desc);
-}
-
-/*
- * Process all extended compatibility/extended property descriptors
- * of a feature descriptor
- */
-static int __must_check ffs_do_single_os_desc(char *data, unsigned len,
-                                             enum ffs_os_desc_type type,
-                                             u16 feature_count,
-                                             ffs_os_desc_callback entity,
-                                             void *priv,
-                                             struct usb_os_desc_header *h)
-{
-       int ret;
-       const unsigned _len = len;
-
-       ENTER();
-
-       /* loop over all ext compat/ext prop descriptors */
-       while (feature_count--) {
-               ret = entity(type, h, data, len, priv);
-               if (unlikely(ret < 0)) {
-                       pr_debug("bad OS descriptor, type: %d\n", type);
-                       return ret;
-               }
-               data += ret;
-               len -= ret;
-       }
-       return _len - len;
-}
-
-/* Process a number of complete Feature Descriptors (Ext Compat or Ext Prop) */
-static int __must_check ffs_do_os_descs(unsigned count,
-                                       char *data, unsigned len,
-                                       ffs_os_desc_callback entity, void *priv)
-{
-       const unsigned _len = len;
-       unsigned long num = 0;
-
-       ENTER();
-
-       for (num = 0; num < count; ++num) {
-               int ret;
-               enum ffs_os_desc_type type;
-               u16 feature_count;
-               struct usb_os_desc_header *desc = (void *)data;
-
-               if (len < sizeof(*desc))
-                       return -EINVAL;
-
-               /*
-                * Record "descriptor" entity.
-                * Process dwLength, bcdVersion, wIndex, get b/wCount.
-                * Move the data pointer to the beginning of extended
-                * compatibilities proper or extended properties proper
-                * portions of the data
-                */
-               if (le32_to_cpu(desc->dwLength) > len)
-                       return -EINVAL;
-
-               ret = __ffs_do_os_desc_header(&type, desc);
-               if (unlikely(ret < 0)) {
-                       pr_debug("entity OS_DESCRIPTOR(%02lx); ret = %d\n",
-                                num, ret);
-                       return ret;
-               }
-               /*
-                * 16-bit hex "?? 00" Little Endian looks like 8-bit hex "??"
-                */
-               feature_count = le16_to_cpu(desc->wCount);
-               if (type == FFS_OS_DESC_EXT_COMPAT &&
-                   (feature_count > 255 || desc->Reserved))
-                               return -EINVAL;
-               len -= ret;
-               data += ret;
-
-               /*
-                * Process all function/property descriptors
-                * of this Feature Descriptor
-                */
-               ret = ffs_do_single_os_desc(data, len, type,
-                                           feature_count, entity, priv, desc);
-               if (unlikely(ret < 0)) {
-                       pr_debug("%s returns %d\n", __func__, ret);
-                       return ret;
-               }
-
-               len -= ret;
-               data += ret;
-       }
-       return _len - len;
-}
-
-/**
- * Validate contents of the buffer from userspace related to OS descriptors.
- */
-static int __ffs_data_do_os_desc(enum ffs_os_desc_type type,
-                                struct usb_os_desc_header *h, void *data,
-                                unsigned len, void *priv)
-{
-       struct ffs_data *ffs = priv;
-       u8 length;
-
-       ENTER();
-
-       switch (type) {
-       case FFS_OS_DESC_EXT_COMPAT: {
-               struct usb_ext_compat_desc *d = data;
-               int i;
-
-               if (len < sizeof(*d) ||
-                   d->bFirstInterfaceNumber >= ffs->interfaces_count ||
-                   d->Reserved1)
-                       return -EINVAL;
-               for (i = 0; i < ARRAY_SIZE(d->Reserved2); ++i)
-                       if (d->Reserved2[i])
-                               return -EINVAL;
-
-               length = sizeof(struct usb_ext_compat_desc);
-       }
-               break;
-       case FFS_OS_DESC_EXT_PROP: {
-               struct usb_ext_prop_desc *d = data;
-               u32 type, pdl;
-               u16 pnl;
-
-               if (len < sizeof(*d) || h->interface >= ffs->interfaces_count)
-                       return -EINVAL;
-               length = le32_to_cpu(d->dwSize);
-               type = le32_to_cpu(d->dwPropertyDataType);
-               if (type < USB_EXT_PROP_UNICODE ||
-                   type > USB_EXT_PROP_UNICODE_MULTI) {
-                       pr_vdebug("unsupported os descriptor property type: %d",
-                                 type);
-                       return -EINVAL;
-               }
-               pnl = le16_to_cpu(d->wPropertyNameLength);
-               pdl = le32_to_cpu(*(u32 *)((u8 *)data + 10 + pnl));
-               if (length != 14 + pnl + pdl) {
-                       pr_vdebug("invalid os descriptor length: %d pnl:%d pdl:%d (descriptor %d)\n",
-                                 length, pnl, pdl, type);
-                       return -EINVAL;
-               }
-               ++ffs->ms_os_descs_ext_prop_count;
-               /* property name reported to the host as "WCHAR"s */
-               ffs->ms_os_descs_ext_prop_name_len += pnl * 2;
-               ffs->ms_os_descs_ext_prop_data_len += pdl;
-       }
-               break;
-       default:
-               pr_vdebug("unknown descriptor: %d\n", type);
-               return -EINVAL;
-       }
-       return length;
-}
-
-static int __ffs_data_got_descs(struct ffs_data *ffs,
-                               char *const _data, size_t len)
-{
-       char *data = _data, *raw_descs;
-       unsigned os_descs_count = 0, counts[3], flags;
-       int ret = -EINVAL, i;
-
-       ENTER();
-
-       if (get_unaligned_le32(data + 4) != len)
-               goto error;
-
-       switch (get_unaligned_le32(data)) {
-       case FUNCTIONFS_DESCRIPTORS_MAGIC:
-               flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC;
-               data += 8;
-               len  -= 8;
-               break;
-       case FUNCTIONFS_DESCRIPTORS_MAGIC_V2:
-               flags = get_unaligned_le32(data + 8);
-               if (flags & ~(FUNCTIONFS_HAS_FS_DESC |
-                             FUNCTIONFS_HAS_HS_DESC |
-                             FUNCTIONFS_HAS_SS_DESC |
-                             FUNCTIONFS_HAS_MS_OS_DESC)) {
-                       ret = -ENOSYS;
-                       goto error;
-               }
-               data += 12;
-               len  -= 12;
-               break;
-       default:
-               goto error;
-       }
-
-       /* Read fs_count, hs_count and ss_count (if present) */
-       for (i = 0; i < 3; ++i) {
-               if (!(flags & (1 << i))) {
-                       counts[i] = 0;
-               } else if (len < 4) {
-                       goto error;
-               } else {
-                       counts[i] = get_unaligned_le32(data);
-                       data += 4;
-                       len  -= 4;
-               }
-       }
-       if (flags & (1 << i)) {
-               os_descs_count = get_unaligned_le32(data);
-               data += 4;
-               len -= 4;
-       };
-
-       /* Read descriptors */
-       raw_descs = data;
-       for (i = 0; i < 3; ++i) {
-               if (!counts[i])
-                       continue;
-               ret = ffs_do_descs(counts[i], data, len,
-                                  __ffs_data_do_entity, ffs);
-               if (ret < 0)
-                       goto error;
-               data += ret;
-               len  -= ret;
-       }
-       if (os_descs_count) {
-               ret = ffs_do_os_descs(os_descs_count, data, len,
-                                     __ffs_data_do_os_desc, ffs);
-               if (ret < 0)
-                       goto error;
-               data += ret;
-               len -= ret;
-       }
-
-       if (raw_descs == data || len) {
-               ret = -EINVAL;
-               goto error;
-       }
-
-       ffs->raw_descs_data     = _data;
-       ffs->raw_descs          = raw_descs;
-       ffs->raw_descs_length   = data - raw_descs;
-       ffs->fs_descs_count     = counts[0];
-       ffs->hs_descs_count     = counts[1];
-       ffs->ss_descs_count     = counts[2];
-       ffs->ms_os_descs_count  = os_descs_count;
-
-       return 0;
-
-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 everything in one chunk so there's less maintenance. */
-       {
-               unsigned i = 0;
-               vla_group(d);
-               vla_item(d, struct usb_gadget_strings *, stringtabs,
-                       lang_count + 1);
-               vla_item(d, struct usb_gadget_strings, stringtab, lang_count);
-               vla_item(d, struct usb_string, strings,
-                       lang_count*(needed_count+1));
-
-               char *vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL);
-
-               if (unlikely(!vlabuf)) {
-                       kfree(_data);
-                       return -ENOMEM;
-               }
-
-               /* Initialize the VLA pointers */
-               stringtabs = vla_ptr(vlabuf, d, stringtabs);
-               t = vla_ptr(vlabuf, d, stringtab);
-               i = lang_count;
-               do {
-                       *stringtabs++ = t++;
-               } while (--i);
-               *stringtabs = NULL;
-
-               /* stringtabs = vlabuf = d_stringtabs for later kfree */
-               stringtabs = vla_ptr(vlabuf, d, stringtabs);
-               t = vla_ptr(vlabuf, d, stringtab);
-               s = vla_ptr(vlabuf, 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 ignore 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, rem_type2 = type;
-       int neg = 0;
-
-       /*
-        * Abort any unhandled setup
-        *
-        * We do not need to worry about some cmpxchg() changing value
-        * of ffs->setup_state without holding the lock because when
-        * state is FFS_SETUP_PENDING cmpxchg() in several places in
-        * the source does nothing.
-        */
-       if (ffs->setup_state == FFS_SETUP_PENDING)
-               ffs->setup_state = FFS_SETUP_CANCELLED;
-
-       switch (type) {
-       case FUNCTIONFS_RESUME:
-               rem_type2 = FUNCTIONFS_SUSPEND;
-               /* FALL THROUGH */
-       case FUNCTIONFS_SUSPEND:
-       case FUNCTIONFS_SETUP:
-               rem_type1 = type;
-               /* 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;
-                       else
-                               pr_vdebug("purging event %d\n", *ev);
-               ffs->ev.count = out - ffs->ev.types;
-       }
-
-       pr_vdebug("adding event %d\n", type);
-       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;
-       unsigned ep_desc_id, idx;
-       static const char *speed_names[] = { "full", "high", "super" };
-
-       if (type != FFS_DESCRIPTOR)
-               return 0;
-
-       /*
-        * If ss_descriptors is not NULL, we are reading super speed
-        * descriptors; if hs_descriptors is not NULL, we are reading high
-        * speed descriptors; otherwise, we are reading full speed
-        * descriptors.
-        */
-       if (func->function.ss_descriptors) {
-               ep_desc_id = 2;
-               func->function.ss_descriptors[(long)valuep] = desc;
-       } else if (func->function.hs_descriptors) {
-               ep_desc_id = 1;
-               func->function.hs_descriptors[(long)valuep] = desc;
-       } else {
-               ep_desc_id = 0;
-               func->function.fs_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[ep_desc_id])) {
-               pr_err("two %sspeed descriptors for EP %d\n",
-                         speed_names[ep_desc_id],
-                         ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
-               return -EINVAL;
-       }
-       ffs_ep->descs[ep_desc_id] = 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;
-
-               pr_vdebug("autoconfig\n");
-               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;
-       }
-
-       pr_vdebug("%02x -> %02x\n", *valuep, newValue);
-       *valuep = newValue;
-       return 0;
-}
-
-static int __ffs_func_bind_do_os_desc(enum ffs_os_desc_type type,
-                                     struct usb_os_desc_header *h, void *data,
-                                     unsigned len, void *priv)
-{
-       struct ffs_function *func = priv;
-       u8 length = 0;
-
-       switch (type) {
-       case FFS_OS_DESC_EXT_COMPAT: {
-               struct usb_ext_compat_desc *desc = data;
-               struct usb_os_desc_table *t;
-
-               t = &func->function.os_desc_table[desc->bFirstInterfaceNumber];
-               t->if_id = func->interfaces_nums[desc->bFirstInterfaceNumber];
-               memcpy(t->os_desc->ext_compat_id, &desc->CompatibleID,
-                      ARRAY_SIZE(desc->CompatibleID) +
-                      ARRAY_SIZE(desc->SubCompatibleID));
-               length = sizeof(*desc);
-       }
-               break;
-       case FFS_OS_DESC_EXT_PROP: {
-               struct usb_ext_prop_desc *desc = data;
-               struct usb_os_desc_table *t;
-               struct usb_os_desc_ext_prop *ext_prop;
-               char *ext_prop_name;
-               char *ext_prop_data;
-
-               t = &func->function.os_desc_table[h->interface];
-               t->if_id = func->interfaces_nums[h->interface];
-
-               ext_prop = func->ffs->ms_os_descs_ext_prop_avail;
-               func->ffs->ms_os_descs_ext_prop_avail += sizeof(*ext_prop);
-
-               ext_prop->type = le32_to_cpu(desc->dwPropertyDataType);
-               ext_prop->name_len = le16_to_cpu(desc->wPropertyNameLength);
-               ext_prop->data_len = le32_to_cpu(*(u32 *)
-                       usb_ext_prop_data_len_ptr(data, ext_prop->name_len));
-               length = ext_prop->name_len + ext_prop->data_len + 14;
-
-               ext_prop_name = func->ffs->ms_os_descs_ext_prop_name_avail;
-               func->ffs->ms_os_descs_ext_prop_name_avail +=
-                       ext_prop->name_len;
-
-               ext_prop_data = func->ffs->ms_os_descs_ext_prop_data_avail;
-               func->ffs->ms_os_descs_ext_prop_data_avail +=
-                       ext_prop->data_len;
-               memcpy(ext_prop_data,
-                      usb_ext_prop_data_ptr(data, ext_prop->name_len),
-                      ext_prop->data_len);
-               /* unicode data reported to the host as "WCHAR"s */
-               switch (ext_prop->type) {
-               case USB_EXT_PROP_UNICODE:
-               case USB_EXT_PROP_UNICODE_ENV:
-               case USB_EXT_PROP_UNICODE_LINK:
-               case USB_EXT_PROP_UNICODE_MULTI:
-                       ext_prop->data_len *= 2;
-                       break;
-               }
-               ext_prop->data = ext_prop_data;
-
-               memcpy(ext_prop_name, usb_ext_prop_name_ptr(data),
-                      ext_prop->name_len);
-               /* property name reported to the host as "WCHAR"s */
-               ext_prop->name_len *= 2;
-               ext_prop->name = ext_prop_name;
-
-               t->os_desc->ext_prop_len +=
-                       ext_prop->name_len + ext_prop->data_len + 14;
-               ++t->os_desc->ext_prop_count;
-               list_add_tail(&ext_prop->entry, &t->os_desc->ext_prop);
-       }
-               break;
-       default:
-               pr_vdebug("unknown descriptor: %d\n", type);
-       }
-
-       return length;
-}
-
-static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f,
-                                               struct usb_configuration *c)
-{
-       struct ffs_function *func = ffs_func_from_usb(f);
-       struct f_fs_opts *ffs_opts =
-               container_of(f->fi, struct f_fs_opts, func_inst);
-       int ret;
-
-       ENTER();
-
-       /*
-        * Legacy gadget triggers binding in functionfs_ready_callback,
-        * which already uses locking; taking the same lock here would
-        * cause a deadlock.
-        *
-        * Configfs-enabled gadgets however do need ffs_dev_lock.
-        */
-       if (!ffs_opts->no_configfs)
-               ffs_dev_lock();
-       ret = ffs_opts->dev->desc_ready ? 0 : -ENODEV;
-       func->ffs = ffs_opts->dev->ffs_data;
-       if (!ffs_opts->no_configfs)
-               ffs_dev_unlock();
-       if (ret)
-               return ERR_PTR(ret);
-
-       func->conf = c;
-       func->gadget = c->cdev->gadget;
-
-       ffs_data_get(func->ffs);
-
-       /*
-        * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
-        * configurations are bound in sequence with list_for_each_entry,
-        * in each configuration its functions are bound in sequence
-        * with list_for_each_entry, so we assume no race condition
-        * with regard to ffs_opts->bound access
-        */
-       if (!ffs_opts->refcnt) {
-               ret = functionfs_bind(func->ffs, c->cdev);
-               if (ret)
-                       return ERR_PTR(ret);
-       }
-       ffs_opts->refcnt++;
-       func->function.strings = func->ffs->stringtabs;
-
-       return ffs_opts;
-}
-
-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;
-       const int super = gadget_is_superspeed(func->gadget) &&
-               func->ffs->ss_descs_count;
-
-       int fs_len, hs_len, ss_len, ret, i;
-
-       /* Make it a single chunk, less management later on */
-       vla_group(d);
-       vla_item_with_sz(d, struct ffs_ep, eps, ffs->eps_count);
-       vla_item_with_sz(d, struct usb_descriptor_header *, fs_descs,
-               full ? ffs->fs_descs_count + 1 : 0);
-       vla_item_with_sz(d, struct usb_descriptor_header *, hs_descs,
-               high ? ffs->hs_descs_count + 1 : 0);
-       vla_item_with_sz(d, struct usb_descriptor_header *, ss_descs,
-               super ? ffs->ss_descs_count + 1 : 0);
-       vla_item_with_sz(d, short, inums, ffs->interfaces_count);
-       vla_item_with_sz(d, struct usb_os_desc_table, os_desc_table,
-                        c->cdev->use_os_string ? ffs->interfaces_count : 0);
-       vla_item_with_sz(d, char[16], ext_compat,
-                        c->cdev->use_os_string ? ffs->interfaces_count : 0);
-       vla_item_with_sz(d, struct usb_os_desc, os_desc,
-                        c->cdev->use_os_string ? ffs->interfaces_count : 0);
-       vla_item_with_sz(d, struct usb_os_desc_ext_prop, ext_prop,
-                        ffs->ms_os_descs_ext_prop_count);
-       vla_item_with_sz(d, char, ext_prop_name,
-                        ffs->ms_os_descs_ext_prop_name_len);
-       vla_item_with_sz(d, char, ext_prop_data,
-                        ffs->ms_os_descs_ext_prop_data_len);
-       vla_item_with_sz(d, char, raw_descs, ffs->raw_descs_length);
-       char *vlabuf;
-
-       ENTER();
-
-       /* Has descriptors only for speeds gadget does not support */
-       if (unlikely(!(full | high | super)))
-               return -ENOTSUPP;
-
-       /* Allocate a single chunk, less management later on */
-       vlabuf = kzalloc(vla_group_size(d), GFP_KERNEL);
-       if (unlikely(!vlabuf))
-               return -ENOMEM;
-
-       ffs->ms_os_descs_ext_prop_avail = vla_ptr(vlabuf, d, ext_prop);
-       ffs->ms_os_descs_ext_prop_name_avail =
-               vla_ptr(vlabuf, d, ext_prop_name);
-       ffs->ms_os_descs_ext_prop_data_avail =
-               vla_ptr(vlabuf, d, ext_prop_data);
-
-       /* Copy descriptors  */
-       memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs,
-              ffs->raw_descs_length);
-
-       memset(vla_ptr(vlabuf, d, inums), 0xff, d_inums__sz);
-       for (ret = ffs->eps_count; ret; --ret) {
-               struct ffs_ep *ptr;
-
-               ptr = vla_ptr(vlabuf, d, eps);
-               ptr[ret].num = -1;
-       }
-
-       /* Save pointers
-        * d_eps == vlabuf, func->eps used to kfree vlabuf later
-       */
-       func->eps             = vla_ptr(vlabuf, d, eps);
-       func->interfaces_nums = vla_ptr(vlabuf, d, inums);
-
-       /*
-        * Go through all the endpoint descriptors and allocate
-        * endpoints first, so that later we can rewrite the endpoint
-        * numbers without worrying that it may be described later on.
-        */
-       if (likely(full)) {
-               func->function.fs_descriptors = vla_ptr(vlabuf, d, fs_descs);
-               fs_len = ffs_do_descs(ffs->fs_descs_count,
-                                     vla_ptr(vlabuf, d, raw_descs),
-                                     d_raw_descs__sz,
-                                     __ffs_func_bind_do_descs, func);
-               if (unlikely(fs_len < 0)) {
-                       ret = fs_len;
-                       goto error;
-               }
-       } else {
-               fs_len = 0;
-       }
-
-       if (likely(high)) {
-               func->function.hs_descriptors = vla_ptr(vlabuf, d, hs_descs);
-               hs_len = ffs_do_descs(ffs->hs_descs_count,
-                                     vla_ptr(vlabuf, d, raw_descs) + fs_len,
-                                     d_raw_descs__sz - fs_len,
-                                     __ffs_func_bind_do_descs, func);
-               if (unlikely(hs_len < 0)) {
-                       ret = hs_len;
-                       goto error;
-               }
-       } else {
-               hs_len = 0;
-       }
-
-       if (likely(super)) {
-               func->function.ss_descriptors = vla_ptr(vlabuf, d, ss_descs);
-               ss_len = ffs_do_descs(ffs->ss_descs_count,
-                               vla_ptr(vlabuf, d, raw_descs) + fs_len + hs_len,
-                               d_raw_descs__sz - fs_len - hs_len,
-                               __ffs_func_bind_do_descs, func);
-               if (unlikely(ss_len < 0)) {
-                       ret = ss_len;
-                       goto error;
-               }
-       } else {
-               ss_len = 0;
-       }
-
-       /*
-        * Now handle interface numbers allocation and interface and
-        * endpoint numbers rewriting.  We can do that in one go
-        * now.
-        */
-       ret = ffs_do_descs(ffs->fs_descs_count +
-                          (high ? ffs->hs_descs_count : 0) +
-                          (super ? ffs->ss_descs_count : 0),
-                          vla_ptr(vlabuf, d, raw_descs), d_raw_descs__sz,
-                          __ffs_func_bind_do_nums, func);
-       if (unlikely(ret < 0))
-               goto error;
-
-       func->function.os_desc_table = vla_ptr(vlabuf, d, os_desc_table);
-       if (c->cdev->use_os_string)
-               for (i = 0; i < ffs->interfaces_count; ++i) {
-                       struct usb_os_desc *desc;
-
-                       desc = func->function.os_desc_table[i].os_desc =
-                               vla_ptr(vlabuf, d, os_desc) +
-                               i * sizeof(struct usb_os_desc);
-                       desc->ext_compat_id =
-                               vla_ptr(vlabuf, d, ext_compat) + i * 16;
-                       INIT_LIST_HEAD(&desc->ext_prop);
-               }
-       ret = ffs_do_os_descs(ffs->ms_os_descs_count,
-                             vla_ptr(vlabuf, d, raw_descs) +
-                             fs_len + hs_len + ss_len,
-                             d_raw_descs__sz - fs_len - hs_len - ss_len,
-                             __ffs_func_bind_do_os_desc, func);
-       if (unlikely(ret < 0))
-               goto error;
-       func->function.os_desc_n =
-               c->cdev->use_os_string ? ffs->interfaces_count : 0;
-
-       /* 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;
-}
-
-static int ffs_func_bind(struct usb_configuration *c,
-                        struct usb_function *f)
-{
-       struct f_fs_opts *ffs_opts = ffs_do_functionfs_bind(f, c);
-
-       if (IS_ERR(ffs_opts))
-               return PTR_ERR(ffs_opts);
-
-       return _ffs_func_bind(c, f);
-}
-
-
-/* Other USB function hooks *************************************************/
-
-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();
-
-       pr_vdebug("creq->bRequestType = %02x\n", creq->bRequestType);
-       pr_vdebug("creq->bRequest     = %02x\n", creq->bRequest);
-       pr_vdebug("creq->wValue       = %04x\n", le16_to_cpu(creq->wValue));
-       pr_vdebug("creq->wIndex       = %04x\n", le16_to_cpu(creq->wIndex));
-       pr_vdebug("creq->wLength      = %04x\n", le16_to_cpu(creq->wLength));
-
-       /*
-        * Most requests directed to interface go through 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 (unlikely(ret < 0))
-                       return ret;
-               break;
-
-       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);
-}
-
-
-/* Endpoint 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;
-}
-
-
-/* Devices management *******************************************************/
-
-static LIST_HEAD(ffs_devices);
-
-static struct ffs_dev *_ffs_do_find_dev(const char *name)
-{
-       struct ffs_dev *dev;
-
-       list_for_each_entry(dev, &ffs_devices, entry) {
-               if (!dev->name || !name)
-                       continue;
-               if (strcmp(dev->name, name) == 0)
-                       return dev;
-       }
-
-       return NULL;
-}
-
-/*
- * ffs_lock must be taken by the caller of this function
- */
-static struct ffs_dev *_ffs_get_single_dev(void)
-{
-       struct ffs_dev *dev;
-
-       if (list_is_singular(&ffs_devices)) {
-               dev = list_first_entry(&ffs_devices, struct ffs_dev, entry);
-               if (dev->single)
-                       return dev;
-       }
-
-       return NULL;
-}
-
-/*
- * ffs_lock must be taken by the caller of this function
- */
-static struct ffs_dev *_ffs_find_dev(const char *name)
-{
-       struct ffs_dev *dev;
-
-       dev = _ffs_get_single_dev();
-       if (dev)
-               return dev;
-
-       return _ffs_do_find_dev(name);
-}
-
-/* Configfs support *********************************************************/
-
-static inline struct f_fs_opts *to_ffs_opts(struct config_item *item)
-{
-       return container_of(to_config_group(item), struct f_fs_opts,
-                           func_inst.group);
-}
-
-static void ffs_attr_release(struct config_item *item)
-{
-       struct f_fs_opts *opts = to_ffs_opts(item);
-
-       usb_put_function_instance(&opts->func_inst);
-}
-
-static struct configfs_item_operations ffs_item_ops = {
-       .release        = ffs_attr_release,
-};
-
-static struct config_item_type ffs_func_type = {
-       .ct_item_ops    = &ffs_item_ops,
-       .ct_owner       = THIS_MODULE,
-};
-
-
-/* Function registration interface ******************************************/
-
-static void ffs_free_inst(struct usb_function_instance *f)
-{
-       struct f_fs_opts *opts;
-
-       opts = to_f_fs_opts(f);
-       ffs_dev_lock();
-       _ffs_free_dev(opts->dev);
-       ffs_dev_unlock();
-       kfree(opts);
-}
-
-#define MAX_INST_NAME_LEN      40
-
-static int ffs_set_inst_name(struct usb_function_instance *fi, const char *name)
-{
-       struct f_fs_opts *opts;
-       char *ptr;
-       const char *tmp;
-       int name_len, ret;
-
-       name_len = strlen(name) + 1;
-       if (name_len > MAX_INST_NAME_LEN)
-               return -ENAMETOOLONG;
-
-       ptr = kstrndup(name, name_len, GFP_KERNEL);
-       if (!ptr)
-               return -ENOMEM;
-
-       opts = to_f_fs_opts(fi);
-       tmp = NULL;
-
-       ffs_dev_lock();
-
-       tmp = opts->dev->name_allocated ? opts->dev->name : NULL;
-       ret = _ffs_name_dev(opts->dev, ptr);
-       if (ret) {
-               kfree(ptr);
-               ffs_dev_unlock();
-               return ret;
-       }
-       opts->dev->name_allocated = true;
-
-       ffs_dev_unlock();
-
-       kfree(tmp);
-
-       return 0;
-}
-
-static struct usb_function_instance *ffs_alloc_inst(void)
-{
-       struct f_fs_opts *opts;
-       struct ffs_dev *dev;
-
-       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
-       if (!opts)
-               return ERR_PTR(-ENOMEM);
-
-       opts->func_inst.set_inst_name = ffs_set_inst_name;
-       opts->func_inst.free_func_inst = ffs_free_inst;
-       ffs_dev_lock();
-       dev = _ffs_alloc_dev();
-       ffs_dev_unlock();
-       if (IS_ERR(dev)) {
-               kfree(opts);
-               return ERR_CAST(dev);
-       }
-       opts->dev = dev;
-       dev->opts = opts;
-
-       config_group_init_type_name(&opts->func_inst.group, "",
-                                   &ffs_func_type);
-       return &opts->func_inst;
-}
-
-static void ffs_free(struct usb_function *f)
-{
-       kfree(ffs_func_from_usb(f));
-}
-
-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;
-       struct f_fs_opts *opts =
-               container_of(f->fi, struct f_fs_opts, func_inst);
-       struct ffs_ep *ep = func->eps;
-       unsigned count = ffs->eps_count;
-       unsigned long flags;
-
-       ENTER();
-       if (ffs->func == func) {
-               ffs_func_eps_disable(func);
-               ffs->func = NULL;
-       }
-
-       if (!--opts->refcnt)
-               functionfs_unbind(ffs);
-
-       /* cleanup after autoconfig */
-       spin_lock_irqsave(&func->ffs->eps_lock, flags);
-       do {
-               if (ep->ep && ep->req)
-                       usb_ep_free_request(ep->ep, ep->req);
-               ep->req = NULL;
-               ++ep;
-       } while (--count);
-       spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
-       kfree(func->eps);
-       func->eps = NULL;
-       /*
-        * eps, descriptors and interfaces_nums are allocated in the
-        * same chunk so only one free is required.
-        */
-       func->function.fs_descriptors = NULL;
-       func->function.hs_descriptors = NULL;
-       func->function.ss_descriptors = NULL;
-       func->interfaces_nums = NULL;
-
-       ffs_event_add(ffs, FUNCTIONFS_UNBIND);
-}
-
-static struct usb_function *ffs_alloc(struct usb_function_instance *fi)
-{
-       struct ffs_function *func;
-
-       ENTER();
-
-       func = kzalloc(sizeof(*func), GFP_KERNEL);
-       if (unlikely(!func))
-               return ERR_PTR(-ENOMEM);
-
-       func->function.name    = "Function FS Gadget";
-
-       func->function.bind    = ffs_func_bind;
-       func->function.unbind  = ffs_func_unbind;
-       func->function.set_alt = ffs_func_set_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->function.free_func = ffs_free;
-
-       return &func->function;
-}
-
-/*
- * ffs_lock must be taken by the caller of this function
- */
-static struct ffs_dev *_ffs_alloc_dev(void)
-{
-       struct ffs_dev *dev;
-       int ret;
-
-       if (_ffs_get_single_dev())
-                       return ERR_PTR(-EBUSY);
-
-       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
-       if (!dev)
-               return ERR_PTR(-ENOMEM);
-
-       if (list_empty(&ffs_devices)) {
-               ret = functionfs_init();
-               if (ret) {
-                       kfree(dev);
-                       return ERR_PTR(ret);
-               }
-       }
-
-       list_add(&dev->entry, &ffs_devices);
-
-       return dev;
-}
-
-/*
- * ffs_lock must be taken by the caller of this function
- * The caller is responsible for "name" being available whenever f_fs needs it
- */
-static int _ffs_name_dev(struct ffs_dev *dev, const char *name)
-{
-       struct ffs_dev *existing;
-
-       existing = _ffs_do_find_dev(name);
-       if (existing)
-               return -EBUSY;
-
-       dev->name = name;
-
-       return 0;
-}
-
-/*
- * The caller is responsible for "name" being available whenever f_fs needs it
- */
-int ffs_name_dev(struct ffs_dev *dev, const char *name)
-{
-       int ret;
-
-       ffs_dev_lock();
-       ret = _ffs_name_dev(dev, name);
-       ffs_dev_unlock();
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(ffs_name_dev);
-
-int ffs_single_dev(struct ffs_dev *dev)
-{
-       int ret;
-
-       ret = 0;
-       ffs_dev_lock();
-
-       if (!list_is_singular(&ffs_devices))
-               ret = -EBUSY;
-       else
-               dev->single = true;
-
-       ffs_dev_unlock();
-       return ret;
-}
-EXPORT_SYMBOL_GPL(ffs_single_dev);
-
-/*
- * ffs_lock must be taken by the caller of this function
- */
-static void _ffs_free_dev(struct ffs_dev *dev)
-{
-       list_del(&dev->entry);
-       if (dev->name_allocated)
-               kfree(dev->name);
-       kfree(dev);
-       if (list_empty(&ffs_devices))
-               functionfs_cleanup();
-}
-
-static void *ffs_acquire_dev(const char *dev_name)
-{
-       struct ffs_dev *ffs_dev;
-
-       ENTER();
-       ffs_dev_lock();
-
-       ffs_dev = _ffs_find_dev(dev_name);
-       if (!ffs_dev)
-               ffs_dev = ERR_PTR(-ENOENT);
-       else if (ffs_dev->mounted)
-               ffs_dev = ERR_PTR(-EBUSY);
-       else if (ffs_dev->ffs_acquire_dev_callback &&
-           ffs_dev->ffs_acquire_dev_callback(ffs_dev))
-               ffs_dev = ERR_PTR(-ENOENT);
-       else
-               ffs_dev->mounted = true;
-
-       ffs_dev_unlock();
-       return ffs_dev;
-}
-
-static void ffs_release_dev(struct ffs_data *ffs_data)
-{
-       struct ffs_dev *ffs_dev;
-
-       ENTER();
-       ffs_dev_lock();
-
-       ffs_dev = ffs_data->private_data;
-       if (ffs_dev) {
-               ffs_dev->mounted = false;
-
-               if (ffs_dev->ffs_release_dev_callback)
-                       ffs_dev->ffs_release_dev_callback(ffs_dev);
-       }
-
-       ffs_dev_unlock();
-}
-
-static int ffs_ready(struct ffs_data *ffs)
-{
-       struct ffs_dev *ffs_obj;
-       int ret = 0;
-
-       ENTER();
-       ffs_dev_lock();
-
-       ffs_obj = ffs->private_data;
-       if (!ffs_obj) {
-               ret = -EINVAL;
-               goto done;
-       }
-       if (WARN_ON(ffs_obj->desc_ready)) {
-               ret = -EBUSY;
-               goto done;
-       }
-
-       ffs_obj->desc_ready = true;
-       ffs_obj->ffs_data = ffs;
-
-       if (ffs_obj->ffs_ready_callback)
-               ret = ffs_obj->ffs_ready_callback(ffs);
-
-done:
-       ffs_dev_unlock();
-       return ret;
-}
-
-static void ffs_closed(struct ffs_data *ffs)
-{
-       struct ffs_dev *ffs_obj;
-
-       ENTER();
-       ffs_dev_lock();
-
-       ffs_obj = ffs->private_data;
-       if (!ffs_obj)
-               goto done;
-
-       ffs_obj->desc_ready = false;
-
-       if (ffs_obj->ffs_closed_callback)
-               ffs_obj->ffs_closed_callback(ffs);
-
-       if (!ffs_obj->opts || ffs_obj->opts->no_configfs
-           || !ffs_obj->opts->func_inst.group.cg_item.ci_parent)
-               goto done;
-
-       unregister_gadget_item(ffs_obj->opts->
-                              func_inst.group.cg_item.ci_parent->ci_parent);
-done:
-       ffs_dev_unlock();
-}
-
-/* Misc helper functions ****************************************************/
-
-static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock)
-{
-       return nonblock
-               ? likely(mutex_trylock(mutex)) ? 0 : -EAGAIN
-               : mutex_lock_interruptible(mutex);
-}
-
-static char *ffs_prepare_buffer(const char __user *buf, size_t len)
-{
-       char *data;
-
-       if (unlikely(!len))
-               return NULL;
-
-       data = kmalloc(len, GFP_KERNEL);
-       if (unlikely(!data))
-               return ERR_PTR(-ENOMEM);
-
-       if (unlikely(__copy_from_user(data, buf, len))) {
-               kfree(data);
-               return ERR_PTR(-EFAULT);
-       }
-
-       pr_vdebug("Buffer from user space:\n");
-       ffs_dump_mem("", data, len);
-
-       return data;
-}
-
-DECLARE_USB_FUNCTION_INIT(ffs, ffs_alloc_inst, ffs_alloc);
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Michal Nazarewicz");
diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/f_hid.c
deleted file mode 100644 (file)
index a95290a..0000000
+++ /dev/null
@@ -1,763 +0,0 @@
-/*
- * f_hid.c -- USB HID function driver
- *
- * Copyright (C) 2010 Fabien Chouteau <fabien.chouteau@barco.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/hid.h>
-#include <linux/cdev.h>
-#include <linux/mutex.h>
-#include <linux/poll.h>
-#include <linux/uaccess.h>
-#include <linux/wait.h>
-#include <linux/sched.h>
-#include <linux/usb/g_hid.h>
-
-#include "u_f.h"
-
-static int major, minors;
-static struct class *hidg_class;
-
-/*-------------------------------------------------------------------------*/
-/*                            HID gadget struct                            */
-
-struct f_hidg_req_list {
-       struct usb_request      *req;
-       unsigned int            pos;
-       struct list_head        list;
-};
-
-struct f_hidg {
-       /* configuration */
-       unsigned char                   bInterfaceSubClass;
-       unsigned char                   bInterfaceProtocol;
-       unsigned short                  report_desc_length;
-       char                            *report_desc;
-       unsigned short                  report_length;
-
-       /* recv report */
-       struct list_head                completed_out_req;
-       spinlock_t                      spinlock;
-       wait_queue_head_t               read_queue;
-       unsigned int                    qlen;
-
-       /* send report */
-       struct mutex                    lock;
-       bool                            write_pending;
-       wait_queue_head_t               write_queue;
-       struct usb_request              *req;
-
-       int                             minor;
-       struct cdev                     cdev;
-       struct usb_function             func;
-
-       struct usb_ep                   *in_ep;
-       struct usb_ep                   *out_ep;
-};
-
-static inline struct f_hidg *func_to_hidg(struct usb_function *f)
-{
-       return container_of(f, struct f_hidg, func);
-}
-
-/*-------------------------------------------------------------------------*/
-/*                           Static descriptors                            */
-
-static struct usb_interface_descriptor hidg_interface_desc = {
-       .bLength                = sizeof hidg_interface_desc,
-       .bDescriptorType        = USB_DT_INTERFACE,
-       /* .bInterfaceNumber    = DYNAMIC */
-       .bAlternateSetting      = 0,
-       .bNumEndpoints          = 2,
-       .bInterfaceClass        = USB_CLASS_HID,
-       /* .bInterfaceSubClass  = DYNAMIC */
-       /* .bInterfaceProtocol  = DYNAMIC */
-       /* .iInterface          = DYNAMIC */
-};
-
-static struct hid_descriptor hidg_desc = {
-       .bLength                        = sizeof hidg_desc,
-       .bDescriptorType                = HID_DT_HID,
-       .bcdHID                         = 0x0101,
-       .bCountryCode                   = 0x00,
-       .bNumDescriptors                = 0x1,
-       /*.desc[0].bDescriptorType      = DYNAMIC */
-       /*.desc[0].wDescriptorLenght    = DYNAMIC */
-};
-
-/* High-Speed Support */
-
-static struct usb_endpoint_descriptor hidg_hs_in_ep_desc = {
-       .bLength                = USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType        = USB_DT_ENDPOINT,
-       .bEndpointAddress       = USB_DIR_IN,
-       .bmAttributes           = USB_ENDPOINT_XFER_INT,
-       /*.wMaxPacketSize       = DYNAMIC */
-       .bInterval              = 4, /* FIXME: Add this field in the
-                                     * HID gadget configuration?
-                                     * (struct hidg_func_descriptor)
-                                     */
-};
-
-static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = {
-       .bLength                = USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType        = USB_DT_ENDPOINT,
-       .bEndpointAddress       = USB_DIR_OUT,
-       .bmAttributes           = USB_ENDPOINT_XFER_INT,
-       /*.wMaxPacketSize       = DYNAMIC */
-       .bInterval              = 4, /* FIXME: Add this field in the
-                                     * HID gadget configuration?
-                                     * (struct hidg_func_descriptor)
-                                     */
-};
-
-static struct usb_descriptor_header *hidg_hs_descriptors[] = {
-       (struct usb_descriptor_header *)&hidg_interface_desc,
-       (struct usb_descriptor_header *)&hidg_desc,
-       (struct usb_descriptor_header *)&hidg_hs_in_ep_desc,
-       (struct usb_descriptor_header *)&hidg_hs_out_ep_desc,
-       NULL,
-};
-
-/* Full-Speed Support */
-
-static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = {
-       .bLength                = USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType        = USB_DT_ENDPOINT,
-       .bEndpointAddress       = USB_DIR_IN,
-       .bmAttributes           = USB_ENDPOINT_XFER_INT,
-       /*.wMaxPacketSize       = DYNAMIC */
-       .bInterval              = 10, /* FIXME: Add this field in the
-                                      * HID gadget configuration?
-                                      * (struct hidg_func_descriptor)
-                                      */
-};
-
-static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = {
-       .bLength                = USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType        = USB_DT_ENDPOINT,
-       .bEndpointAddress       = USB_DIR_OUT,
-       .bmAttributes           = USB_ENDPOINT_XFER_INT,
-       /*.wMaxPacketSize       = DYNAMIC */
-       .bInterval              = 10, /* FIXME: Add this field in the
-                                      * HID gadget configuration?
-                                      * (struct hidg_func_descriptor)
-                                      */
-};
-
-static struct usb_descriptor_header *hidg_fs_descriptors[] = {
-       (struct usb_descriptor_header *)&hidg_interface_desc,
-       (struct usb_descriptor_header *)&hidg_desc,
-       (struct usb_descriptor_header *)&hidg_fs_in_ep_desc,
-       (struct usb_descriptor_header *)&hidg_fs_out_ep_desc,
-       NULL,
-};
-
-/*-------------------------------------------------------------------------*/
-/*                              Char Device                                */
-
-static ssize_t f_hidg_read(struct file *file, char __user *buffer,
-                       size_t count, loff_t *ptr)
-{
-       struct f_hidg *hidg = file->private_data;
-       struct f_hidg_req_list *list;
-       struct usb_request *req;
-       unsigned long flags;
-       int ret;
-
-       if (!count)
-               return 0;
-
-       if (!access_ok(VERIFY_WRITE, buffer, count))
-               return -EFAULT;
-
-       spin_lock_irqsave(&hidg->spinlock, flags);
-
-#define READ_COND (!list_empty(&hidg->completed_out_req))
-
-       /* wait for at least one buffer to complete */
-       while (!READ_COND) {
-               spin_unlock_irqrestore(&hidg->spinlock, flags);
-               if (file->f_flags & O_NONBLOCK)
-                       return -EAGAIN;
-
-               if (wait_event_interruptible(hidg->read_queue, READ_COND))
-                       return -ERESTARTSYS;
-
-               spin_lock_irqsave(&hidg->spinlock, flags);
-       }
-
-       /* pick the first one */
-       list = list_first_entry(&hidg->completed_out_req,
-                               struct f_hidg_req_list, list);
-       req = list->req;
-       count = min_t(unsigned int, count, req->actual - list->pos);
-       spin_unlock_irqrestore(&hidg->spinlock, flags);
-
-       /* copy to user outside spinlock */
-       count -= copy_to_user(buffer, req->buf + list->pos, count);
-       list->pos += count;
-
-       /*
-        * if this request is completely handled and transfered to
-        * userspace, remove its entry from the list and requeue it
-        * again. Otherwise, we will revisit it again upon the next
-        * call, taking into account its current read position.
-        */
-       if (list->pos == req->actual) {
-               spin_lock_irqsave(&hidg->spinlock, flags);
-               list_del(&list->list);
-               kfree(list);
-               spin_unlock_irqrestore(&hidg->spinlock, flags);
-
-               req->length = hidg->report_length;
-               ret = usb_ep_queue(hidg->out_ep, req, GFP_KERNEL);
-               if (ret < 0)
-                       return ret;
-       }
-
-       return count;
-}
-
-static void f_hidg_req_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       struct f_hidg *hidg = (struct f_hidg *)ep->driver_data;
-
-       if (req->status != 0) {
-               ERROR(hidg->func.config->cdev,
-                       "End Point Request ERROR: %d\n", req->status);
-       }
-
-       hidg->write_pending = 0;
-       wake_up(&hidg->write_queue);
-}
-
-static ssize_t f_hidg_write(struct file *file, const char __user *buffer,
-                           size_t count, loff_t *offp)
-{
-       struct f_hidg *hidg  = file->private_data;
-       ssize_t status = -ENOMEM;
-
-       if (!access_ok(VERIFY_READ, buffer, count))
-               return -EFAULT;
-
-       mutex_lock(&hidg->lock);
-
-#define WRITE_COND (!hidg->write_pending)
-
-       /* write queue */
-       while (!WRITE_COND) {
-               mutex_unlock(&hidg->lock);
-               if (file->f_flags & O_NONBLOCK)
-                       return -EAGAIN;
-
-               if (wait_event_interruptible_exclusive(
-                               hidg->write_queue, WRITE_COND))
-                       return -ERESTARTSYS;
-
-               mutex_lock(&hidg->lock);
-       }
-
-       count  = min_t(unsigned, count, hidg->report_length);
-       status = copy_from_user(hidg->req->buf, buffer, count);
-
-       if (status != 0) {
-               ERROR(hidg->func.config->cdev,
-                       "copy_from_user error\n");
-               mutex_unlock(&hidg->lock);
-               return -EINVAL;
-       }
-
-       hidg->req->status   = 0;
-       hidg->req->zero     = 0;
-       hidg->req->length   = count;
-       hidg->req->complete = f_hidg_req_complete;
-       hidg->req->context  = hidg;
-       hidg->write_pending = 1;
-
-       status = usb_ep_queue(hidg->in_ep, hidg->req, GFP_ATOMIC);
-       if (status < 0) {
-               ERROR(hidg->func.config->cdev,
-                       "usb_ep_queue error on int endpoint %zd\n", status);
-               hidg->write_pending = 0;
-               wake_up(&hidg->write_queue);
-       } else {
-               status = count;
-       }
-
-       mutex_unlock(&hidg->lock);
-
-       return status;
-}
-
-static unsigned int f_hidg_poll(struct file *file, poll_table *wait)
-{
-       struct f_hidg   *hidg  = file->private_data;
-       unsigned int    ret = 0;
-
-       poll_wait(file, &hidg->read_queue, wait);
-       poll_wait(file, &hidg->write_queue, wait);
-
-       if (WRITE_COND)
-               ret |= POLLOUT | POLLWRNORM;
-
-       if (READ_COND)
-               ret |= POLLIN | POLLRDNORM;
-
-       return ret;
-}
-
-#undef WRITE_COND
-#undef READ_COND
-
-static int f_hidg_release(struct inode *inode, struct file *fd)
-{
-       fd->private_data = NULL;
-       return 0;
-}
-
-static int f_hidg_open(struct inode *inode, struct file *fd)
-{
-       struct f_hidg *hidg =
-               container_of(inode->i_cdev, struct f_hidg, cdev);
-
-       fd->private_data = hidg;
-
-       return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-/*                                usb_function                             */
-
-static inline struct usb_request *hidg_alloc_ep_req(struct usb_ep *ep,
-                                                   unsigned length)
-{
-       return alloc_ep_req(ep, length, length);
-}
-
-static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       struct f_hidg *hidg = (struct f_hidg *) req->context;
-       struct f_hidg_req_list *req_list;
-       unsigned long flags;
-
-       req_list = kzalloc(sizeof(*req_list), GFP_ATOMIC);
-       if (!req_list)
-               return;
-
-       req_list->req = req;
-
-       spin_lock_irqsave(&hidg->spinlock, flags);
-       list_add_tail(&req_list->list, &hidg->completed_out_req);
-       spin_unlock_irqrestore(&hidg->spinlock, flags);
-
-       wake_up(&hidg->read_queue);
-}
-
-static int hidg_setup(struct usb_function *f,
-               const struct usb_ctrlrequest *ctrl)
-{
-       struct f_hidg                   *hidg = func_to_hidg(f);
-       struct usb_composite_dev        *cdev = f->config->cdev;
-       struct usb_request              *req  = cdev->req;
-       int status = 0;
-       __u16 value, length;
-
-       value   = __le16_to_cpu(ctrl->wValue);
-       length  = __le16_to_cpu(ctrl->wLength);
-
-       VDBG(cdev, "hid_setup crtl_request : bRequestType:0x%x bRequest:0x%x "
-               "Value:0x%x\n", ctrl->bRequestType, ctrl->bRequest, value);
-
-       switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
-       case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
-                 | HID_REQ_GET_REPORT):
-               VDBG(cdev, "get_report\n");
-
-               /* send an empty report */
-               length = min_t(unsigned, length, hidg->report_length);
-               memset(req->buf, 0x0, length);
-
-               goto respond;
-               break;
-
-       case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
-                 | HID_REQ_GET_PROTOCOL):
-               VDBG(cdev, "get_protocol\n");
-               goto stall;
-               break;
-
-       case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
-                 | HID_REQ_SET_REPORT):
-               VDBG(cdev, "set_report | wLenght=%d\n", ctrl->wLength);
-               goto stall;
-               break;
-
-       case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
-                 | HID_REQ_SET_PROTOCOL):
-               VDBG(cdev, "set_protocol\n");
-               goto stall;
-               break;
-
-       case ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8
-                 | USB_REQ_GET_DESCRIPTOR):
-               switch (value >> 8) {
-               case HID_DT_HID:
-                       VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: HID\n");
-                       length = min_t(unsigned short, length,
-                                                  hidg_desc.bLength);
-                       memcpy(req->buf, &hidg_desc, length);
-                       goto respond;
-                       break;
-               case HID_DT_REPORT:
-                       VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: REPORT\n");
-                       length = min_t(unsigned short, length,
-                                                  hidg->report_desc_length);
-                       memcpy(req->buf, hidg->report_desc, length);
-                       goto respond;
-                       break;
-
-               default:
-                       VDBG(cdev, "Unknown descriptor request 0x%x\n",
-                                value >> 8);
-                       goto stall;
-                       break;
-               }
-               break;
-
-       default:
-               VDBG(cdev, "Unknown request 0x%x\n",
-                        ctrl->bRequest);
-               goto stall;
-               break;
-       }
-
-stall:
-       return -EOPNOTSUPP;
-
-respond:
-       req->zero = 0;
-       req->length = length;
-       status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
-       if (status < 0)
-               ERROR(cdev, "usb_ep_queue error on ep0 %d\n", value);
-       return status;
-}
-
-static void hidg_disable(struct usb_function *f)
-{
-       struct f_hidg *hidg = func_to_hidg(f);
-       struct f_hidg_req_list *list, *next;
-
-       usb_ep_disable(hidg->in_ep);
-       hidg->in_ep->driver_data = NULL;
-
-       usb_ep_disable(hidg->out_ep);
-       hidg->out_ep->driver_data = NULL;
-
-       list_for_each_entry_safe(list, next, &hidg->completed_out_req, list) {
-               list_del(&list->list);
-               kfree(list);
-       }
-}
-
-static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
-{
-       struct usb_composite_dev                *cdev = f->config->cdev;
-       struct f_hidg                           *hidg = func_to_hidg(f);
-       int i, status = 0;
-
-       VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt);
-
-       if (hidg->in_ep != NULL) {
-               /* restart endpoint */
-               if (hidg->in_ep->driver_data != NULL)
-                       usb_ep_disable(hidg->in_ep);
-
-               status = config_ep_by_speed(f->config->cdev->gadget, f,
-                                           hidg->in_ep);
-               if (status) {
-                       ERROR(cdev, "config_ep_by_speed FAILED!\n");
-                       goto fail;
-               }
-               status = usb_ep_enable(hidg->in_ep);
-               if (status < 0) {
-                       ERROR(cdev, "Enable IN endpoint FAILED!\n");
-                       goto fail;
-               }
-               hidg->in_ep->driver_data = hidg;
-       }
-
-
-       if (hidg->out_ep != NULL) {
-               /* restart endpoint */
-               if (hidg->out_ep->driver_data != NULL)
-                       usb_ep_disable(hidg->out_ep);
-
-               status = config_ep_by_speed(f->config->cdev->gadget, f,
-                                           hidg->out_ep);
-               if (status) {
-                       ERROR(cdev, "config_ep_by_speed FAILED!\n");
-                       goto fail;
-               }
-               status = usb_ep_enable(hidg->out_ep);
-               if (status < 0) {
-                       ERROR(cdev, "Enable IN endpoint FAILED!\n");
-                       goto fail;
-               }
-               hidg->out_ep->driver_data = hidg;
-
-               /*
-                * allocate a bunch of read buffers and queue them all at once.
-                */
-               for (i = 0; i < hidg->qlen && status == 0; i++) {
-                       struct usb_request *req =
-                                       hidg_alloc_ep_req(hidg->out_ep,
-                                                         hidg->report_length);
-                       if (req) {
-                               req->complete = hidg_set_report_complete;
-                               req->context  = hidg;
-                               status = usb_ep_queue(hidg->out_ep, req,
-                                                     GFP_ATOMIC);
-                               if (status)
-                                       ERROR(cdev, "%s queue req --> %d\n",
-                                               hidg->out_ep->name, status);
-                       } else {
-                               usb_ep_disable(hidg->out_ep);
-                               hidg->out_ep->driver_data = NULL;
-                               status = -ENOMEM;
-                               goto fail;
-                       }
-               }
-       }
-
-fail:
-       return status;
-}
-
-const struct file_operations f_hidg_fops = {
-       .owner          = THIS_MODULE,
-       .open           = f_hidg_open,
-       .release        = f_hidg_release,
-       .write          = f_hidg_write,
-       .read           = f_hidg_read,
-       .poll           = f_hidg_poll,
-       .llseek         = noop_llseek,
-};
-
-static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
-{
-       struct usb_ep           *ep;
-       struct f_hidg           *hidg = func_to_hidg(f);
-       int                     status;
-       dev_t                   dev;
-
-       /* allocate instance-specific interface IDs, and patch descriptors */
-       status = usb_interface_id(c, f);
-       if (status < 0)
-               goto fail;
-       hidg_interface_desc.bInterfaceNumber = status;
-
-       /* allocate instance-specific endpoints */
-       status = -ENODEV;
-       ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_in_ep_desc);
-       if (!ep)
-               goto fail;
-       ep->driver_data = c->cdev;      /* claim */
-       hidg->in_ep = ep;
-
-       ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_out_ep_desc);
-       if (!ep)
-               goto fail;
-       ep->driver_data = c->cdev;      /* claim */
-       hidg->out_ep = ep;
-
-       /* preallocate request and buffer */
-       status = -ENOMEM;
-       hidg->req = usb_ep_alloc_request(hidg->in_ep, GFP_KERNEL);
-       if (!hidg->req)
-               goto fail;
-
-       hidg->req->buf = kmalloc(hidg->report_length, GFP_KERNEL);
-       if (!hidg->req->buf)
-               goto fail;
-
-       /* set descriptor dynamic values */
-       hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass;
-       hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol;
-       hidg_hs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
-       hidg_fs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
-       hidg_hs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
-       hidg_fs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
-       hidg_desc.desc[0].bDescriptorType = HID_DT_REPORT;
-       hidg_desc.desc[0].wDescriptorLength =
-               cpu_to_le16(hidg->report_desc_length);
-
-       hidg_hs_in_ep_desc.bEndpointAddress =
-               hidg_fs_in_ep_desc.bEndpointAddress;
-       hidg_hs_out_ep_desc.bEndpointAddress =
-               hidg_fs_out_ep_desc.bEndpointAddress;
-
-       status = usb_assign_descriptors(f, hidg_fs_descriptors,
-                       hidg_hs_descriptors, NULL);
-       if (status)
-               goto fail;
-
-       mutex_init(&hidg->lock);
-       spin_lock_init(&hidg->spinlock);
-       init_waitqueue_head(&hidg->write_queue);
-       init_waitqueue_head(&hidg->read_queue);
-       INIT_LIST_HEAD(&hidg->completed_out_req);
-
-       /* create char device */
-       cdev_init(&hidg->cdev, &f_hidg_fops);
-       dev = MKDEV(major, hidg->minor);
-       status = cdev_add(&hidg->cdev, dev, 1);
-       if (status)
-               goto fail;
-
-       device_create(hidg_class, NULL, dev, NULL, "%s%d", "hidg", hidg->minor);
-
-       return 0;
-
-fail:
-       ERROR(f->config->cdev, "hidg_bind FAILED\n");
-       if (hidg->req != NULL) {
-               kfree(hidg->req->buf);
-               if (hidg->in_ep != NULL)
-                       usb_ep_free_request(hidg->in_ep, hidg->req);
-       }
-
-       usb_free_all_descriptors(f);
-       return status;
-}
-
-static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
-{
-       struct f_hidg *hidg = func_to_hidg(f);
-
-       device_destroy(hidg_class, MKDEV(major, hidg->minor));
-       cdev_del(&hidg->cdev);
-
-       /* disable/free request and end point */
-       usb_ep_disable(hidg->in_ep);
-       usb_ep_dequeue(hidg->in_ep, hidg->req);
-       kfree(hidg->req->buf);
-       usb_ep_free_request(hidg->in_ep, hidg->req);
-
-       usb_free_all_descriptors(f);
-
-       kfree(hidg->report_desc);
-       kfree(hidg);
-}
-
-/*-------------------------------------------------------------------------*/
-/*                                 Strings                                 */
-
-#define CT_FUNC_HID_IDX        0
-
-static struct usb_string ct_func_string_defs[] = {
-       [CT_FUNC_HID_IDX].s     = "HID Interface",
-       {},                     /* end of list */
-};
-
-static struct usb_gadget_strings ct_func_string_table = {
-       .language       = 0x0409,       /* en-US */
-       .strings        = ct_func_string_defs,
-};
-
-static struct usb_gadget_strings *ct_func_strings[] = {
-       &ct_func_string_table,
-       NULL,
-};
-
-/*-------------------------------------------------------------------------*/
-/*                             usb_configuration                           */
-
-int __init hidg_bind_config(struct usb_configuration *c,
-                           struct hidg_func_descriptor *fdesc, int index)
-{
-       struct f_hidg *hidg;
-       int status;
-
-       if (index >= minors)
-               return -ENOENT;
-
-       /* maybe allocate device-global string IDs, and patch descriptors */
-       if (ct_func_string_defs[CT_FUNC_HID_IDX].id == 0) {
-               status = usb_string_id(c->cdev);
-               if (status < 0)
-                       return status;
-               ct_func_string_defs[CT_FUNC_HID_IDX].id = status;
-               hidg_interface_desc.iInterface = status;
-       }
-
-       /* allocate and initialize one new instance */
-       hidg = kzalloc(sizeof *hidg, GFP_KERNEL);
-       if (!hidg)
-               return -ENOMEM;
-
-       hidg->minor = index;
-       hidg->bInterfaceSubClass = fdesc->subclass;
-       hidg->bInterfaceProtocol = fdesc->protocol;
-       hidg->report_length = fdesc->report_length;
-       hidg->report_desc_length = fdesc->report_desc_length;
-       hidg->report_desc = kmemdup(fdesc->report_desc,
-                                   fdesc->report_desc_length,
-                                   GFP_KERNEL);
-       if (!hidg->report_desc) {
-               kfree(hidg);
-               return -ENOMEM;
-       }
-
-       hidg->func.name    = "hid";
-       hidg->func.strings = ct_func_strings;
-       hidg->func.bind    = hidg_bind;
-       hidg->func.unbind  = hidg_unbind;
-       hidg->func.set_alt = hidg_set_alt;
-       hidg->func.disable = hidg_disable;
-       hidg->func.setup   = hidg_setup;
-
-       /* this could me made configurable at some point */
-       hidg->qlen         = 4;
-
-       status = usb_add_function(c, &hidg->func);
-       if (status)
-               kfree(hidg);
-
-       return status;
-}
-
-int __init ghid_setup(struct usb_gadget *g, int count)
-{
-       int status;
-       dev_t dev;
-
-       hidg_class = class_create(THIS_MODULE, "hidg");
-
-       status = alloc_chrdev_region(&dev, 0, count, "hidg");
-       if (!status) {
-               major = MAJOR(dev);
-               minors = count;
-       }
-
-       return status;
-}
-
-void ghid_cleanup(void)
-{
-       if (major) {
-               unregister_chrdev_region(MKDEV(major, 0), minors);
-               major = minors = 0;
-       }
-
-       class_destroy(hidg_class);
-       hidg_class = NULL;
-}
diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c
deleted file mode 100644 (file)
index 4557cd0..0000000
+++ /dev/null
@@ -1,571 +0,0 @@
-/*
- * f_loopback.c - USB peripheral loopback configuration driver
- *
- * Copyright (C) 2003-2008 David Brownell
- * Copyright (C) 2008 by Nokia Corporation
- *
- * 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.
- */
-
-/* #define VERBOSE_DEBUG */
-
-#include <linux/slab.h>
-#include <linux/kernel.h>
-#include <linux/device.h>
-#include <linux/module.h>
-#include <linux/err.h>
-#include <linux/usb/composite.h>
-
-#include "g_zero.h"
-#include "u_f.h"
-
-/*
- * LOOPBACK FUNCTION ... a testing vehicle for USB peripherals,
- *
- * This takes messages of various sizes written OUT to a device, and loops
- * them back so they can be read IN from it.  It has been used by certain
- * test applications.  It supports limited testing of data queueing logic.
- *
- *
- * This is currently packaged as a configuration driver, which can't be
- * combined with other functions to make composite devices.  However, it
- * can be combined with other independent configurations.
- */
-struct f_loopback {
-       struct usb_function     function;
-
-       struct usb_ep           *in_ep;
-       struct usb_ep           *out_ep;
-};
-
-static inline struct f_loopback *func_to_loop(struct usb_function *f)
-{
-       return container_of(f, struct f_loopback, function);
-}
-
-static unsigned qlen;
-static unsigned buflen;
-
-/*-------------------------------------------------------------------------*/
-
-static struct usb_interface_descriptor loopback_intf = {
-       .bLength =              sizeof loopback_intf,
-       .bDescriptorType =      USB_DT_INTERFACE,
-
-       .bNumEndpoints =        2,
-       .bInterfaceClass =      USB_CLASS_VENDOR_SPEC,
-       /* .iInterface = DYNAMIC */
-};
-
-/* full speed support: */
-
-static struct usb_endpoint_descriptor fs_loop_source_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-};
-
-static struct usb_endpoint_descriptor fs_loop_sink_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_OUT,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-};
-
-static struct usb_descriptor_header *fs_loopback_descs[] = {
-       (struct usb_descriptor_header *) &loopback_intf,
-       (struct usb_descriptor_header *) &fs_loop_sink_desc,
-       (struct usb_descriptor_header *) &fs_loop_source_desc,
-       NULL,
-};
-
-/* high speed support: */
-
-static struct usb_endpoint_descriptor hs_loop_source_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(512),
-};
-
-static struct usb_endpoint_descriptor hs_loop_sink_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(512),
-};
-
-static struct usb_descriptor_header *hs_loopback_descs[] = {
-       (struct usb_descriptor_header *) &loopback_intf,
-       (struct usb_descriptor_header *) &hs_loop_source_desc,
-       (struct usb_descriptor_header *) &hs_loop_sink_desc,
-       NULL,
-};
-
-/* super speed support: */
-
-static struct usb_endpoint_descriptor ss_loop_source_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(1024),
-};
-
-static struct usb_ss_ep_comp_descriptor ss_loop_source_comp_desc = {
-       .bLength =              USB_DT_SS_EP_COMP_SIZE,
-       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
-       .bMaxBurst =            0,
-       .bmAttributes =         0,
-       .wBytesPerInterval =    0,
-};
-
-static struct usb_endpoint_descriptor ss_loop_sink_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(1024),
-};
-
-static struct usb_ss_ep_comp_descriptor ss_loop_sink_comp_desc = {
-       .bLength =              USB_DT_SS_EP_COMP_SIZE,
-       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
-       .bMaxBurst =            0,
-       .bmAttributes =         0,
-       .wBytesPerInterval =    0,
-};
-
-static struct usb_descriptor_header *ss_loopback_descs[] = {
-       (struct usb_descriptor_header *) &loopback_intf,
-       (struct usb_descriptor_header *) &ss_loop_source_desc,
-       (struct usb_descriptor_header *) &ss_loop_source_comp_desc,
-       (struct usb_descriptor_header *) &ss_loop_sink_desc,
-       (struct usb_descriptor_header *) &ss_loop_sink_comp_desc,
-       NULL,
-};
-
-/* function-specific strings: */
-
-static struct usb_string strings_loopback[] = {
-       [0].s = "loop input to output",
-       {  }                    /* end of list */
-};
-
-static struct usb_gadget_strings stringtab_loop = {
-       .language       = 0x0409,       /* en-us */
-       .strings        = strings_loopback,
-};
-
-static struct usb_gadget_strings *loopback_strings[] = {
-       &stringtab_loop,
-       NULL,
-};
-
-/*-------------------------------------------------------------------------*/
-
-static int loopback_bind(struct usb_configuration *c, struct usb_function *f)
-{
-       struct usb_composite_dev *cdev = c->cdev;
-       struct f_loopback       *loop = func_to_loop(f);
-       int                     id;
-       int ret;
-
-       /* allocate interface ID(s) */
-       id = usb_interface_id(c, f);
-       if (id < 0)
-               return id;
-       loopback_intf.bInterfaceNumber = id;
-
-       id = usb_string_id(cdev);
-       if (id < 0)
-               return id;
-       strings_loopback[0].id = id;
-       loopback_intf.iInterface = id;
-
-       /* allocate endpoints */
-
-       loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_source_desc);
-       if (!loop->in_ep) {
-autoconf_fail:
-               ERROR(cdev, "%s: can't autoconfigure on %s\n",
-                       f->name, cdev->gadget->name);
-               return -ENODEV;
-       }
-       loop->in_ep->driver_data = cdev;        /* claim */
-
-       loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_sink_desc);
-       if (!loop->out_ep)
-               goto autoconf_fail;
-       loop->out_ep->driver_data = cdev;       /* claim */
-
-       /* support high speed hardware */
-       hs_loop_source_desc.bEndpointAddress =
-               fs_loop_source_desc.bEndpointAddress;
-       hs_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress;
-
-       /* support super speed hardware */
-       ss_loop_source_desc.bEndpointAddress =
-               fs_loop_source_desc.bEndpointAddress;
-       ss_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress;
-
-       ret = usb_assign_descriptors(f, fs_loopback_descs, hs_loopback_descs,
-                       ss_loopback_descs);
-       if (ret)
-               return ret;
-
-       DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
-           (gadget_is_superspeed(c->cdev->gadget) ? "super" :
-            (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")),
-                       f->name, loop->in_ep->name, loop->out_ep->name);
-       return 0;
-}
-
-static void lb_free_func(struct usb_function *f)
-{
-       struct f_lb_opts *opts;
-
-       opts = container_of(f->fi, struct f_lb_opts, func_inst);
-
-       mutex_lock(&opts->lock);
-       opts->refcnt--;
-       mutex_unlock(&opts->lock);
-
-       usb_free_all_descriptors(f);
-       kfree(func_to_loop(f));
-}
-
-static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       struct f_loopback       *loop = ep->driver_data;
-       struct usb_composite_dev *cdev = loop->function.config->cdev;
-       int                     status = req->status;
-
-       switch (status) {
-
-       case 0:                         /* normal completion? */
-               if (ep == loop->out_ep) {
-                       /* loop this OUT packet back IN to the host */
-                       req->zero = (req->actual < req->length);
-                       req->length = req->actual;
-                       status = usb_ep_queue(loop->in_ep, req, GFP_ATOMIC);
-                       if (status == 0)
-                               return;
-
-                       /* "should never get here" */
-                       ERROR(cdev, "can't loop %s to %s: %d\n",
-                               ep->name, loop->in_ep->name,
-                               status);
-               }
-
-               /* queue the buffer for some later OUT packet */
-               req->length = buflen;
-               status = usb_ep_queue(loop->out_ep, req, GFP_ATOMIC);
-               if (status == 0)
-                       return;
-
-               /* "should never get here" */
-               /* FALLTHROUGH */
-
-       default:
-               ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name,
-                               status, req->actual, req->length);
-               /* FALLTHROUGH */
-
-       /* NOTE:  since this driver doesn't maintain an explicit record
-        * of requests it submitted (just maintains qlen count), we
-        * rely on the hardware driver to clean up on disconnect or
-        * endpoint disable.
-        */
-       case -ECONNABORTED:             /* hardware forced ep reset */
-       case -ECONNRESET:               /* request dequeued */
-       case -ESHUTDOWN:                /* disconnect from host */
-               free_ep_req(ep, req);
-               return;
-       }
-}
-
-static void disable_loopback(struct f_loopback *loop)
-{
-       struct usb_composite_dev        *cdev;
-
-       cdev = loop->function.config->cdev;
-       disable_endpoints(cdev, loop->in_ep, loop->out_ep, NULL, NULL);
-       VDBG(cdev, "%s disabled\n", loop->function.name);
-}
-
-static inline struct usb_request *lb_alloc_ep_req(struct usb_ep *ep, int len)
-{
-       return alloc_ep_req(ep, len, buflen);
-}
-
-static int
-enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop)
-{
-       int                                     result = 0;
-       struct usb_ep                           *ep;
-       struct usb_request                      *req;
-       unsigned                                i;
-
-       /* one endpoint writes data back IN to the host */
-       ep = loop->in_ep;
-       result = config_ep_by_speed(cdev->gadget, &(loop->function), ep);
-       if (result)
-               return result;
-       result = usb_ep_enable(ep);
-       if (result < 0)
-               return result;
-       ep->driver_data = loop;
-
-       /* one endpoint just reads OUT packets */
-       ep = loop->out_ep;
-       result = config_ep_by_speed(cdev->gadget, &(loop->function), ep);
-       if (result)
-               goto fail0;
-
-       result = usb_ep_enable(ep);
-       if (result < 0) {
-fail0:
-               ep = loop->in_ep;
-               usb_ep_disable(ep);
-               ep->driver_data = NULL;
-               return result;
-       }
-       ep->driver_data = loop;
-
-       /* allocate a bunch of read buffers and queue them all at once.
-        * we buffer at most 'qlen' transfers; fewer if any need more
-        * than 'buflen' bytes each.
-        */
-       for (i = 0; i < qlen && result == 0; i++) {
-               req = lb_alloc_ep_req(ep, 0);
-               if (req) {
-                       req->complete = loopback_complete;
-                       result = usb_ep_queue(ep, req, GFP_ATOMIC);
-                       if (result)
-                               ERROR(cdev, "%s queue req --> %d\n",
-                                               ep->name, result);
-               } else {
-                       usb_ep_disable(ep);
-                       ep->driver_data = NULL;
-                       result = -ENOMEM;
-                       goto fail0;
-               }
-       }
-
-       DBG(cdev, "%s enabled\n", loop->function.name);
-       return result;
-}
-
-static int loopback_set_alt(struct usb_function *f,
-               unsigned intf, unsigned alt)
-{
-       struct f_loopback       *loop = func_to_loop(f);
-       struct usb_composite_dev *cdev = f->config->cdev;
-
-       /* we know alt is zero */
-       if (loop->in_ep->driver_data)
-               disable_loopback(loop);
-       return enable_loopback(cdev, loop);
-}
-
-static void loopback_disable(struct usb_function *f)
-{
-       struct f_loopback       *loop = func_to_loop(f);
-
-       disable_loopback(loop);
-}
-
-static struct usb_function *loopback_alloc(struct usb_function_instance *fi)
-{
-       struct f_loopback       *loop;
-       struct f_lb_opts        *lb_opts;
-
-       loop = kzalloc(sizeof *loop, GFP_KERNEL);
-       if (!loop)
-               return ERR_PTR(-ENOMEM);
-
-       lb_opts = container_of(fi, struct f_lb_opts, func_inst);
-
-       mutex_lock(&lb_opts->lock);
-       lb_opts->refcnt++;
-       mutex_unlock(&lb_opts->lock);
-
-       buflen = lb_opts->bulk_buflen;
-       qlen = lb_opts->qlen;
-       if (!qlen)
-               qlen = 32;
-
-       loop->function.name = "loopback";
-       loop->function.bind = loopback_bind;
-       loop->function.set_alt = loopback_set_alt;
-       loop->function.disable = loopback_disable;
-       loop->function.strings = loopback_strings;
-
-       loop->function.free_func = lb_free_func;
-
-       return &loop->function;
-}
-
-static inline struct f_lb_opts *to_f_lb_opts(struct config_item *item)
-{
-       return container_of(to_config_group(item), struct f_lb_opts,
-                           func_inst.group);
-}
-
-CONFIGFS_ATTR_STRUCT(f_lb_opts);
-CONFIGFS_ATTR_OPS(f_lb_opts);
-
-static void lb_attr_release(struct config_item *item)
-{
-       struct f_lb_opts *lb_opts = to_f_lb_opts(item);
-
-       usb_put_function_instance(&lb_opts->func_inst);
-}
-
-static struct configfs_item_operations lb_item_ops = {
-       .release                = lb_attr_release,
-       .show_attribute         = f_lb_opts_attr_show,
-       .store_attribute        = f_lb_opts_attr_store,
-};
-
-static ssize_t f_lb_opts_qlen_show(struct f_lb_opts *opts, char *page)
-{
-       int result;
-
-       mutex_lock(&opts->lock);
-       result = sprintf(page, "%d", opts->qlen);
-       mutex_unlock(&opts->lock);
-
-       return result;
-}
-
-static ssize_t f_lb_opts_qlen_store(struct f_lb_opts *opts,
-                                   const char *page, size_t len)
-{
-       int ret;
-       u32 num;
-
-       mutex_lock(&opts->lock);
-       if (opts->refcnt) {
-               ret = -EBUSY;
-               goto end;
-       }
-
-       ret = kstrtou32(page, 0, &num);
-       if (ret)
-               goto end;
-
-       opts->qlen = num;
-       ret = len;
-end:
-       mutex_unlock(&opts->lock);
-       return ret;
-}
-
-static struct f_lb_opts_attribute f_lb_opts_qlen =
-       __CONFIGFS_ATTR(qlen, S_IRUGO | S_IWUSR,
-                       f_lb_opts_qlen_show,
-                       f_lb_opts_qlen_store);
-
-static ssize_t f_lb_opts_bulk_buflen_show(struct f_lb_opts *opts, char *page)
-{
-       int result;
-
-       mutex_lock(&opts->lock);
-       result = sprintf(page, "%d", opts->bulk_buflen);
-       mutex_unlock(&opts->lock);
-
-       return result;
-}
-
-static ssize_t f_lb_opts_bulk_buflen_store(struct f_lb_opts *opts,
-                                   const char *page, size_t len)
-{
-       int ret;
-       u32 num;
-
-       mutex_lock(&opts->lock);
-       if (opts->refcnt) {
-               ret = -EBUSY;
-               goto end;
-       }
-
-       ret = kstrtou32(page, 0, &num);
-       if (ret)
-               goto end;
-
-       opts->bulk_buflen = num;
-       ret = len;
-end:
-       mutex_unlock(&opts->lock);
-       return ret;
-}
-
-static struct f_lb_opts_attribute f_lb_opts_bulk_buflen =
-       __CONFIGFS_ATTR(buflen, S_IRUGO | S_IWUSR,
-                       f_lb_opts_bulk_buflen_show,
-                       f_lb_opts_bulk_buflen_store);
-
-static struct configfs_attribute *lb_attrs[] = {
-       &f_lb_opts_qlen.attr,
-       &f_lb_opts_bulk_buflen.attr,
-       NULL,
-};
-
-static struct config_item_type lb_func_type = {
-       .ct_item_ops    = &lb_item_ops,
-       .ct_attrs       = lb_attrs,
-       .ct_owner       = THIS_MODULE,
-};
-
-static void lb_free_instance(struct usb_function_instance *fi)
-{
-       struct f_lb_opts *lb_opts;
-
-       lb_opts = container_of(fi, struct f_lb_opts, func_inst);
-       kfree(lb_opts);
-}
-
-static struct usb_function_instance *loopback_alloc_instance(void)
-{
-       struct f_lb_opts *lb_opts;
-
-       lb_opts = kzalloc(sizeof(*lb_opts), GFP_KERNEL);
-       if (!lb_opts)
-               return ERR_PTR(-ENOMEM);
-       mutex_init(&lb_opts->lock);
-       lb_opts->func_inst.free_func_inst = lb_free_instance;
-       lb_opts->bulk_buflen = GZERO_BULK_BUFLEN;
-       lb_opts->qlen = GZERO_QLEN;
-
-       config_group_init_type_name(&lb_opts->func_inst.group, "",
-                                   &lb_func_type);
-
-       return  &lb_opts->func_inst;
-}
-DECLARE_USB_FUNCTION(Loopback, loopback_alloc_instance, loopback_alloc);
-
-int __init lb_modinit(void)
-{
-       int ret;
-
-       ret = usb_function_register(&Loopbackusb_func);
-       if (ret)
-               return ret;
-       return ret;
-}
-void __exit lb_modexit(void)
-{
-       usb_function_unregister(&Loopbackusb_func);
-}
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c
deleted file mode 100644 (file)
index b963939..0000000
+++ /dev/null
@@ -1,3668 +0,0 @@
-/*
- * f_mass_storage.c -- Mass Storage USB Composite Function
- *
- * Copyright (C) 2003-2008 Alan Stern
- * Copyright (C) 2009 Samsung Electronics
- *                    Author: Michal Nazarewicz <mina86@mina86.com>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions, and the following disclaimer,
- *    without modification.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. The names of the above-listed copyright holders may not be used
- *    to endorse or promote products derived from this software without
- *    specific prior written permission.
- *
- * ALTERNATIVELY, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") as published by the Free Software
- * Foundation, either version 2 of that License or (at your option) any
- * later version.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/*
- * The Mass Storage Function acts as a USB Mass Storage device,
- * appearing to the host as a disk drive or as a CD-ROM drive.  In
- * addition to providing an example of a genuinely useful composite
- * function for a USB device, it also illustrates a technique of
- * double-buffering for increased throughput.
- *
- * For more information about MSF and in particular its module
- * parameters and sysfs interface read the
- * <Documentation/usb/mass-storage.txt> file.
- */
-
-/*
- * MSF is configured by specifying a fsg_config structure.  It has the
- * following fields:
- *
- *     nluns           Number of LUNs function have (anywhere from 1
- *                             to FSG_MAX_LUNS which is 8).
- *     luns            An array of LUN configuration values.  This
- *                             should be filled for each LUN that
- *                             function will include (ie. for "nluns"
- *                             LUNs).  Each element of the array has
- *                             the following fields:
- *     ->filename      The path to the backing file for the LUN.
- *                             Required if LUN is not marked as
- *                             removable.
- *     ->ro            Flag specifying access to the LUN shall be
- *                             read-only.  This is implied if CD-ROM
- *                             emulation is enabled as well as when
- *                             it was impossible to open "filename"
- *                             in R/W mode.
- *     ->removable     Flag specifying that LUN shall be indicated as
- *                             being removable.
- *     ->cdrom         Flag specifying that LUN shall be reported as
- *                             being a CD-ROM.
- *     ->nofua         Flag specifying that FUA flag in SCSI WRITE(10,12)
- *                             commands for this LUN shall be ignored.
- *
- *     vendor_name
- *     product_name
- *     release         Information used as a reply to INQUIRY
- *                             request.  To use default set to NULL,
- *                             NULL, 0xffff respectively.  The first
- *                             field should be 8 and the second 16
- *                             characters or less.
- *
- *     can_stall       Set to permit function to halt bulk endpoints.
- *                             Disabled on some USB devices known not
- *                             to work correctly.  You should set it
- *                             to true.
- *
- * If "removable" is not set for a LUN then a backing file must be
- * specified.  If it is set, then NULL filename means the LUN's medium
- * is not loaded (an empty string as "filename" in the fsg_config
- * structure causes error).  The CD-ROM emulation includes a single
- * data track and no audio tracks; hence there need be only one
- * backing file per LUN.
- *
- * This function is heavily based on "File-backed Storage Gadget" by
- * Alan Stern which in turn is heavily based on "Gadget Zero" by David
- * Brownell.  The driver's SCSI command interface was based on the
- * "Information technology - Small Computer System Interface - 2"
- * document from X3T9.2 Project 375D, Revision 10L, 7-SEP-93,
- * available at <http://www.t10.org/ftp/t10/drafts/s2/s2-r10l.pdf>.
- * The single exception is opcode 0x23 (READ FORMAT CAPACITIES), which
- * was based on the "Universal Serial Bus Mass Storage Class UFI
- * Command Specification" document, Revision 1.0, December 14, 1998,
- * available at
- * <http://www.usb.org/developers/devclass_docs/usbmass-ufi10.pdf>.
- */
-
-/*
- *                             Driver Design
- *
- * The MSF is fairly straightforward.  There is a main kernel
- * thread that handles most of the work.  Interrupt routines field
- * callbacks from the controller driver: bulk- and interrupt-request
- * completion notifications, endpoint-0 events, and disconnect events.
- * Completion events are passed to the main thread by wakeup calls.  Many
- * ep0 requests are handled at interrupt time, but SetInterface,
- * SetConfiguration, and device reset requests are forwarded to the
- * thread in the form of "exceptions" using SIGUSR1 signals (since they
- * should interrupt any ongoing file I/O operations).
- *
- * The thread's main routine implements the standard command/data/status
- * parts of a SCSI interaction.  It and its subroutines are full of tests
- * for pending signals/exceptions -- all this polling is necessary since
- * the kernel has no setjmp/longjmp equivalents.  (Maybe this is an
- * indication that the driver really wants to be running in userspace.)
- * An important point is that so long as the thread is alive it keeps an
- * open reference to the backing file.  This will prevent unmounting
- * the backing file's underlying filesystem and could cause problems
- * during system shutdown, for example.  To prevent such problems, the
- * thread catches INT, TERM, and KILL signals and converts them into
- * an EXIT exception.
- *
- * In normal operation the main thread is started during the gadget's
- * fsg_bind() callback and stopped during fsg_unbind().  But it can
- * also exit when it receives a signal, and there's no point leaving
- * the gadget running when the thread is dead.  As of this moment, MSF
- * provides no way to deregister the gadget when thread dies -- maybe
- * a callback functions is needed.
- *
- * To provide maximum throughput, the driver uses a circular pipeline of
- * buffer heads (struct fsg_buffhd).  In principle the pipeline can be
- * arbitrarily long; in practice the benefits don't justify having more
- * than 2 stages (i.e., double buffering).  But it helps to think of the
- * pipeline as being a long one.  Each buffer head contains a bulk-in and
- * a bulk-out request pointer (since the buffer can be used for both
- * output and input -- directions always are given from the host's
- * point of view) as well as a pointer to the buffer and various state
- * variables.
- *
- * Use of the pipeline follows a simple protocol.  There is a variable
- * (fsg->next_buffhd_to_fill) that points to the next buffer head to use.
- * At any time that buffer head may still be in use from an earlier
- * request, so each buffer head has a state variable indicating whether
- * it is EMPTY, FULL, or BUSY.  Typical use involves waiting for the
- * buffer head to be EMPTY, filling the buffer either by file I/O or by
- * USB I/O (during which the buffer head is BUSY), and marking the buffer
- * head FULL when the I/O is complete.  Then the buffer will be emptied
- * (again possibly by USB I/O, during which it is marked BUSY) and
- * finally marked EMPTY again (possibly by a completion routine).
- *
- * A module parameter tells the driver to avoid stalling the bulk
- * endpoints wherever the transport specification allows.  This is
- * necessary for some UDCs like the SuperH, which cannot reliably clear a
- * halt on a bulk endpoint.  However, under certain circumstances the
- * Bulk-only specification requires a stall.  In such cases the driver
- * will halt the endpoint and set a flag indicating that it should clear
- * the halt in software during the next device reset.  Hopefully this
- * will permit everything to work correctly.  Furthermore, although the
- * specification allows the bulk-out endpoint to halt when the host sends
- * too much data, implementing this would cause an unavoidable race.
- * The driver will always use the "no-stall" approach for OUT transfers.
- *
- * One subtle point concerns sending status-stage responses for ep0
- * requests.  Some of these requests, such as device reset, can involve
- * interrupting an ongoing file I/O operation, which might take an
- * arbitrarily long time.  During that delay the host might give up on
- * the original ep0 request and issue a new one.  When that happens the
- * driver should not notify the host about completion of the original
- * request, as the host will no longer be waiting for it.  So the driver
- * assigns to each ep0 request a unique tag, and it keeps track of the
- * tag value of the request associated with a long-running exception
- * (device-reset, interface-change, or configuration-change).  When the
- * exception handler is finished, the status-stage response is submitted
- * only if the current ep0 request tag is equal to the exception request
- * tag.  Thus only the most recently received ep0 request will get a
- * status-stage response.
- *
- * Warning: This driver source file is too long.  It ought to be split up
- * into a header file plus about 3 separate .c files, to handle the details
- * of the Gadget, USB Mass Storage, and SCSI protocols.
- */
-
-
-/* #define VERBOSE_DEBUG */
-/* #define DUMP_MSGS */
-
-#include <linux/blkdev.h>
-#include <linux/completion.h>
-#include <linux/dcache.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/fcntl.h>
-#include <linux/file.h>
-#include <linux/fs.h>
-#include <linux/kref.h>
-#include <linux/kthread.h>
-#include <linux/limits.h>
-#include <linux/rwsem.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/string.h>
-#include <linux/freezer.h>
-#include <linux/module.h>
-
-#include <linux/usb/ch9.h>
-#include <linux/usb/gadget.h>
-#include <linux/usb/composite.h>
-
-#include "gadget_chips.h"
-#include "configfs.h"
-
-
-/*------------------------------------------------------------------------*/
-
-#define FSG_DRIVER_DESC                "Mass Storage Function"
-#define FSG_DRIVER_VERSION     "2009/09/11"
-
-static const char fsg_string_interface[] = "Mass Storage";
-
-#include "storage_common.h"
-#include "f_mass_storage.h"
-
-/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */
-static struct usb_string               fsg_strings[] = {
-       {FSG_STRING_INTERFACE,          fsg_string_interface},
-       {}
-};
-
-static struct usb_gadget_strings       fsg_stringtab = {
-       .language       = 0x0409,               /* en-us */
-       .strings        = fsg_strings,
-};
-
-static struct usb_gadget_strings *fsg_strings_array[] = {
-       &fsg_stringtab,
-       NULL,
-};
-
-/*-------------------------------------------------------------------------*/
-
-struct fsg_dev;
-struct fsg_common;
-
-/* Data shared by all the FSG instances. */
-struct fsg_common {
-       struct usb_gadget       *gadget;
-       struct usb_composite_dev *cdev;
-       struct fsg_dev          *fsg, *new_fsg;
-       wait_queue_head_t       fsg_wait;
-
-       /* filesem protects: backing files in use */
-       struct rw_semaphore     filesem;
-
-       /* lock protects: state, all the req_busy's */
-       spinlock_t              lock;
-
-       struct usb_ep           *ep0;           /* Copy of gadget->ep0 */
-       struct usb_request      *ep0req;        /* Copy of cdev->req */
-       unsigned int            ep0_req_tag;
-
-       struct fsg_buffhd       *next_buffhd_to_fill;
-       struct fsg_buffhd       *next_buffhd_to_drain;
-       struct fsg_buffhd       *buffhds;
-       unsigned int            fsg_num_buffers;
-
-       int                     cmnd_size;
-       u8                      cmnd[MAX_COMMAND_SIZE];
-
-       unsigned int            nluns;
-       unsigned int            lun;
-       struct fsg_lun          **luns;
-       struct fsg_lun          *curlun;
-
-       unsigned int            bulk_out_maxpacket;
-       enum fsg_state          state;          /* For exception handling */
-       unsigned int            exception_req_tag;
-
-       enum data_direction     data_dir;
-       u32                     data_size;
-       u32                     data_size_from_cmnd;
-       u32                     tag;
-       u32                     residue;
-       u32                     usb_amount_left;
-
-       unsigned int            can_stall:1;
-       unsigned int            free_storage_on_release:1;
-       unsigned int            phase_error:1;
-       unsigned int            short_packet_received:1;
-       unsigned int            bad_lun_okay:1;
-       unsigned int            running:1;
-       unsigned int            sysfs:1;
-
-       int                     thread_wakeup_needed;
-       struct completion       thread_notifier;
-       struct task_struct      *thread_task;
-
-       /* Callback functions. */
-       const struct fsg_operations     *ops;
-       /* Gadget's private data. */
-       void                    *private_data;
-
-       /*
-        * Vendor (8 chars), product (16 chars), release (4
-        * hexadecimal digits) and NUL byte
-        */
-       char inquiry_string[8 + 16 + 4 + 1];
-
-       struct kref             ref;
-};
-
-struct fsg_dev {
-       struct usb_function     function;
-       struct usb_gadget       *gadget;        /* Copy of cdev->gadget */
-       struct fsg_common       *common;
-
-       u16                     interface_number;
-
-       unsigned int            bulk_in_enabled:1;
-       unsigned int            bulk_out_enabled:1;
-
-       unsigned long           atomic_bitflags;
-#define IGNORE_BULK_OUT                0
-
-       struct usb_ep           *bulk_in;
-       struct usb_ep           *bulk_out;
-};
-
-static inline int __fsg_is_set(struct fsg_common *common,
-                              const char *func, unsigned line)
-{
-       if (common->fsg)
-               return 1;
-       ERROR(common, "common->fsg is NULL in %s at %u\n", func, line);
-       WARN_ON(1);
-       return 0;
-}
-
-#define fsg_is_set(common) likely(__fsg_is_set(common, __func__, __LINE__))
-
-static inline struct fsg_dev *fsg_from_func(struct usb_function *f)
-{
-       return container_of(f, struct fsg_dev, function);
-}
-
-typedef void (*fsg_routine_t)(struct fsg_dev *);
-
-static int exception_in_progress(struct fsg_common *common)
-{
-       return common->state > FSG_STATE_IDLE;
-}
-
-/* Make bulk-out requests be divisible by the maxpacket size */
-static void set_bulk_out_req_length(struct fsg_common *common,
-                                   struct fsg_buffhd *bh, unsigned int length)
-{
-       unsigned int    rem;
-
-       bh->bulk_out_intended_length = length;
-       rem = length % common->bulk_out_maxpacket;
-       if (rem > 0)
-               length += common->bulk_out_maxpacket - rem;
-       bh->outreq->length = length;
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep)
-{
-       const char      *name;
-
-       if (ep == fsg->bulk_in)
-               name = "bulk-in";
-       else if (ep == fsg->bulk_out)
-               name = "bulk-out";
-       else
-               name = ep->name;
-       DBG(fsg, "%s set halt\n", name);
-       return usb_ep_set_halt(ep);
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-/* These routines may be called in process context or in_irq */
-
-/* Caller must hold fsg->lock */
-static void wakeup_thread(struct fsg_common *common)
-{
-       smp_wmb();      /* ensure the write of bh->state is complete */
-       /* Tell the main thread that something has happened */
-       common->thread_wakeup_needed = 1;
-       if (common->thread_task)
-               wake_up_process(common->thread_task);
-}
-
-static void raise_exception(struct fsg_common *common, enum fsg_state new_state)
-{
-       unsigned long           flags;
-
-       /*
-        * Do nothing if a higher-priority exception is already in progress.
-        * If a lower-or-equal priority exception is in progress, preempt it
-        * and notify the main thread by sending it a signal.
-        */
-       spin_lock_irqsave(&common->lock, flags);
-       if (common->state <= new_state) {
-               common->exception_req_tag = common->ep0_req_tag;
-               common->state = new_state;
-               if (common->thread_task)
-                       send_sig_info(SIGUSR1, SEND_SIG_FORCED,
-                                     common->thread_task);
-       }
-       spin_unlock_irqrestore(&common->lock, flags);
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-static int ep0_queue(struct fsg_common *common)
-{
-       int     rc;
-
-       rc = usb_ep_queue(common->ep0, common->ep0req, GFP_ATOMIC);
-       common->ep0->driver_data = common;
-       if (rc != 0 && rc != -ESHUTDOWN) {
-               /* We can't do much more than wait for a reset */
-               WARNING(common, "error in submission: %s --> %d\n",
-                       common->ep0->name, rc);
-       }
-       return rc;
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-/* Completion handlers. These always run in_irq. */
-
-static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       struct fsg_common       *common = ep->driver_data;
-       struct fsg_buffhd       *bh = req->context;
-
-       if (req->status || req->actual != req->length)
-               DBG(common, "%s --> %d, %u/%u\n", __func__,
-                   req->status, req->actual, req->length);
-       if (req->status == -ECONNRESET)         /* Request was cancelled */
-               usb_ep_fifo_flush(ep);
-
-       /* Hold the lock while we update the request and buffer states */
-       smp_wmb();
-       spin_lock(&common->lock);
-       bh->inreq_busy = 0;
-       bh->state = BUF_STATE_EMPTY;
-       wakeup_thread(common);
-       spin_unlock(&common->lock);
-}
-
-static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       struct fsg_common       *common = ep->driver_data;
-       struct fsg_buffhd       *bh = req->context;
-
-       dump_msg(common, "bulk-out", req->buf, req->actual);
-       if (req->status || req->actual != bh->bulk_out_intended_length)
-               DBG(common, "%s --> %d, %u/%u\n", __func__,
-                   req->status, req->actual, bh->bulk_out_intended_length);
-       if (req->status == -ECONNRESET)         /* Request was cancelled */
-               usb_ep_fifo_flush(ep);
-
-       /* Hold the lock while we update the request and buffer states */
-       smp_wmb();
-       spin_lock(&common->lock);
-       bh->outreq_busy = 0;
-       bh->state = BUF_STATE_FULL;
-       wakeup_thread(common);
-       spin_unlock(&common->lock);
-}
-
-static int fsg_setup(struct usb_function *f,
-                    const struct usb_ctrlrequest *ctrl)
-{
-       struct fsg_dev          *fsg = fsg_from_func(f);
-       struct usb_request      *req = fsg->common->ep0req;
-       u16                     w_index = le16_to_cpu(ctrl->wIndex);
-       u16                     w_value = le16_to_cpu(ctrl->wValue);
-       u16                     w_length = le16_to_cpu(ctrl->wLength);
-
-       if (!fsg_is_set(fsg->common))
-               return -EOPNOTSUPP;
-
-       ++fsg->common->ep0_req_tag;     /* Record arrival of a new request */
-       req->context = NULL;
-       req->length = 0;
-       dump_msg(fsg, "ep0-setup", (u8 *) ctrl, sizeof(*ctrl));
-
-       switch (ctrl->bRequest) {
-
-       case US_BULK_RESET_REQUEST:
-               if (ctrl->bRequestType !=
-                   (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE))
-                       break;
-               if (w_index != fsg->interface_number || w_value != 0 ||
-                               w_length != 0)
-                       return -EDOM;
-
-               /*
-                * Raise an exception to stop the current operation
-                * and reinitialize our state.
-                */
-               DBG(fsg, "bulk reset request\n");
-               raise_exception(fsg->common, FSG_STATE_RESET);
-               return USB_GADGET_DELAYED_STATUS;
-
-       case US_BULK_GET_MAX_LUN:
-               if (ctrl->bRequestType !=
-                   (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE))
-                       break;
-               if (w_index != fsg->interface_number || w_value != 0 ||
-                               w_length != 1)
-                       return -EDOM;
-               VDBG(fsg, "get max LUN\n");
-               *(u8 *)req->buf = fsg->common->nluns - 1;
-
-               /* Respond with data/status */
-               req->length = min((u16)1, w_length);
-               return ep0_queue(fsg->common);
-       }
-
-       VDBG(fsg,
-            "unknown class-specific control req %02x.%02x v%04x i%04x l%u\n",
-            ctrl->bRequestType, ctrl->bRequest,
-            le16_to_cpu(ctrl->wValue), w_index, w_length);
-       return -EOPNOTSUPP;
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-/* All the following routines run in process context */
-
-/* Use this for bulk or interrupt transfers, not ep0 */
-static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep,
-                          struct usb_request *req, int *pbusy,
-                          enum fsg_buffer_state *state)
-{
-       int     rc;
-
-       if (ep == fsg->bulk_in)
-               dump_msg(fsg, "bulk-in", req->buf, req->length);
-
-       spin_lock_irq(&fsg->common->lock);
-       *pbusy = 1;
-       *state = BUF_STATE_BUSY;
-       spin_unlock_irq(&fsg->common->lock);
-       rc = usb_ep_queue(ep, req, GFP_KERNEL);
-       if (rc != 0) {
-               *pbusy = 0;
-               *state = BUF_STATE_EMPTY;
-
-               /* We can't do much more than wait for a reset */
-
-               /*
-                * Note: currently the net2280 driver fails zero-length
-                * submissions if DMA is enabled.
-                */
-               if (rc != -ESHUTDOWN &&
-                   !(rc == -EOPNOTSUPP && req->length == 0))
-                       WARNING(fsg, "error in submission: %s --> %d\n",
-                               ep->name, rc);
-       }
-}
-
-static bool start_in_transfer(struct fsg_common *common, struct fsg_buffhd *bh)
-{
-       if (!fsg_is_set(common))
-               return false;
-       start_transfer(common->fsg, common->fsg->bulk_in,
-                      bh->inreq, &bh->inreq_busy, &bh->state);
-       return true;
-}
-
-static bool start_out_transfer(struct fsg_common *common, struct fsg_buffhd *bh)
-{
-       if (!fsg_is_set(common))
-               return false;
-       start_transfer(common->fsg, common->fsg->bulk_out,
-                      bh->outreq, &bh->outreq_busy, &bh->state);
-       return true;
-}
-
-static int sleep_thread(struct fsg_common *common, bool can_freeze)
-{
-       int     rc = 0;
-
-       /* Wait until a signal arrives or we are woken up */
-       for (;;) {
-               if (can_freeze)
-                       try_to_freeze();
-               set_current_state(TASK_INTERRUPTIBLE);
-               if (signal_pending(current)) {
-                       rc = -EINTR;
-                       break;
-               }
-               if (common->thread_wakeup_needed)
-                       break;
-               schedule();
-       }
-       __set_current_state(TASK_RUNNING);
-       common->thread_wakeup_needed = 0;
-       smp_rmb();      /* ensure the latest bh->state is visible */
-       return rc;
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-static int do_read(struct fsg_common *common)
-{
-       struct fsg_lun          *curlun = common->curlun;
-       u32                     lba;
-       struct fsg_buffhd       *bh;
-       int                     rc;
-       u32                     amount_left;
-       loff_t                  file_offset, file_offset_tmp;
-       unsigned int            amount;
-       ssize_t                 nread;
-
-       /*
-        * Get the starting Logical Block Address and check that it's
-        * not too big.
-        */
-       if (common->cmnd[0] == READ_6)
-               lba = get_unaligned_be24(&common->cmnd[1]);
-       else {
-               lba = get_unaligned_be32(&common->cmnd[2]);
-
-               /*
-                * We allow DPO (Disable Page Out = don't save data in the
-                * cache) and FUA (Force Unit Access = don't read from the
-                * cache), but we don't implement them.
-                */
-               if ((common->cmnd[1] & ~0x18) != 0) {
-                       curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
-                       return -EINVAL;
-               }
-       }
-       if (lba >= curlun->num_sectors) {
-               curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
-               return -EINVAL;
-       }
-       file_offset = ((loff_t) lba) << curlun->blkbits;
-
-       /* Carry out the file reads */
-       amount_left = common->data_size_from_cmnd;
-       if (unlikely(amount_left == 0))
-               return -EIO;            /* No default reply */
-
-       for (;;) {
-               /*
-                * Figure out how much we need to read:
-                * Try to read the remaining amount.
-                * But don't read more than the buffer size.
-                * And don't try to read past the end of the file.
-                */
-               amount = min(amount_left, FSG_BUFLEN);
-               amount = min((loff_t)amount,
-                            curlun->file_length - file_offset);
-
-               /* Wait for the next buffer to become available */
-               bh = common->next_buffhd_to_fill;
-               while (bh->state != BUF_STATE_EMPTY) {
-                       rc = sleep_thread(common, false);
-                       if (rc)
-                               return rc;
-               }
-
-               /*
-                * If we were asked to read past the end of file,
-                * end with an empty buffer.
-                */
-               if (amount == 0) {
-                       curlun->sense_data =
-                                       SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
-                       curlun->sense_data_info =
-                                       file_offset >> curlun->blkbits;
-                       curlun->info_valid = 1;
-                       bh->inreq->length = 0;
-                       bh->state = BUF_STATE_FULL;
-                       break;
-               }
-
-               /* Perform the read */
-               file_offset_tmp = file_offset;
-               nread = vfs_read(curlun->filp,
-                                (char __user *)bh->buf,
-                                amount, &file_offset_tmp);
-               VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,
-                     (unsigned long long)file_offset, (int)nread);
-               if (signal_pending(current))
-                       return -EINTR;
-
-               if (nread < 0) {
-                       LDBG(curlun, "error in file read: %d\n", (int)nread);
-                       nread = 0;
-               } else if (nread < amount) {
-                       LDBG(curlun, "partial file read: %d/%u\n",
-                            (int)nread, amount);
-                       nread = round_down(nread, curlun->blksize);
-               }
-               file_offset  += nread;
-               amount_left  -= nread;
-               common->residue -= nread;
-
-               /*
-                * Except at the end of the transfer, nread will be
-                * equal to the buffer size, which is divisible by the
-                * bulk-in maxpacket size.
-                */
-               bh->inreq->length = nread;
-               bh->state = BUF_STATE_FULL;
-
-               /* If an error occurred, report it and its position */
-               if (nread < amount) {
-                       curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
-                       curlun->sense_data_info =
-                                       file_offset >> curlun->blkbits;
-                       curlun->info_valid = 1;
-                       break;
-               }
-
-               if (amount_left == 0)
-                       break;          /* No more left to read */
-
-               /* Send this buffer and go read some more */
-               bh->inreq->zero = 0;
-               if (!start_in_transfer(common, bh))
-                       /* Don't know what to do if common->fsg is NULL */
-                       return -EIO;
-               common->next_buffhd_to_fill = bh->next;
-       }
-
-       return -EIO;            /* No default reply */
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-static int do_write(struct fsg_common *common)
-{
-       struct fsg_lun          *curlun = common->curlun;
-       u32                     lba;
-       struct fsg_buffhd       *bh;
-       int                     get_some_more;
-       u32                     amount_left_to_req, amount_left_to_write;
-       loff_t                  usb_offset, file_offset, file_offset_tmp;
-       unsigned int            amount;
-       ssize_t                 nwritten;
-       int                     rc;
-
-       if (curlun->ro) {
-               curlun->sense_data = SS_WRITE_PROTECTED;
-               return -EINVAL;
-       }
-       spin_lock(&curlun->filp->f_lock);
-       curlun->filp->f_flags &= ~O_SYNC;       /* Default is not to wait */
-       spin_unlock(&curlun->filp->f_lock);
-
-       /*
-        * Get the starting Logical Block Address and check that it's
-        * not too big
-        */
-       if (common->cmnd[0] == WRITE_6)
-               lba = get_unaligned_be24(&common->cmnd[1]);
-       else {
-               lba = get_unaligned_be32(&common->cmnd[2]);
-
-               /*
-                * We allow DPO (Disable Page Out = don't save data in the
-                * cache) and FUA (Force Unit Access = write directly to the
-                * medium).  We don't implement DPO; we implement FUA by
-                * performing synchronous output.
-                */
-               if (common->cmnd[1] & ~0x18) {
-                       curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
-                       return -EINVAL;
-               }
-               if (!curlun->nofua && (common->cmnd[1] & 0x08)) { /* FUA */
-                       spin_lock(&curlun->filp->f_lock);
-                       curlun->filp->f_flags |= O_SYNC;
-                       spin_unlock(&curlun->filp->f_lock);
-               }
-       }
-       if (lba >= curlun->num_sectors) {
-               curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
-               return -EINVAL;
-       }
-
-       /* Carry out the file writes */
-       get_some_more = 1;
-       file_offset = usb_offset = ((loff_t) lba) << curlun->blkbits;
-       amount_left_to_req = common->data_size_from_cmnd;
-       amount_left_to_write = common->data_size_from_cmnd;
-
-       while (amount_left_to_write > 0) {
-
-               /* Queue a request for more data from the host */
-               bh = common->next_buffhd_to_fill;
-               if (bh->state == BUF_STATE_EMPTY && get_some_more) {
-
-                       /*
-                        * Figure out how much we want to get:
-                        * Try to get the remaining amount,
-                        * but not more than the buffer size.
-                        */
-                       amount = min(amount_left_to_req, FSG_BUFLEN);
-
-                       /* Beyond the end of the backing file? */
-                       if (usb_offset >= curlun->file_length) {
-                               get_some_more = 0;
-                               curlun->sense_data =
-                                       SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
-                               curlun->sense_data_info =
-                                       usb_offset >> curlun->blkbits;
-                               curlun->info_valid = 1;
-                               continue;
-                       }
-
-                       /* Get the next buffer */
-                       usb_offset += amount;
-                       common->usb_amount_left -= amount;
-                       amount_left_to_req -= amount;
-                       if (amount_left_to_req == 0)
-                               get_some_more = 0;
-
-                       /*
-                        * Except at the end of the transfer, amount will be
-                        * equal to the buffer size, which is divisible by
-                        * the bulk-out maxpacket size.
-                        */
-                       set_bulk_out_req_length(common, bh, amount);
-                       if (!start_out_transfer(common, bh))
-                               /* Dunno what to do if common->fsg is NULL */
-                               return -EIO;
-                       common->next_buffhd_to_fill = bh->next;
-                       continue;
-               }
-
-               /* Write the received data to the backing file */
-               bh = common->next_buffhd_to_drain;
-               if (bh->state == BUF_STATE_EMPTY && !get_some_more)
-                       break;                  /* We stopped early */
-               if (bh->state == BUF_STATE_FULL) {
-                       smp_rmb();
-                       common->next_buffhd_to_drain = bh->next;
-                       bh->state = BUF_STATE_EMPTY;
-
-                       /* Did something go wrong with the transfer? */
-                       if (bh->outreq->status != 0) {
-                               curlun->sense_data = SS_COMMUNICATION_FAILURE;
-                               curlun->sense_data_info =
-                                       file_offset >> curlun->blkbits;
-                               curlun->info_valid = 1;
-                               break;
-                       }
-
-                       amount = bh->outreq->actual;
-                       if (curlun->file_length - file_offset < amount) {
-                               LERROR(curlun,
-                                      "write %u @ %llu beyond end %llu\n",
-                                      amount, (unsigned long long)file_offset,
-                                      (unsigned long long)curlun->file_length);
-                               amount = curlun->file_length - file_offset;
-                       }
-
-                       /* Don't accept excess data.  The spec doesn't say
-                        * what to do in this case.  We'll ignore the error.
-                        */
-                       amount = min(amount, bh->bulk_out_intended_length);
-
-                       /* Don't write a partial block */
-                       amount = round_down(amount, curlun->blksize);
-                       if (amount == 0)
-                               goto empty_write;
-
-                       /* Perform the write */
-                       file_offset_tmp = file_offset;
-                       nwritten = vfs_write(curlun->filp,
-                                            (char __user *)bh->buf,
-                                            amount, &file_offset_tmp);
-                       VLDBG(curlun, "file write %u @ %llu -> %d\n", amount,
-                             (unsigned long long)file_offset, (int)nwritten);
-                       if (signal_pending(current))
-                               return -EINTR;          /* Interrupted! */
-
-                       if (nwritten < 0) {
-                               LDBG(curlun, "error in file write: %d\n",
-                                    (int)nwritten);
-                               nwritten = 0;
-                       } else if (nwritten < amount) {
-                               LDBG(curlun, "partial file write: %d/%u\n",
-                                    (int)nwritten, amount);
-                               nwritten = round_down(nwritten, curlun->blksize);
-                       }
-                       file_offset += nwritten;
-                       amount_left_to_write -= nwritten;
-                       common->residue -= nwritten;
-
-                       /* If an error occurred, report it and its position */
-                       if (nwritten < amount) {
-                               curlun->sense_data = SS_WRITE_ERROR;
-                               curlun->sense_data_info =
-                                       file_offset >> curlun->blkbits;
-                               curlun->info_valid = 1;
-                               break;
-                       }
-
- empty_write:
-                       /* Did the host decide to stop early? */
-                       if (bh->outreq->actual < bh->bulk_out_intended_length) {
-                               common->short_packet_received = 1;
-                               break;
-                       }
-                       continue;
-               }
-
-               /* Wait for something to happen */
-               rc = sleep_thread(common, false);
-               if (rc)
-                       return rc;
-       }
-
-       return -EIO;            /* No default reply */
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-static int do_synchronize_cache(struct fsg_common *common)
-{
-       struct fsg_lun  *curlun = common->curlun;
-       int             rc;
-
-       /* We ignore the requested LBA and write out all file's
-        * dirty data buffers. */
-       rc = fsg_lun_fsync_sub(curlun);
-       if (rc)
-               curlun->sense_data = SS_WRITE_ERROR;
-       return 0;
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-static void invalidate_sub(struct fsg_lun *curlun)
-{
-       struct file     *filp = curlun->filp;
-       struct inode    *inode = file_inode(filp);
-       unsigned long   rc;
-
-       rc = invalidate_mapping_pages(inode->i_mapping, 0, -1);
-       VLDBG(curlun, "invalidate_mapping_pages -> %ld\n", rc);
-}
-
-static int do_verify(struct fsg_common *common)
-{
-       struct fsg_lun          *curlun = common->curlun;
-       u32                     lba;
-       u32                     verification_length;
-       struct fsg_buffhd       *bh = common->next_buffhd_to_fill;
-       loff_t                  file_offset, file_offset_tmp;
-       u32                     amount_left;
-       unsigned int            amount;
-       ssize_t                 nread;
-
-       /*
-        * Get the starting Logical Block Address and check that it's
-        * not too big.
-        */
-       lba = get_unaligned_be32(&common->cmnd[2]);
-       if (lba >= curlun->num_sectors) {
-               curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
-               return -EINVAL;
-       }
-
-       /*
-        * We allow DPO (Disable Page Out = don't save data in the
-        * cache) but we don't implement it.
-        */
-       if (common->cmnd[1] & ~0x10) {
-               curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
-               return -EINVAL;
-       }
-
-       verification_length = get_unaligned_be16(&common->cmnd[7]);
-       if (unlikely(verification_length == 0))
-               return -EIO;            /* No default reply */
-
-       /* Prepare to carry out the file verify */
-       amount_left = verification_length << curlun->blkbits;
-       file_offset = ((loff_t) lba) << curlun->blkbits;
-
-       /* Write out all the dirty buffers before invalidating them */
-       fsg_lun_fsync_sub(curlun);
-       if (signal_pending(current))
-               return -EINTR;
-
-       invalidate_sub(curlun);
-       if (signal_pending(current))
-               return -EINTR;
-
-       /* Just try to read the requested blocks */
-       while (amount_left > 0) {
-               /*
-                * Figure out how much we need to read:
-                * Try to read the remaining amount, but not more than
-                * the buffer size.
-                * And don't try to read past the end of the file.
-                */
-               amount = min(amount_left, FSG_BUFLEN);
-               amount = min((loff_t)amount,
-                            curlun->file_length - file_offset);
-               if (amount == 0) {
-                       curlun->sense_data =
-                                       SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
-                       curlun->sense_data_info =
-                               file_offset >> curlun->blkbits;
-                       curlun->info_valid = 1;
-                       break;
-               }
-
-               /* Perform the read */
-               file_offset_tmp = file_offset;
-               nread = vfs_read(curlun->filp,
-                               (char __user *) bh->buf,
-                               amount, &file_offset_tmp);
-               VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,
-                               (unsigned long long) file_offset,
-                               (int) nread);
-               if (signal_pending(current))
-                       return -EINTR;
-
-               if (nread < 0) {
-                       LDBG(curlun, "error in file verify: %d\n", (int)nread);
-                       nread = 0;
-               } else if (nread < amount) {
-                       LDBG(curlun, "partial file verify: %d/%u\n",
-                            (int)nread, amount);
-                       nread = round_down(nread, curlun->blksize);
-               }
-               if (nread == 0) {
-                       curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
-                       curlun->sense_data_info =
-                               file_offset >> curlun->blkbits;
-                       curlun->info_valid = 1;
-                       break;
-               }
-               file_offset += nread;
-               amount_left -= nread;
-       }
-       return 0;
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh)
-{
-       struct fsg_lun *curlun = common->curlun;
-       u8      *buf = (u8 *) bh->buf;
-
-       if (!curlun) {          /* Unsupported LUNs are okay */
-               common->bad_lun_okay = 1;
-               memset(buf, 0, 36);
-               buf[0] = 0x7f;          /* Unsupported, no device-type */
-               buf[4] = 31;            /* Additional length */
-               return 36;
-       }
-
-       buf[0] = curlun->cdrom ? TYPE_ROM : TYPE_DISK;
-       buf[1] = curlun->removable ? 0x80 : 0;
-       buf[2] = 2;             /* ANSI SCSI level 2 */
-       buf[3] = 2;             /* SCSI-2 INQUIRY data format */
-       buf[4] = 31;            /* Additional length */
-       buf[5] = 0;             /* No special options */
-       buf[6] = 0;
-       buf[7] = 0;
-       memcpy(buf + 8, common->inquiry_string, sizeof common->inquiry_string);
-       return 36;
-}
-
-static int do_request_sense(struct fsg_common *common, struct fsg_buffhd *bh)
-{
-       struct fsg_lun  *curlun = common->curlun;
-       u8              *buf = (u8 *) bh->buf;
-       u32             sd, sdinfo;
-       int             valid;
-
-       /*
-        * From the SCSI-2 spec., section 7.9 (Unit attention condition):
-        *
-        * If a REQUEST SENSE command is received from an initiator
-        * with a pending unit attention condition (before the target
-        * generates the contingent allegiance condition), then the
-        * target shall either:
-        *   a) report any pending sense data and preserve the unit
-        *      attention condition on the logical unit, or,
-        *   b) report the unit attention condition, may discard any
-        *      pending sense data, and clear the unit attention
-        *      condition on the logical unit for that initiator.
-        *
-        * FSG normally uses option a); enable this code to use option b).
-        */
-#if 0
-       if (curlun && curlun->unit_attention_data != SS_NO_SENSE) {
-               curlun->sense_data = curlun->unit_attention_data;
-               curlun->unit_attention_data = SS_NO_SENSE;
-       }
-#endif
-
-       if (!curlun) {          /* Unsupported LUNs are okay */
-               common->bad_lun_okay = 1;
-               sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
-               sdinfo = 0;
-               valid = 0;
-       } else {
-               sd = curlun->sense_data;
-               sdinfo = curlun->sense_data_info;
-               valid = curlun->info_valid << 7;
-               curlun->sense_data = SS_NO_SENSE;
-               curlun->sense_data_info = 0;
-               curlun->info_valid = 0;
-       }
-
-       memset(buf, 0, 18);
-       buf[0] = valid | 0x70;                  /* Valid, current error */
-       buf[2] = SK(sd);
-       put_unaligned_be32(sdinfo, &buf[3]);    /* Sense information */
-       buf[7] = 18 - 8;                        /* Additional sense length */
-       buf[12] = ASC(sd);
-       buf[13] = ASCQ(sd);
-       return 18;
-}
-
-static int do_read_capacity(struct fsg_common *common, struct fsg_buffhd *bh)
-{
-       struct fsg_lun  *curlun = common->curlun;
-       u32             lba = get_unaligned_be32(&common->cmnd[2]);
-       int             pmi = common->cmnd[8];
-       u8              *buf = (u8 *)bh->buf;
-
-       /* Check the PMI and LBA fields */
-       if (pmi > 1 || (pmi == 0 && lba != 0)) {
-               curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
-               return -EINVAL;
-       }
-
-       put_unaligned_be32(curlun->num_sectors - 1, &buf[0]);
-                                               /* Max logical block */
-       put_unaligned_be32(curlun->blksize, &buf[4]);/* Block length */
-       return 8;
-}
-
-static int do_read_header(struct fsg_common *common, struct fsg_buffhd *bh)
-{
-       struct fsg_lun  *curlun = common->curlun;
-       int             msf = common->cmnd[1] & 0x02;
-       u32             lba = get_unaligned_be32(&common->cmnd[2]);
-       u8              *buf = (u8 *)bh->buf;
-
-       if (common->cmnd[1] & ~0x02) {          /* Mask away MSF */
-               curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
-               return -EINVAL;
-       }
-       if (lba >= curlun->num_sectors) {
-               curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
-               return -EINVAL;
-       }
-
-       memset(buf, 0, 8);
-       buf[0] = 0x01;          /* 2048 bytes of user data, rest is EC */
-       store_cdrom_address(&buf[4], msf, lba);
-       return 8;
-}
-
-static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh)
-{
-       struct fsg_lun  *curlun = common->curlun;
-       int             msf = common->cmnd[1] & 0x02;
-       int             start_track = common->cmnd[6];
-       u8              *buf = (u8 *)bh->buf;
-
-       if ((common->cmnd[1] & ~0x02) != 0 ||   /* Mask away MSF */
-                       start_track > 1) {
-               curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
-               return -EINVAL;
-       }
-
-       memset(buf, 0, 20);
-       buf[1] = (20-2);                /* TOC data length */
-       buf[2] = 1;                     /* First track number */
-       buf[3] = 1;                     /* Last track number */
-       buf[5] = 0x16;                  /* Data track, copying allowed */
-       buf[6] = 0x01;                  /* Only track is number 1 */
-       store_cdrom_address(&buf[8], msf, 0);
-
-       buf[13] = 0x16;                 /* Lead-out track is data */
-       buf[14] = 0xAA;                 /* Lead-out track number */
-       store_cdrom_address(&buf[16], msf, curlun->num_sectors);
-       return 20;
-}
-
-static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh)
-{
-       struct fsg_lun  *curlun = common->curlun;
-       int             mscmnd = common->cmnd[0];
-       u8              *buf = (u8 *) bh->buf;
-       u8              *buf0 = buf;
-       int             pc, page_code;
-       int             changeable_values, all_pages;
-       int             valid_page = 0;
-       int             len, limit;
-
-       if ((common->cmnd[1] & ~0x08) != 0) {   /* Mask away DBD */
-               curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
-               return -EINVAL;
-       }
-       pc = common->cmnd[2] >> 6;
-       page_code = common->cmnd[2] & 0x3f;
-       if (pc == 3) {
-               curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED;
-               return -EINVAL;
-       }
-       changeable_values = (pc == 1);
-       all_pages = (page_code == 0x3f);
-
-       /*
-        * Write the mode parameter header.  Fixed values are: default
-        * medium type, no cache control (DPOFUA), and no block descriptors.
-        * The only variable value is the WriteProtect bit.  We will fill in
-        * the mode data length later.
-        */
-       memset(buf, 0, 8);
-       if (mscmnd == MODE_SENSE) {
-               buf[2] = (curlun->ro ? 0x80 : 0x00);            /* WP, DPOFUA */
-               buf += 4;
-               limit = 255;
-       } else {                        /* MODE_SENSE_10 */
-               buf[3] = (curlun->ro ? 0x80 : 0x00);            /* WP, DPOFUA */
-               buf += 8;
-               limit = 65535;          /* Should really be FSG_BUFLEN */
-       }
-
-       /* No block descriptors */
-
-       /*
-        * The mode pages, in numerical order.  The only page we support
-        * is the Caching page.
-        */
-       if (page_code == 0x08 || all_pages) {
-               valid_page = 1;
-               buf[0] = 0x08;          /* Page code */
-               buf[1] = 10;            /* Page length */
-               memset(buf+2, 0, 10);   /* None of the fields are changeable */
-
-               if (!changeable_values) {
-                       buf[2] = 0x04;  /* Write cache enable, */
-                                       /* Read cache not disabled */
-                                       /* No cache retention priorities */
-                       put_unaligned_be16(0xffff, &buf[4]);
-                                       /* Don't disable prefetch */
-                                       /* Minimum prefetch = 0 */
-                       put_unaligned_be16(0xffff, &buf[8]);
-                                       /* Maximum prefetch */
-                       put_unaligned_be16(0xffff, &buf[10]);
-                                       /* Maximum prefetch ceiling */
-               }
-               buf += 12;
-       }
-
-       /*
-        * Check that a valid page was requested and the mode data length
-        * isn't too long.
-        */
-       len = buf - buf0;
-       if (!valid_page || len > limit) {
-               curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
-               return -EINVAL;
-       }
-
-       /*  Store the mode data length */
-       if (mscmnd == MODE_SENSE)
-               buf0[0] = len - 1;
-       else
-               put_unaligned_be16(len - 2, buf0);
-       return len;
-}
-
-static int do_start_stop(struct fsg_common *common)
-{
-       struct fsg_lun  *curlun = common->curlun;
-       int             loej, start;
-
-       if (!curlun) {
-               return -EINVAL;
-       } else if (!curlun->removable) {
-               curlun->sense_data = SS_INVALID_COMMAND;
-               return -EINVAL;
-       } else if ((common->cmnd[1] & ~0x01) != 0 || /* Mask away Immed */
-                  (common->cmnd[4] & ~0x03) != 0) { /* Mask LoEj, Start */
-               curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
-               return -EINVAL;
-       }
-
-       loej  = common->cmnd[4] & 0x02;
-       start = common->cmnd[4] & 0x01;
-
-       /*
-        * Our emulation doesn't support mounting; the medium is
-        * available for use as soon as it is loaded.
-        */
-       if (start) {
-               if (!fsg_lun_is_open(curlun)) {
-                       curlun->sense_data = SS_MEDIUM_NOT_PRESENT;
-                       return -EINVAL;
-               }
-               return 0;
-       }
-
-       /* Are we allowed to unload the media? */
-       if (curlun->prevent_medium_removal) {
-               LDBG(curlun, "unload attempt prevented\n");
-               curlun->sense_data = SS_MEDIUM_REMOVAL_PREVENTED;
-               return -EINVAL;
-       }
-
-       if (!loej)
-               return 0;
-
-       up_read(&common->filesem);
-       down_write(&common->filesem);
-       fsg_lun_close(curlun);
-       up_write(&common->filesem);
-       down_read(&common->filesem);
-
-       return 0;
-}
-
-static int do_prevent_allow(struct fsg_common *common)
-{
-       struct fsg_lun  *curlun = common->curlun;
-       int             prevent;
-
-       if (!common->curlun) {
-               return -EINVAL;
-       } else if (!common->curlun->removable) {
-               common->curlun->sense_data = SS_INVALID_COMMAND;
-               return -EINVAL;
-       }
-
-       prevent = common->cmnd[4] & 0x01;
-       if ((common->cmnd[4] & ~0x01) != 0) {   /* Mask away Prevent */
-               curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
-               return -EINVAL;
-       }
-
-       if (curlun->prevent_medium_removal && !prevent)
-               fsg_lun_fsync_sub(curlun);
-       curlun->prevent_medium_removal = prevent;
-       return 0;
-}
-
-static int do_read_format_capacities(struct fsg_common *common,
-                       struct fsg_buffhd *bh)
-{
-       struct fsg_lun  *curlun = common->curlun;
-       u8              *buf = (u8 *) bh->buf;
-
-       buf[0] = buf[1] = buf[2] = 0;
-       buf[3] = 8;     /* Only the Current/Maximum Capacity Descriptor */
-       buf += 4;
-
-       put_unaligned_be32(curlun->num_sectors, &buf[0]);
-                                               /* Number of blocks */
-       put_unaligned_be32(curlun->blksize, &buf[4]);/* Block length */
-       buf[4] = 0x02;                          /* Current capacity */
-       return 12;
-}
-
-static int do_mode_select(struct fsg_common *common, struct fsg_buffhd *bh)
-{
-       struct fsg_lun  *curlun = common->curlun;
-
-       /* We don't support MODE SELECT */
-       if (curlun)
-               curlun->sense_data = SS_INVALID_COMMAND;
-       return -EINVAL;
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-static int halt_bulk_in_endpoint(struct fsg_dev *fsg)
-{
-       int     rc;
-
-       rc = fsg_set_halt(fsg, fsg->bulk_in);
-       if (rc == -EAGAIN)
-               VDBG(fsg, "delayed bulk-in endpoint halt\n");
-       while (rc != 0) {
-               if (rc != -EAGAIN) {
-                       WARNING(fsg, "usb_ep_set_halt -> %d\n", rc);
-                       rc = 0;
-                       break;
-               }
-
-               /* Wait for a short time and then try again */
-               if (msleep_interruptible(100) != 0)
-                       return -EINTR;
-               rc = usb_ep_set_halt(fsg->bulk_in);
-       }
-       return rc;
-}
-
-static int wedge_bulk_in_endpoint(struct fsg_dev *fsg)
-{
-       int     rc;
-
-       DBG(fsg, "bulk-in set wedge\n");
-       rc = usb_ep_set_wedge(fsg->bulk_in);
-       if (rc == -EAGAIN)
-               VDBG(fsg, "delayed bulk-in endpoint wedge\n");
-       while (rc != 0) {
-               if (rc != -EAGAIN) {
-                       WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc);
-                       rc = 0;
-                       break;
-               }
-
-               /* Wait for a short time and then try again */
-               if (msleep_interruptible(100) != 0)
-                       return -EINTR;
-               rc = usb_ep_set_wedge(fsg->bulk_in);
-       }
-       return rc;
-}
-
-static int throw_away_data(struct fsg_common *common)
-{
-       struct fsg_buffhd       *bh;
-       u32                     amount;
-       int                     rc;
-
-       for (bh = common->next_buffhd_to_drain;
-            bh->state != BUF_STATE_EMPTY || common->usb_amount_left > 0;
-            bh = common->next_buffhd_to_drain) {
-
-               /* Throw away the data in a filled buffer */
-               if (bh->state == BUF_STATE_FULL) {
-                       smp_rmb();
-                       bh->state = BUF_STATE_EMPTY;
-                       common->next_buffhd_to_drain = bh->next;
-
-                       /* A short packet or an error ends everything */
-                       if (bh->outreq->actual < bh->bulk_out_intended_length ||
-                           bh->outreq->status != 0) {
-                               raise_exception(common,
-                                               FSG_STATE_ABORT_BULK_OUT);
-                               return -EINTR;
-                       }
-                       continue;
-               }
-
-               /* Try to submit another request if we need one */
-               bh = common->next_buffhd_to_fill;
-               if (bh->state == BUF_STATE_EMPTY
-                && common->usb_amount_left > 0) {
-                       amount = min(common->usb_amount_left, FSG_BUFLEN);
-
-                       /*
-                        * Except at the end of the transfer, amount will be
-                        * equal to the buffer size, which is divisible by
-                        * the bulk-out maxpacket size.
-                        */
-                       set_bulk_out_req_length(common, bh, amount);
-                       if (!start_out_transfer(common, bh))
-                               /* Dunno what to do if common->fsg is NULL */
-                               return -EIO;
-                       common->next_buffhd_to_fill = bh->next;
-                       common->usb_amount_left -= amount;
-                       continue;
-               }
-
-               /* Otherwise wait for something to happen */
-               rc = sleep_thread(common, true);
-               if (rc)
-                       return rc;
-       }
-       return 0;
-}
-
-static int finish_reply(struct fsg_common *common)
-{
-       struct fsg_buffhd       *bh = common->next_buffhd_to_fill;
-       int                     rc = 0;
-
-       switch (common->data_dir) {
-       case DATA_DIR_NONE:
-               break;                  /* Nothing to send */
-
-       /*
-        * If we don't know whether the host wants to read or write,
-        * this must be CB or CBI with an unknown command.  We mustn't
-        * try to send or receive any data.  So stall both bulk pipes
-        * if we can and wait for a reset.
-        */
-       case DATA_DIR_UNKNOWN:
-               if (!common->can_stall) {
-                       /* Nothing */
-               } else if (fsg_is_set(common)) {
-                       fsg_set_halt(common->fsg, common->fsg->bulk_out);
-                       rc = halt_bulk_in_endpoint(common->fsg);
-               } else {
-                       /* Don't know what to do if common->fsg is NULL */
-                       rc = -EIO;
-               }
-               break;
-
-       /* All but the last buffer of data must have already been sent */
-       case DATA_DIR_TO_HOST:
-               if (common->data_size == 0) {
-                       /* Nothing to send */
-
-               /* Don't know what to do if common->fsg is NULL */
-               } else if (!fsg_is_set(common)) {
-                       rc = -EIO;
-
-               /* If there's no residue, simply send the last buffer */
-               } else if (common->residue == 0) {
-                       bh->inreq->zero = 0;
-                       if (!start_in_transfer(common, bh))
-                               return -EIO;
-                       common->next_buffhd_to_fill = bh->next;
-
-               /*
-                * For Bulk-only, mark the end of the data with a short
-                * packet.  If we are allowed to stall, halt the bulk-in
-                * endpoint.  (Note: This violates the Bulk-Only Transport
-                * specification, which requires us to pad the data if we
-                * don't halt the endpoint.  Presumably nobody will mind.)
-                */
-               } else {
-                       bh->inreq->zero = 1;
-                       if (!start_in_transfer(common, bh))
-                               rc = -EIO;
-                       common->next_buffhd_to_fill = bh->next;
-                       if (common->can_stall)
-                               rc = halt_bulk_in_endpoint(common->fsg);
-               }
-               break;
-
-       /*
-        * We have processed all we want from the data the host has sent.
-        * There may still be outstanding bulk-out requests.
-        */
-       case DATA_DIR_FROM_HOST:
-               if (common->residue == 0) {
-                       /* Nothing to receive */
-
-               /* Did the host stop sending unexpectedly early? */
-               } else if (common->short_packet_received) {
-                       raise_exception(common, FSG_STATE_ABORT_BULK_OUT);
-                       rc = -EINTR;
-
-               /*
-                * We haven't processed all the incoming data.  Even though
-                * we may be allowed to stall, doing so would cause a race.
-                * The controller may already have ACK'ed all the remaining
-                * bulk-out packets, in which case the host wouldn't see a
-                * STALL.  Not realizing the endpoint was halted, it wouldn't
-                * clear the halt -- leading to problems later on.
-                */
-#if 0
-               } else if (common->can_stall) {
-                       if (fsg_is_set(common))
-                               fsg_set_halt(common->fsg,
-                                            common->fsg->bulk_out);
-                       raise_exception(common, FSG_STATE_ABORT_BULK_OUT);
-                       rc = -EINTR;
-#endif
-
-               /*
-                * We can't stall.  Read in the excess data and throw it
-                * all away.
-                */
-               } else {
-                       rc = throw_away_data(common);
-               }
-               break;
-       }
-       return rc;
-}
-
-static int send_status(struct fsg_common *common)
-{
-       struct fsg_lun          *curlun = common->curlun;
-       struct fsg_buffhd       *bh;
-       struct bulk_cs_wrap     *csw;
-       int                     rc;
-       u8                      status = US_BULK_STAT_OK;
-       u32                     sd, sdinfo = 0;
-
-       /* Wait for the next buffer to become available */
-       bh = common->next_buffhd_to_fill;
-       while (bh->state != BUF_STATE_EMPTY) {
-               rc = sleep_thread(common, true);
-               if (rc)
-                       return rc;
-       }
-
-       if (curlun) {
-               sd = curlun->sense_data;
-               sdinfo = curlun->sense_data_info;
-       } else if (common->bad_lun_okay)
-               sd = SS_NO_SENSE;
-       else
-               sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
-
-       if (common->phase_error) {
-               DBG(common, "sending phase-error status\n");
-               status = US_BULK_STAT_PHASE;
-               sd = SS_INVALID_COMMAND;
-       } else if (sd != SS_NO_SENSE) {
-               DBG(common, "sending command-failure status\n");
-               status = US_BULK_STAT_FAIL;
-               VDBG(common, "  sense data: SK x%02x, ASC x%02x, ASCQ x%02x;"
-                               "  info x%x\n",
-                               SK(sd), ASC(sd), ASCQ(sd), sdinfo);
-       }
-
-       /* Store and send the Bulk-only CSW */
-       csw = (void *)bh->buf;
-
-       csw->Signature = cpu_to_le32(US_BULK_CS_SIGN);
-       csw->Tag = common->tag;
-       csw->Residue = cpu_to_le32(common->residue);
-       csw->Status = status;
-
-       bh->inreq->length = US_BULK_CS_WRAP_LEN;
-       bh->inreq->zero = 0;
-       if (!start_in_transfer(common, bh))
-               /* Don't know what to do if common->fsg is NULL */
-               return -EIO;
-
-       common->next_buffhd_to_fill = bh->next;
-       return 0;
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * Check whether the command is properly formed and whether its data size
- * and direction agree with the values we already have.
- */
-static int check_command(struct fsg_common *common, int cmnd_size,
-                        enum data_direction data_dir, unsigned int mask,
-                        int needs_medium, const char *name)
-{
-       int                     i;
-       unsigned int            lun = common->cmnd[1] >> 5;
-       static const char       dirletter[4] = {'u', 'o', 'i', 'n'};
-       char                    hdlen[20];
-       struct fsg_lun          *curlun;
-
-       hdlen[0] = 0;
-       if (common->data_dir != DATA_DIR_UNKNOWN)
-               sprintf(hdlen, ", H%c=%u", dirletter[(int) common->data_dir],
-                       common->data_size);
-       VDBG(common, "SCSI command: %s;  Dc=%d, D%c=%u;  Hc=%d%s\n",
-            name, cmnd_size, dirletter[(int) data_dir],
-            common->data_size_from_cmnd, common->cmnd_size, hdlen);
-
-       /*
-        * We can't reply at all until we know the correct data direction
-        * and size.
-        */
-       if (common->data_size_from_cmnd == 0)
-               data_dir = DATA_DIR_NONE;
-       if (common->data_size < common->data_size_from_cmnd) {
-               /*
-                * Host data size < Device data size is a phase error.
-                * Carry out the command, but only transfer as much as
-                * we are allowed.
-                */
-               common->data_size_from_cmnd = common->data_size;
-               common->phase_error = 1;
-       }
-       common->residue = common->data_size;
-       common->usb_amount_left = common->data_size;
-
-       /* Conflicting data directions is a phase error */
-       if (common->data_dir != data_dir && common->data_size_from_cmnd > 0) {
-               common->phase_error = 1;
-               return -EINVAL;
-       }
-
-       /* Verify the length of the command itself */
-       if (cmnd_size != common->cmnd_size) {
-
-               /*
-                * Special case workaround: There are plenty of buggy SCSI
-                * implementations. Many have issues with cbw->Length
-                * field passing a wrong command size. For those cases we
-                * always try to work around the problem by using the length
-                * sent by the host side provided it is at least as large
-                * as the correct command length.
-                * Examples of such cases would be MS-Windows, which issues
-                * REQUEST SENSE with cbw->Length == 12 where it should
-                * be 6, and xbox360 issuing INQUIRY, TEST UNIT READY and
-                * REQUEST SENSE with cbw->Length == 10 where it should
-                * be 6 as well.
-                */
-               if (cmnd_size <= common->cmnd_size) {
-                       DBG(common, "%s is buggy! Expected length %d "
-                           "but we got %d\n", name,
-                           cmnd_size, common->cmnd_size);
-                       cmnd_size = common->cmnd_size;
-               } else {
-                       common->phase_error = 1;
-                       return -EINVAL;
-               }
-       }
-
-       /* Check that the LUN values are consistent */
-       if (common->lun != lun)
-               DBG(common, "using LUN %u from CBW, not LUN %u from CDB\n",
-                   common->lun, lun);
-
-       /* Check the LUN */
-       curlun = common->curlun;
-       if (curlun) {
-               if (common->cmnd[0] != REQUEST_SENSE) {
-                       curlun->sense_data = SS_NO_SENSE;
-                       curlun->sense_data_info = 0;
-                       curlun->info_valid = 0;
-               }
-       } else {
-               common->bad_lun_okay = 0;
-
-               /*
-                * INQUIRY and REQUEST SENSE commands are explicitly allowed
-                * to use unsupported LUNs; all others may not.
-                */
-               if (common->cmnd[0] != INQUIRY &&
-                   common->cmnd[0] != REQUEST_SENSE) {
-                       DBG(common, "unsupported LUN %u\n", common->lun);
-                       return -EINVAL;
-               }
-       }
-
-       /*
-        * If a unit attention condition exists, only INQUIRY and
-        * REQUEST SENSE commands are allowed; anything else must fail.
-        */
-       if (curlun && curlun->unit_attention_data != SS_NO_SENSE &&
-           common->cmnd[0] != INQUIRY &&
-           common->cmnd[0] != REQUEST_SENSE) {
-               curlun->sense_data = curlun->unit_attention_data;
-               curlun->unit_attention_data = SS_NO_SENSE;
-               return -EINVAL;
-       }
-
-       /* Check that only command bytes listed in the mask are non-zero */
-       common->cmnd[1] &= 0x1f;                        /* Mask away the LUN */
-       for (i = 1; i < cmnd_size; ++i) {
-               if (common->cmnd[i] && !(mask & (1 << i))) {
-                       if (curlun)
-                               curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
-                       return -EINVAL;
-               }
-       }
-
-       /* If the medium isn't mounted and the command needs to access
-        * it, return an error. */
-       if (curlun && !fsg_lun_is_open(curlun) && needs_medium) {
-               curlun->sense_data = SS_MEDIUM_NOT_PRESENT;
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-/* wrapper of check_command for data size in blocks handling */
-static int check_command_size_in_blocks(struct fsg_common *common,
-               int cmnd_size, enum data_direction data_dir,
-               unsigned int mask, int needs_medium, const char *name)
-{
-       if (common->curlun)
-               common->data_size_from_cmnd <<= common->curlun->blkbits;
-       return check_command(common, cmnd_size, data_dir,
-                       mask, needs_medium, name);
-}
-
-static int do_scsi_command(struct fsg_common *common)
-{
-       struct fsg_buffhd       *bh;
-       int                     rc;
-       int                     reply = -EINVAL;
-       int                     i;
-       static char             unknown[16];
-
-       dump_cdb(common);
-
-       /* Wait for the next buffer to become available for data or status */
-       bh = common->next_buffhd_to_fill;
-       common->next_buffhd_to_drain = bh;
-       while (bh->state != BUF_STATE_EMPTY) {
-               rc = sleep_thread(common, true);
-               if (rc)
-                       return rc;
-       }
-       common->phase_error = 0;
-       common->short_packet_received = 0;
-
-       down_read(&common->filesem);    /* We're using the backing file */
-       switch (common->cmnd[0]) {
-
-       case INQUIRY:
-               common->data_size_from_cmnd = common->cmnd[4];
-               reply = check_command(common, 6, DATA_DIR_TO_HOST,
-                                     (1<<4), 0,
-                                     "INQUIRY");
-               if (reply == 0)
-                       reply = do_inquiry(common, bh);
-               break;
-
-       case MODE_SELECT:
-               common->data_size_from_cmnd = common->cmnd[4];
-               reply = check_command(common, 6, DATA_DIR_FROM_HOST,
-                                     (1<<1) | (1<<4), 0,
-                                     "MODE SELECT(6)");
-               if (reply == 0)
-                       reply = do_mode_select(common, bh);
-               break;
-
-       case MODE_SELECT_10:
-               common->data_size_from_cmnd =
-                       get_unaligned_be16(&common->cmnd[7]);
-               reply = check_command(common, 10, DATA_DIR_FROM_HOST,
-                                     (1<<1) | (3<<7), 0,
-                                     "MODE SELECT(10)");
-               if (reply == 0)
-                       reply = do_mode_select(common, bh);
-               break;
-
-       case MODE_SENSE:
-               common->data_size_from_cmnd = common->cmnd[4];
-               reply = check_command(common, 6, DATA_DIR_TO_HOST,
-                                     (1<<1) | (1<<2) | (1<<4), 0,
-                                     "MODE SENSE(6)");
-               if (reply == 0)
-                       reply = do_mode_sense(common, bh);
-               break;
-
-       case MODE_SENSE_10:
-               common->data_size_from_cmnd =
-                       get_unaligned_be16(&common->cmnd[7]);
-               reply = check_command(common, 10, DATA_DIR_TO_HOST,
-                                     (1<<1) | (1<<2) | (3<<7), 0,
-                                     "MODE SENSE(10)");
-               if (reply == 0)
-                       reply = do_mode_sense(common, bh);
-               break;
-
-       case ALLOW_MEDIUM_REMOVAL:
-               common->data_size_from_cmnd = 0;
-               reply = check_command(common, 6, DATA_DIR_NONE,
-                                     (1<<4), 0,
-                                     "PREVENT-ALLOW MEDIUM REMOVAL");
-               if (reply == 0)
-                       reply = do_prevent_allow(common);
-               break;
-
-       case READ_6:
-               i = common->cmnd[4];
-               common->data_size_from_cmnd = (i == 0) ? 256 : i;
-               reply = check_command_size_in_blocks(common, 6,
-                                     DATA_DIR_TO_HOST,
-                                     (7<<1) | (1<<4), 1,
-                                     "READ(6)");
-               if (reply == 0)
-                       reply = do_read(common);
-               break;
-
-       case READ_10:
-               common->data_size_from_cmnd =
-                               get_unaligned_be16(&common->cmnd[7]);
-               reply = check_command_size_in_blocks(common, 10,
-                                     DATA_DIR_TO_HOST,
-                                     (1<<1) | (0xf<<2) | (3<<7), 1,
-                                     "READ(10)");
-               if (reply == 0)
-                       reply = do_read(common);
-               break;
-
-       case READ_12:
-               common->data_size_from_cmnd =
-                               get_unaligned_be32(&common->cmnd[6]);
-               reply = check_command_size_in_blocks(common, 12,
-                                     DATA_DIR_TO_HOST,
-                                     (1<<1) | (0xf<<2) | (0xf<<6), 1,
-                                     "READ(12)");
-               if (reply == 0)
-                       reply = do_read(common);
-               break;
-
-       case READ_CAPACITY:
-               common->data_size_from_cmnd = 8;
-               reply = check_command(common, 10, DATA_DIR_TO_HOST,
-                                     (0xf<<2) | (1<<8), 1,
-                                     "READ CAPACITY");
-               if (reply == 0)
-                       reply = do_read_capacity(common, bh);
-               break;
-
-       case READ_HEADER:
-               if (!common->curlun || !common->curlun->cdrom)
-                       goto unknown_cmnd;
-               common->data_size_from_cmnd =
-                       get_unaligned_be16(&common->cmnd[7]);
-               reply = check_command(common, 10, DATA_DIR_TO_HOST,
-                                     (3<<7) | (0x1f<<1), 1,
-                                     "READ HEADER");
-               if (reply == 0)
-                       reply = do_read_header(common, bh);
-               break;
-
-       case READ_TOC:
-               if (!common->curlun || !common->curlun->cdrom)
-                       goto unknown_cmnd;
-               common->data_size_from_cmnd =
-                       get_unaligned_be16(&common->cmnd[7]);
-               reply = check_command(common, 10, DATA_DIR_TO_HOST,
-                                     (7<<6) | (1<<1), 1,
-                                     "READ TOC");
-               if (reply == 0)
-                       reply = do_read_toc(common, bh);
-               break;
-
-       case READ_FORMAT_CAPACITIES:
-               common->data_size_from_cmnd =
-                       get_unaligned_be16(&common->cmnd[7]);
-               reply = check_command(common, 10, DATA_DIR_TO_HOST,
-                                     (3<<7), 1,
-                                     "READ FORMAT CAPACITIES");
-               if (reply == 0)
-                       reply = do_read_format_capacities(common, bh);
-               break;
-
-       case REQUEST_SENSE:
-               common->data_size_from_cmnd = common->cmnd[4];
-               reply = check_command(common, 6, DATA_DIR_TO_HOST,
-                                     (1<<4), 0,
-                                     "REQUEST SENSE");
-               if (reply == 0)
-                       reply = do_request_sense(common, bh);
-               break;
-
-       case START_STOP:
-               common->data_size_from_cmnd = 0;
-               reply = check_command(common, 6, DATA_DIR_NONE,
-                                     (1<<1) | (1<<4), 0,
-                                     "START-STOP UNIT");
-               if (reply == 0)
-                       reply = do_start_stop(common);
-               break;
-
-       case SYNCHRONIZE_CACHE:
-               common->data_size_from_cmnd = 0;
-               reply = check_command(common, 10, DATA_DIR_NONE,
-                                     (0xf<<2) | (3<<7), 1,
-                                     "SYNCHRONIZE CACHE");
-               if (reply == 0)
-                       reply = do_synchronize_cache(common);
-               break;
-
-       case TEST_UNIT_READY:
-               common->data_size_from_cmnd = 0;
-               reply = check_command(common, 6, DATA_DIR_NONE,
-                               0, 1,
-                               "TEST UNIT READY");
-               break;
-
-       /*
-        * Although optional, this command is used by MS-Windows.  We
-        * support a minimal version: BytChk must be 0.
-        */
-       case VERIFY:
-               common->data_size_from_cmnd = 0;
-               reply = check_command(common, 10, DATA_DIR_NONE,
-                                     (1<<1) | (0xf<<2) | (3<<7), 1,
-                                     "VERIFY");
-               if (reply == 0)
-                       reply = do_verify(common);
-               break;
-
-       case WRITE_6:
-               i = common->cmnd[4];
-               common->data_size_from_cmnd = (i == 0) ? 256 : i;
-               reply = check_command_size_in_blocks(common, 6,
-                                     DATA_DIR_FROM_HOST,
-                                     (7<<1) | (1<<4), 1,
-                                     "WRITE(6)");
-               if (reply == 0)
-                       reply = do_write(common);
-               break;
-
-       case WRITE_10:
-               common->data_size_from_cmnd =
-                               get_unaligned_be16(&common->cmnd[7]);
-               reply = check_command_size_in_blocks(common, 10,
-                                     DATA_DIR_FROM_HOST,
-                                     (1<<1) | (0xf<<2) | (3<<7), 1,
-                                     "WRITE(10)");
-               if (reply == 0)
-                       reply = do_write(common);
-               break;
-
-       case WRITE_12:
-               common->data_size_from_cmnd =
-                               get_unaligned_be32(&common->cmnd[6]);
-               reply = check_command_size_in_blocks(common, 12,
-                                     DATA_DIR_FROM_HOST,
-                                     (1<<1) | (0xf<<2) | (0xf<<6), 1,
-                                     "WRITE(12)");
-               if (reply == 0)
-                       reply = do_write(common);
-               break;
-
-       /*
-        * Some mandatory commands that we recognize but don't implement.
-        * They don't mean much in this setting.  It's left as an exercise
-        * for anyone interested to implement RESERVE and RELEASE in terms
-        * of Posix locks.
-        */
-       case FORMAT_UNIT:
-       case RELEASE:
-       case RESERVE:
-       case SEND_DIAGNOSTIC:
-               /* Fall through */
-
-       default:
-unknown_cmnd:
-               common->data_size_from_cmnd = 0;
-               sprintf(unknown, "Unknown x%02x", common->cmnd[0]);
-               reply = check_command(common, common->cmnd_size,
-                                     DATA_DIR_UNKNOWN, ~0, 0, unknown);
-               if (reply == 0) {
-                       common->curlun->sense_data = SS_INVALID_COMMAND;
-                       reply = -EINVAL;
-               }
-               break;
-       }
-       up_read(&common->filesem);
-
-       if (reply == -EINTR || signal_pending(current))
-               return -EINTR;
-
-       /* Set up the single reply buffer for finish_reply() */
-       if (reply == -EINVAL)
-               reply = 0;              /* Error reply length */
-       if (reply >= 0 && common->data_dir == DATA_DIR_TO_HOST) {
-               reply = min((u32)reply, common->data_size_from_cmnd);
-               bh->inreq->length = reply;
-               bh->state = BUF_STATE_FULL;
-               common->residue -= reply;
-       }                               /* Otherwise it's already set */
-
-       return 0;
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
-{
-       struct usb_request      *req = bh->outreq;
-       struct bulk_cb_wrap     *cbw = req->buf;
-       struct fsg_common       *common = fsg->common;
-
-       /* Was this a real packet?  Should it be ignored? */
-       if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags))
-               return -EINVAL;
-
-       /* Is the CBW valid? */
-       if (req->actual != US_BULK_CB_WRAP_LEN ||
-                       cbw->Signature != cpu_to_le32(
-                               US_BULK_CB_SIGN)) {
-               DBG(fsg, "invalid CBW: len %u sig 0x%x\n",
-                               req->actual,
-                               le32_to_cpu(cbw->Signature));
-
-               /*
-                * The Bulk-only spec says we MUST stall the IN endpoint
-                * (6.6.1), so it's unavoidable.  It also says we must
-                * retain this state until the next reset, but there's
-                * no way to tell the controller driver it should ignore
-                * Clear-Feature(HALT) requests.
-                *
-                * We aren't required to halt the OUT endpoint; instead
-                * we can simply accept and discard any data received
-                * until the next reset.
-                */
-               wedge_bulk_in_endpoint(fsg);
-               set_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
-               return -EINVAL;
-       }
-
-       /* Is the CBW meaningful? */
-       if (cbw->Lun >= FSG_MAX_LUNS || cbw->Flags & ~US_BULK_FLAG_IN ||
-                       cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) {
-               DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, "
-                               "cmdlen %u\n",
-                               cbw->Lun, cbw->Flags, cbw->Length);
-
-               /*
-                * We can do anything we want here, so let's stall the
-                * bulk pipes if we are allowed to.
-                */
-               if (common->can_stall) {
-                       fsg_set_halt(fsg, fsg->bulk_out);
-                       halt_bulk_in_endpoint(fsg);
-               }
-               return -EINVAL;
-       }
-
-       /* Save the command for later */
-       common->cmnd_size = cbw->Length;
-       memcpy(common->cmnd, cbw->CDB, common->cmnd_size);
-       if (cbw->Flags & US_BULK_FLAG_IN)
-               common->data_dir = DATA_DIR_TO_HOST;
-       else
-               common->data_dir = DATA_DIR_FROM_HOST;
-       common->data_size = le32_to_cpu(cbw->DataTransferLength);
-       if (common->data_size == 0)
-               common->data_dir = DATA_DIR_NONE;
-       common->lun = cbw->Lun;
-       if (common->lun < common->nluns)
-               common->curlun = common->luns[common->lun];
-       else
-               common->curlun = NULL;
-       common->tag = cbw->Tag;
-       return 0;
-}
-
-static int get_next_command(struct fsg_common *common)
-{
-       struct fsg_buffhd       *bh;
-       int                     rc = 0;
-
-       /* Wait for the next buffer to become available */
-       bh = common->next_buffhd_to_fill;
-       while (bh->state != BUF_STATE_EMPTY) {
-               rc = sleep_thread(common, true);
-               if (rc)
-                       return rc;
-       }
-
-       /* Queue a request to read a Bulk-only CBW */
-       set_bulk_out_req_length(common, bh, US_BULK_CB_WRAP_LEN);
-       if (!start_out_transfer(common, bh))
-               /* Don't know what to do if common->fsg is NULL */
-               return -EIO;
-
-       /*
-        * We will drain the buffer in software, which means we
-        * can reuse it for the next filling.  No need to advance
-        * next_buffhd_to_fill.
-        */
-
-       /* Wait for the CBW to arrive */
-       while (bh->state != BUF_STATE_FULL) {
-               rc = sleep_thread(common, true);
-               if (rc)
-                       return rc;
-       }
-       smp_rmb();
-       rc = fsg_is_set(common) ? received_cbw(common->fsg, bh) : -EIO;
-       bh->state = BUF_STATE_EMPTY;
-
-       return rc;
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-static int alloc_request(struct fsg_common *common, struct usb_ep *ep,
-               struct usb_request **preq)
-{
-       *preq = usb_ep_alloc_request(ep, GFP_ATOMIC);
-       if (*preq)
-               return 0;
-       ERROR(common, "can't allocate request for %s\n", ep->name);
-       return -ENOMEM;
-}
-
-/* Reset interface setting and re-init endpoint state (toggle etc). */
-static int do_set_interface(struct fsg_common *common, struct fsg_dev *new_fsg)
-{
-       struct fsg_dev *fsg;
-       int i, rc = 0;
-
-       if (common->running)
-               DBG(common, "reset interface\n");
-
-reset:
-       /* Deallocate the requests */
-       if (common->fsg) {
-               fsg = common->fsg;
-
-               for (i = 0; i < common->fsg_num_buffers; ++i) {
-                       struct fsg_buffhd *bh = &common->buffhds[i];
-
-                       if (bh->inreq) {
-                               usb_ep_free_request(fsg->bulk_in, bh->inreq);
-                               bh->inreq = NULL;
-                       }
-                       if (bh->outreq) {
-                               usb_ep_free_request(fsg->bulk_out, bh->outreq);
-                               bh->outreq = NULL;
-                       }
-               }
-
-               /* Disable the endpoints */
-               if (fsg->bulk_in_enabled) {
-                       usb_ep_disable(fsg->bulk_in);
-                       fsg->bulk_in->driver_data = NULL;
-                       fsg->bulk_in_enabled = 0;
-               }
-               if (fsg->bulk_out_enabled) {
-                       usb_ep_disable(fsg->bulk_out);
-                       fsg->bulk_out->driver_data = NULL;
-                       fsg->bulk_out_enabled = 0;
-               }
-
-               common->fsg = NULL;
-               wake_up(&common->fsg_wait);
-       }
-
-       common->running = 0;
-       if (!new_fsg || rc)
-               return rc;
-
-       common->fsg = new_fsg;
-       fsg = common->fsg;
-
-       /* Enable the endpoints */
-       rc = config_ep_by_speed(common->gadget, &(fsg->function), fsg->bulk_in);
-       if (rc)
-               goto reset;
-       rc = usb_ep_enable(fsg->bulk_in);
-       if (rc)
-               goto reset;
-       fsg->bulk_in->driver_data = common;
-       fsg->bulk_in_enabled = 1;
-
-       rc = config_ep_by_speed(common->gadget, &(fsg->function),
-                               fsg->bulk_out);
-       if (rc)
-               goto reset;
-       rc = usb_ep_enable(fsg->bulk_out);
-       if (rc)
-               goto reset;
-       fsg->bulk_out->driver_data = common;
-       fsg->bulk_out_enabled = 1;
-       common->bulk_out_maxpacket = usb_endpoint_maxp(fsg->bulk_out->desc);
-       clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
-
-       /* Allocate the requests */
-       for (i = 0; i < common->fsg_num_buffers; ++i) {
-               struct fsg_buffhd       *bh = &common->buffhds[i];
-
-               rc = alloc_request(common, fsg->bulk_in, &bh->inreq);
-               if (rc)
-                       goto reset;
-               rc = alloc_request(common, fsg->bulk_out, &bh->outreq);
-               if (rc)
-                       goto reset;
-               bh->inreq->buf = bh->outreq->buf = bh->buf;
-               bh->inreq->context = bh->outreq->context = bh;
-               bh->inreq->complete = bulk_in_complete;
-               bh->outreq->complete = bulk_out_complete;
-       }
-
-       common->running = 1;
-       for (i = 0; i < common->nluns; ++i)
-               if (common->luns[i])
-                       common->luns[i]->unit_attention_data =
-                               SS_RESET_OCCURRED;
-       return rc;
-}
-
-
-/****************************** ALT CONFIGS ******************************/
-
-static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
-{
-       struct fsg_dev *fsg = fsg_from_func(f);
-       fsg->common->new_fsg = fsg;
-       raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
-       return USB_GADGET_DELAYED_STATUS;
-}
-
-static void fsg_disable(struct usb_function *f)
-{
-       struct fsg_dev *fsg = fsg_from_func(f);
-       fsg->common->new_fsg = NULL;
-       raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-static void handle_exception(struct fsg_common *common)
-{
-       siginfo_t               info;
-       int                     i;
-       struct fsg_buffhd       *bh;
-       enum fsg_state          old_state;
-       struct fsg_lun          *curlun;
-       unsigned int            exception_req_tag;
-
-       /*
-        * Clear the existing signals.  Anything but SIGUSR1 is converted
-        * into a high-priority EXIT exception.
-        */
-       for (;;) {
-               int sig =
-                       dequeue_signal_lock(current, &current->blocked, &info);
-               if (!sig)
-                       break;
-               if (sig != SIGUSR1) {
-                       if (common->state < FSG_STATE_EXIT)
-                               DBG(common, "Main thread exiting on signal\n");
-                       raise_exception(common, FSG_STATE_EXIT);
-               }
-       }
-
-       /* Cancel all the pending transfers */
-       if (likely(common->fsg)) {
-               for (i = 0; i < common->fsg_num_buffers; ++i) {
-                       bh = &common->buffhds[i];
-                       if (bh->inreq_busy)
-                               usb_ep_dequeue(common->fsg->bulk_in, bh->inreq);
-                       if (bh->outreq_busy)
-                               usb_ep_dequeue(common->fsg->bulk_out,
-                                              bh->outreq);
-               }
-
-               /* Wait until everything is idle */
-               for (;;) {
-                       int num_active = 0;
-                       for (i = 0; i < common->fsg_num_buffers; ++i) {
-                               bh = &common->buffhds[i];
-                               num_active += bh->inreq_busy + bh->outreq_busy;
-                       }
-                       if (num_active == 0)
-                               break;
-                       if (sleep_thread(common, true))
-                               return;
-               }
-
-               /* Clear out the controller's fifos */
-               if (common->fsg->bulk_in_enabled)
-                       usb_ep_fifo_flush(common->fsg->bulk_in);
-               if (common->fsg->bulk_out_enabled)
-                       usb_ep_fifo_flush(common->fsg->bulk_out);
-       }
-
-       /*
-        * Reset the I/O buffer states and pointers, the SCSI
-        * state, and the exception.  Then invoke the handler.
-        */
-       spin_lock_irq(&common->lock);
-
-       for (i = 0; i < common->fsg_num_buffers; ++i) {
-               bh = &common->buffhds[i];
-               bh->state = BUF_STATE_EMPTY;
-       }
-       common->next_buffhd_to_fill = &common->buffhds[0];
-       common->next_buffhd_to_drain = &common->buffhds[0];
-       exception_req_tag = common->exception_req_tag;
-       old_state = common->state;
-
-       if (old_state == FSG_STATE_ABORT_BULK_OUT)
-               common->state = FSG_STATE_STATUS_PHASE;
-       else {
-               for (i = 0; i < common->nluns; ++i) {
-                       curlun = common->luns[i];
-                       if (!curlun)
-                               continue;
-                       curlun->prevent_medium_removal = 0;
-                       curlun->sense_data = SS_NO_SENSE;
-                       curlun->unit_attention_data = SS_NO_SENSE;
-                       curlun->sense_data_info = 0;
-                       curlun->info_valid = 0;
-               }
-               common->state = FSG_STATE_IDLE;
-       }
-       spin_unlock_irq(&common->lock);
-
-       /* Carry out any extra actions required for the exception */
-       switch (old_state) {
-       case FSG_STATE_ABORT_BULK_OUT:
-               send_status(common);
-               spin_lock_irq(&common->lock);
-               if (common->state == FSG_STATE_STATUS_PHASE)
-                       common->state = FSG_STATE_IDLE;
-               spin_unlock_irq(&common->lock);
-               break;
-
-       case FSG_STATE_RESET:
-               /*
-                * In case we were forced against our will to halt a
-                * bulk endpoint, clear the halt now.  (The SuperH UDC
-                * requires this.)
-                */
-               if (!fsg_is_set(common))
-                       break;
-               if (test_and_clear_bit(IGNORE_BULK_OUT,
-                                      &common->fsg->atomic_bitflags))
-                       usb_ep_clear_halt(common->fsg->bulk_in);
-
-               if (common->ep0_req_tag == exception_req_tag)
-                       ep0_queue(common);      /* Complete the status stage */
-
-               /*
-                * Technically this should go here, but it would only be
-                * a waste of time.  Ditto for the INTERFACE_CHANGE and
-                * CONFIG_CHANGE cases.
-                */
-               /* for (i = 0; i < common->nluns; ++i) */
-               /*      if (common->luns[i]) */
-               /*              common->luns[i]->unit_attention_data = */
-               /*                      SS_RESET_OCCURRED;  */
-               break;
-
-       case FSG_STATE_CONFIG_CHANGE:
-               do_set_interface(common, common->new_fsg);
-               if (common->new_fsg)
-                       usb_composite_setup_continue(common->cdev);
-               break;
-
-       case FSG_STATE_EXIT:
-       case FSG_STATE_TERMINATED:
-               do_set_interface(common, NULL);         /* Free resources */
-               spin_lock_irq(&common->lock);
-               common->state = FSG_STATE_TERMINATED;   /* Stop the thread */
-               spin_unlock_irq(&common->lock);
-               break;
-
-       case FSG_STATE_INTERFACE_CHANGE:
-       case FSG_STATE_DISCONNECT:
-       case FSG_STATE_COMMAND_PHASE:
-       case FSG_STATE_DATA_PHASE:
-       case FSG_STATE_STATUS_PHASE:
-       case FSG_STATE_IDLE:
-               break;
-       }
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-static int fsg_main_thread(void *common_)
-{
-       struct fsg_common       *common = common_;
-
-       /*
-        * Allow the thread to be killed by a signal, but set the signal mask
-        * to block everything but INT, TERM, KILL, and USR1.
-        */
-       allow_signal(SIGINT);
-       allow_signal(SIGTERM);
-       allow_signal(SIGKILL);
-       allow_signal(SIGUSR1);
-
-       /* Allow the thread to be frozen */
-       set_freezable();
-
-       /*
-        * Arrange for userspace references to be interpreted as kernel
-        * pointers.  That way we can pass a kernel pointer to a routine
-        * that expects a __user pointer and it will work okay.
-        */
-       set_fs(get_ds());
-
-       /* The main loop */
-       while (common->state != FSG_STATE_TERMINATED) {
-               if (exception_in_progress(common) || signal_pending(current)) {
-                       handle_exception(common);
-                       continue;
-               }
-
-               if (!common->running) {
-                       sleep_thread(common, true);
-                       continue;
-               }
-
-               if (get_next_command(common))
-                       continue;
-
-               spin_lock_irq(&common->lock);
-               if (!exception_in_progress(common))
-                       common->state = FSG_STATE_DATA_PHASE;
-               spin_unlock_irq(&common->lock);
-
-               if (do_scsi_command(common) || finish_reply(common))
-                       continue;
-
-               spin_lock_irq(&common->lock);
-               if (!exception_in_progress(common))
-                       common->state = FSG_STATE_STATUS_PHASE;
-               spin_unlock_irq(&common->lock);
-
-               if (send_status(common))
-                       continue;
-
-               spin_lock_irq(&common->lock);
-               if (!exception_in_progress(common))
-                       common->state = FSG_STATE_IDLE;
-               spin_unlock_irq(&common->lock);
-       }
-
-       spin_lock_irq(&common->lock);
-       common->thread_task = NULL;
-       spin_unlock_irq(&common->lock);
-
-       if (!common->ops || !common->ops->thread_exits
-        || common->ops->thread_exits(common) < 0) {
-               struct fsg_lun **curlun_it = common->luns;
-               unsigned i = common->nluns;
-
-               down_write(&common->filesem);
-               for (; i--; ++curlun_it) {
-                       struct fsg_lun *curlun = *curlun_it;
-                       if (!curlun || !fsg_lun_is_open(curlun))
-                               continue;
-
-                       fsg_lun_close(curlun);
-                       curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
-               }
-               up_write(&common->filesem);
-       }
-
-       /* Let fsg_unbind() know the thread has exited */
-       complete_and_exit(&common->thread_notifier, 0);
-}
-
-
-/*************************** DEVICE ATTRIBUTES ***************************/
-
-static ssize_t ro_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
-       struct fsg_lun          *curlun = fsg_lun_from_dev(dev);
-
-       return fsg_show_ro(curlun, buf);
-}
-
-static ssize_t nofua_show(struct device *dev, struct device_attribute *attr,
-                         char *buf)
-{
-       struct fsg_lun          *curlun = fsg_lun_from_dev(dev);
-
-       return fsg_show_nofua(curlun, buf);
-}
-
-static ssize_t file_show(struct device *dev, struct device_attribute *attr,
-                        char *buf)
-{
-       struct fsg_lun          *curlun = fsg_lun_from_dev(dev);
-       struct rw_semaphore     *filesem = dev_get_drvdata(dev);
-
-       return fsg_show_file(curlun, filesem, buf);
-}
-
-static ssize_t ro_store(struct device *dev, struct device_attribute *attr,
-                       const char *buf, size_t count)
-{
-       struct fsg_lun          *curlun = fsg_lun_from_dev(dev);
-       struct rw_semaphore     *filesem = dev_get_drvdata(dev);
-
-       return fsg_store_ro(curlun, filesem, buf, count);
-}
-
-static ssize_t nofua_store(struct device *dev, struct device_attribute *attr,
-                          const char *buf, size_t count)
-{
-       struct fsg_lun          *curlun = fsg_lun_from_dev(dev);
-
-       return fsg_store_nofua(curlun, buf, count);
-}
-
-static ssize_t file_store(struct device *dev, struct device_attribute *attr,
-                         const char *buf, size_t count)
-{
-       struct fsg_lun          *curlun = fsg_lun_from_dev(dev);
-       struct rw_semaphore     *filesem = dev_get_drvdata(dev);
-
-       return fsg_store_file(curlun, filesem, buf, count);
-}
-
-static DEVICE_ATTR_RW(ro);
-static DEVICE_ATTR_RW(nofua);
-static DEVICE_ATTR_RW(file);
-
-static struct device_attribute dev_attr_ro_cdrom = __ATTR_RO(ro);
-static struct device_attribute dev_attr_file_nonremovable = __ATTR_RO(file);
-
-
-/****************************** FSG COMMON ******************************/
-
-static void fsg_common_release(struct kref *ref);
-
-static void fsg_lun_release(struct device *dev)
-{
-       /* Nothing needs to be done */
-}
-
-void fsg_common_get(struct fsg_common *common)
-{
-       kref_get(&common->ref);
-}
-EXPORT_SYMBOL_GPL(fsg_common_get);
-
-void fsg_common_put(struct fsg_common *common)
-{
-       kref_put(&common->ref, fsg_common_release);
-}
-EXPORT_SYMBOL_GPL(fsg_common_put);
-
-/* check if fsg_num_buffers is within a valid range */
-static inline int fsg_num_buffers_validate(unsigned int fsg_num_buffers)
-{
-       if (fsg_num_buffers >= 2 && fsg_num_buffers <= 4)
-               return 0;
-       pr_err("fsg_num_buffers %u is out of range (%d to %d)\n",
-              fsg_num_buffers, 2, 4);
-       return -EINVAL;
-}
-
-static struct fsg_common *fsg_common_setup(struct fsg_common *common)
-{
-       if (!common) {
-               common = kzalloc(sizeof(*common), GFP_KERNEL);
-               if (!common)
-                       return ERR_PTR(-ENOMEM);
-               common->free_storage_on_release = 1;
-       } else {
-               common->free_storage_on_release = 0;
-       }
-       init_rwsem(&common->filesem);
-       spin_lock_init(&common->lock);
-       kref_init(&common->ref);
-       init_completion(&common->thread_notifier);
-       init_waitqueue_head(&common->fsg_wait);
-       common->state = FSG_STATE_TERMINATED;
-
-       return common;
-}
-
-void fsg_common_set_sysfs(struct fsg_common *common, bool sysfs)
-{
-       common->sysfs = sysfs;
-}
-EXPORT_SYMBOL_GPL(fsg_common_set_sysfs);
-
-static void _fsg_common_free_buffers(struct fsg_buffhd *buffhds, unsigned n)
-{
-       if (buffhds) {
-               struct fsg_buffhd *bh = buffhds;
-               while (n--) {
-                       kfree(bh->buf);
-                       ++bh;
-               }
-               kfree(buffhds);
-       }
-}
-
-int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n)
-{
-       struct fsg_buffhd *bh, *buffhds;
-       int i, rc;
-
-       rc = fsg_num_buffers_validate(n);
-       if (rc != 0)
-               return rc;
-
-       buffhds = kcalloc(n, sizeof(*buffhds), GFP_KERNEL);
-       if (!buffhds)
-               return -ENOMEM;
-
-       /* Data buffers cyclic list */
-       bh = buffhds;
-       i = n;
-       goto buffhds_first_it;
-       do {
-               bh->next = bh + 1;
-               ++bh;
-buffhds_first_it:
-               bh->buf = kmalloc(FSG_BUFLEN, GFP_KERNEL);
-               if (unlikely(!bh->buf))
-                       goto error_release;
-       } while (--i);
-       bh->next = buffhds;
-
-       _fsg_common_free_buffers(common->buffhds, common->fsg_num_buffers);
-       common->fsg_num_buffers = n;
-       common->buffhds = buffhds;
-
-       return 0;
-
-error_release:
-       /*
-        * "buf"s pointed to by heads after n - i are NULL
-        * so releasing them won't hurt
-        */
-       _fsg_common_free_buffers(buffhds, n);
-
-       return -ENOMEM;
-}
-EXPORT_SYMBOL_GPL(fsg_common_set_num_buffers);
-
-static inline void fsg_common_remove_sysfs(struct fsg_lun *lun)
-{
-       device_remove_file(&lun->dev, &dev_attr_nofua);
-       /*
-        * device_remove_file() =>
-        *
-        * here the attr (e.g. dev_attr_ro) is only used to be passed to:
-        *
-        *      sysfs_remove_file() =>
-        *
-        *      here e.g. both dev_attr_ro_cdrom and dev_attr_ro are in
-        *      the same namespace and
-        *      from here only attr->name is passed to:
-        *
-        *              sysfs_hash_and_remove()
-        *
-        *              attr->name is the same for dev_attr_ro_cdrom and
-        *              dev_attr_ro
-        *              attr->name is the same for dev_attr_file and
-        *              dev_attr_file_nonremovable
-        *
-        * so we don't differentiate between removing e.g. dev_attr_ro_cdrom
-        * and dev_attr_ro
-        */
-       device_remove_file(&lun->dev, &dev_attr_ro);
-       device_remove_file(&lun->dev, &dev_attr_file);
-}
-
-void fsg_common_remove_lun(struct fsg_lun *lun, bool sysfs)
-{
-       if (sysfs) {
-               fsg_common_remove_sysfs(lun);
-               device_unregister(&lun->dev);
-       }
-       fsg_lun_close(lun);
-       kfree(lun);
-}
-EXPORT_SYMBOL_GPL(fsg_common_remove_lun);
-
-static void _fsg_common_remove_luns(struct fsg_common *common, int n)
-{
-       int i;
-
-       for (i = 0; i < n; ++i)
-               if (common->luns[i]) {
-                       fsg_common_remove_lun(common->luns[i], common->sysfs);
-                       common->luns[i] = NULL;
-               }
-}
-EXPORT_SYMBOL_GPL(fsg_common_remove_luns);
-
-void fsg_common_remove_luns(struct fsg_common *common)
-{
-       _fsg_common_remove_luns(common, common->nluns);
-}
-
-void fsg_common_free_luns(struct fsg_common *common)
-{
-       fsg_common_remove_luns(common);
-       kfree(common->luns);
-       common->luns = NULL;
-}
-EXPORT_SYMBOL_GPL(fsg_common_free_luns);
-
-int fsg_common_set_nluns(struct fsg_common *common, int nluns)
-{
-       struct fsg_lun **curlun;
-
-       /* Find out how many LUNs there should be */
-       if (nluns < 1 || nluns > FSG_MAX_LUNS) {
-               pr_err("invalid number of LUNs: %u\n", nluns);
-               return -EINVAL;
-       }
-
-       curlun = kcalloc(nluns, sizeof(*curlun), GFP_KERNEL);
-       if (unlikely(!curlun))
-               return -ENOMEM;
-
-       if (common->luns)
-               fsg_common_free_luns(common);
-
-       common->luns = curlun;
-       common->nluns = nluns;
-
-       pr_info("Number of LUNs=%d\n", common->nluns);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(fsg_common_set_nluns);
-
-void fsg_common_set_ops(struct fsg_common *common,
-                       const struct fsg_operations *ops)
-{
-       common->ops = ops;
-}
-EXPORT_SYMBOL_GPL(fsg_common_set_ops);
-
-void fsg_common_free_buffers(struct fsg_common *common)
-{
-       _fsg_common_free_buffers(common->buffhds, common->fsg_num_buffers);
-       common->buffhds = NULL;
-}
-EXPORT_SYMBOL_GPL(fsg_common_free_buffers);
-
-int fsg_common_set_cdev(struct fsg_common *common,
-                        struct usb_composite_dev *cdev, bool can_stall)
-{
-       struct usb_string *us;
-
-       common->gadget = cdev->gadget;
-       common->ep0 = cdev->gadget->ep0;
-       common->ep0req = cdev->req;
-       common->cdev = cdev;
-
-       us = usb_gstrings_attach(cdev, fsg_strings_array,
-                                ARRAY_SIZE(fsg_strings));
-       if (IS_ERR(us))
-               return PTR_ERR(us);
-
-       fsg_intf_desc.iInterface = us[FSG_STRING_INTERFACE].id;
-
-       /*
-        * Some peripheral controllers are known not to be able to
-        * halt bulk endpoints correctly.  If one of them is present,
-        * disable stalls.
-        */
-       common->can_stall = can_stall && !(gadget_is_at91(common->gadget));
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(fsg_common_set_cdev);
-
-static inline int fsg_common_add_sysfs(struct fsg_common *common,
-                                      struct fsg_lun *lun)
-{
-       int rc;
-
-       rc = device_register(&lun->dev);
-       if (rc) {
-               put_device(&lun->dev);
-               return rc;
-       }
-
-       rc = device_create_file(&lun->dev,
-                               lun->cdrom
-                             ? &dev_attr_ro_cdrom
-                             : &dev_attr_ro);
-       if (rc)
-               goto error;
-       rc = device_create_file(&lun->dev,
-                               lun->removable
-                             ? &dev_attr_file
-                             : &dev_attr_file_nonremovable);
-       if (rc)
-               goto error;
-       rc = device_create_file(&lun->dev, &dev_attr_nofua);
-       if (rc)
-               goto error;
-
-       return 0;
-
-error:
-       /* removing nonexistent files is a no-op */
-       fsg_common_remove_sysfs(lun);
-       device_unregister(&lun->dev);
-       return rc;
-}
-
-int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg,
-                         unsigned int id, const char *name,
-                         const char **name_pfx)
-{
-       struct fsg_lun *lun;
-       char *pathbuf, *p;
-       int rc = -ENOMEM;
-
-       if (!common->nluns || !common->luns)
-               return -ENODEV;
-
-       if (common->luns[id])
-               return -EBUSY;
-
-       if (!cfg->filename && !cfg->removable) {
-               pr_err("no file given for LUN%d\n", id);
-               return -EINVAL;
-       }
-
-       lun = kzalloc(sizeof(*lun), GFP_KERNEL);
-       if (!lun)
-               return -ENOMEM;
-
-       lun->name_pfx = name_pfx;
-
-       lun->cdrom = !!cfg->cdrom;
-       lun->ro = cfg->cdrom || cfg->ro;
-       lun->initially_ro = lun->ro;
-       lun->removable = !!cfg->removable;
-
-       if (!common->sysfs) {
-               /* we DON'T own the name!*/
-               lun->name = name;
-       } else {
-               lun->dev.release = fsg_lun_release;
-               lun->dev.parent = &common->gadget->dev;
-               dev_set_drvdata(&lun->dev, &common->filesem);
-               dev_set_name(&lun->dev, "%s", name);
-               lun->name = dev_name(&lun->dev);
-
-               rc = fsg_common_add_sysfs(common, lun);
-               if (rc) {
-                       pr_info("failed to register LUN%d: %d\n", id, rc);
-                       goto error_sysfs;
-               }
-       }
-
-       common->luns[id] = lun;
-
-       if (cfg->filename) {
-               rc = fsg_lun_open(lun, cfg->filename);
-               if (rc)
-                       goto error_lun;
-       }
-
-       pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
-       p = "(no medium)";
-       if (fsg_lun_is_open(lun)) {
-               p = "(error)";
-               if (pathbuf) {
-                       p = d_path(&lun->filp->f_path, pathbuf, PATH_MAX);
-                       if (IS_ERR(p))
-                               p = "(error)";
-               }
-       }
-       pr_info("LUN: %s%s%sfile: %s\n",
-             lun->removable ? "removable " : "",
-             lun->ro ? "read only " : "",
-             lun->cdrom ? "CD-ROM " : "",
-             p);
-       kfree(pathbuf);
-
-       return 0;
-
-error_lun:
-       if (common->sysfs) {
-               fsg_common_remove_sysfs(lun);
-               device_unregister(&lun->dev);
-       }
-       fsg_lun_close(lun);
-       common->luns[id] = NULL;
-error_sysfs:
-       kfree(lun);
-       return rc;
-}
-EXPORT_SYMBOL_GPL(fsg_common_create_lun);
-
-int fsg_common_create_luns(struct fsg_common *common, struct fsg_config *cfg)
-{
-       char buf[8]; /* enough for 100000000 different numbers, decimal */
-       int i, rc;
-
-       for (i = 0; i < common->nluns; ++i) {
-               snprintf(buf, sizeof(buf), "lun%d", i);
-               rc = fsg_common_create_lun(common, &cfg->luns[i], i, buf, NULL);
-               if (rc)
-                       goto fail;
-       }
-
-       pr_info("Number of LUNs=%d\n", common->nluns);
-
-       return 0;
-
-fail:
-       _fsg_common_remove_luns(common, i);
-       return rc;
-}
-EXPORT_SYMBOL_GPL(fsg_common_create_luns);
-
-void fsg_common_set_inquiry_string(struct fsg_common *common, const char *vn,
-                                  const char *pn)
-{
-       int i;
-
-       /* Prepare inquiryString */
-       i = get_default_bcdDevice();
-       snprintf(common->inquiry_string, sizeof(common->inquiry_string),
-                "%-8s%-16s%04x", vn ?: "Linux",
-                /* Assume product name dependent on the first LUN */
-                pn ?: ((*common->luns)->cdrom
-                    ? "File-CD Gadget"
-                    : "File-Stor Gadget"),
-                i);
-}
-EXPORT_SYMBOL_GPL(fsg_common_set_inquiry_string);
-
-int fsg_common_run_thread(struct fsg_common *common)
-{
-       common->state = FSG_STATE_IDLE;
-       /* Tell the thread to start working */
-       common->thread_task =
-               kthread_create(fsg_main_thread, common, "file-storage");
-       if (IS_ERR(common->thread_task)) {
-               common->state = FSG_STATE_TERMINATED;
-               return PTR_ERR(common->thread_task);
-       }
-
-       DBG(common, "I/O thread pid: %d\n", task_pid_nr(common->thread_task));
-
-       wake_up_process(common->thread_task);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(fsg_common_run_thread);
-
-static void fsg_common_release(struct kref *ref)
-{
-       struct fsg_common *common = container_of(ref, struct fsg_common, ref);
-
-       /* If the thread isn't already dead, tell it to exit now */
-       if (common->state != FSG_STATE_TERMINATED) {
-               raise_exception(common, FSG_STATE_EXIT);
-               wait_for_completion(&common->thread_notifier);
-       }
-
-       if (likely(common->luns)) {
-               struct fsg_lun **lun_it = common->luns;
-               unsigned i = common->nluns;
-
-               /* In error recovery common->nluns may be zero. */
-               for (; i; --i, ++lun_it) {
-                       struct fsg_lun *lun = *lun_it;
-                       if (!lun)
-                               continue;
-                       if (common->sysfs)
-                               fsg_common_remove_sysfs(lun);
-                       fsg_lun_close(lun);
-                       if (common->sysfs)
-                               device_unregister(&lun->dev);
-                       kfree(lun);
-               }
-
-               kfree(common->luns);
-       }
-
-       _fsg_common_free_buffers(common->buffhds, common->fsg_num_buffers);
-       if (common->free_storage_on_release)
-               kfree(common);
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
-{
-       struct fsg_dev          *fsg = fsg_from_func(f);
-       struct usb_gadget       *gadget = c->cdev->gadget;
-       int                     i;
-       struct usb_ep           *ep;
-       unsigned                max_burst;
-       int                     ret;
-       struct fsg_opts         *opts;
-
-       opts = fsg_opts_from_func_inst(f->fi);
-       if (!opts->no_configfs) {
-               ret = fsg_common_set_cdev(fsg->common, c->cdev,
-                                         fsg->common->can_stall);
-               if (ret)
-                       return ret;
-               fsg_common_set_inquiry_string(fsg->common, NULL, NULL);
-               ret = fsg_common_run_thread(fsg->common);
-               if (ret)
-                       return ret;
-       }
-
-       fsg->gadget = gadget;
-
-       /* New interface */
-       i = usb_interface_id(c, f);
-       if (i < 0)
-               return i;
-       fsg_intf_desc.bInterfaceNumber = i;
-       fsg->interface_number = i;
-
-       /* Find all the endpoints we will use */
-       ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc);
-       if (!ep)
-               goto autoconf_fail;
-       ep->driver_data = fsg->common;  /* claim the endpoint */
-       fsg->bulk_in = ep;
-
-       ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc);
-       if (!ep)
-               goto autoconf_fail;
-       ep->driver_data = fsg->common;  /* claim the endpoint */
-       fsg->bulk_out = ep;
-
-       /* Assume endpoint addresses are the same for both speeds */
-       fsg_hs_bulk_in_desc.bEndpointAddress =
-               fsg_fs_bulk_in_desc.bEndpointAddress;
-       fsg_hs_bulk_out_desc.bEndpointAddress =
-               fsg_fs_bulk_out_desc.bEndpointAddress;
-
-       /* Calculate bMaxBurst, we know packet size is 1024 */
-       max_burst = min_t(unsigned, FSG_BUFLEN / 1024, 15);
-
-       fsg_ss_bulk_in_desc.bEndpointAddress =
-               fsg_fs_bulk_in_desc.bEndpointAddress;
-       fsg_ss_bulk_in_comp_desc.bMaxBurst = max_burst;
-
-       fsg_ss_bulk_out_desc.bEndpointAddress =
-               fsg_fs_bulk_out_desc.bEndpointAddress;
-       fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst;
-
-       ret = usb_assign_descriptors(f, fsg_fs_function, fsg_hs_function,
-                       fsg_ss_function);
-       if (ret)
-               goto autoconf_fail;
-
-       return 0;
-
-autoconf_fail:
-       ERROR(fsg, "unable to autoconfigure all endpoints\n");
-       return -ENOTSUPP;
-}
-
-/****************************** ALLOCATE FUNCTION *************************/
-
-static void fsg_unbind(struct usb_configuration *c, struct usb_function *f)
-{
-       struct fsg_dev          *fsg = fsg_from_func(f);
-       struct fsg_common       *common = fsg->common;
-
-       DBG(fsg, "unbind\n");
-       if (fsg->common->fsg == fsg) {
-               fsg->common->new_fsg = NULL;
-               raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
-               /* FIXME: make interruptible or killable somehow? */
-               wait_event(common->fsg_wait, common->fsg != fsg);
-       }
-
-       usb_free_all_descriptors(&fsg->function);
-}
-
-static inline struct fsg_lun_opts *to_fsg_lun_opts(struct config_item *item)
-{
-       return container_of(to_config_group(item), struct fsg_lun_opts, group);
-}
-
-static inline struct fsg_opts *to_fsg_opts(struct config_item *item)
-{
-       return container_of(to_config_group(item), struct fsg_opts,
-                           func_inst.group);
-}
-
-CONFIGFS_ATTR_STRUCT(fsg_lun_opts);
-CONFIGFS_ATTR_OPS(fsg_lun_opts);
-
-static void fsg_lun_attr_release(struct config_item *item)
-{
-       struct fsg_lun_opts *lun_opts;
-
-       lun_opts = to_fsg_lun_opts(item);
-       kfree(lun_opts);
-}
-
-static struct configfs_item_operations fsg_lun_item_ops = {
-       .release                = fsg_lun_attr_release,
-       .show_attribute         = fsg_lun_opts_attr_show,
-       .store_attribute        = fsg_lun_opts_attr_store,
-};
-
-static ssize_t fsg_lun_opts_file_show(struct fsg_lun_opts *opts, char *page)
-{
-       struct fsg_opts *fsg_opts;
-
-       fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent);
-
-       return fsg_show_file(opts->lun, &fsg_opts->common->filesem, page);
-}
-
-static ssize_t fsg_lun_opts_file_store(struct fsg_lun_opts *opts,
-                                      const char *page, size_t len)
-{
-       struct fsg_opts *fsg_opts;
-
-       fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent);
-
-       return fsg_store_file(opts->lun, &fsg_opts->common->filesem, page, len);
-}
-
-static struct fsg_lun_opts_attribute fsg_lun_opts_file =
-       __CONFIGFS_ATTR(file, S_IRUGO | S_IWUSR, fsg_lun_opts_file_show,
-                       fsg_lun_opts_file_store);
-
-static ssize_t fsg_lun_opts_ro_show(struct fsg_lun_opts *opts, char *page)
-{
-       return fsg_show_ro(opts->lun, page);
-}
-
-static ssize_t fsg_lun_opts_ro_store(struct fsg_lun_opts *opts,
-                                      const char *page, size_t len)
-{
-       struct fsg_opts *fsg_opts;
-
-       fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent);
-
-       return fsg_store_ro(opts->lun, &fsg_opts->common->filesem, page, len);
-}
-
-static struct fsg_lun_opts_attribute fsg_lun_opts_ro =
-       __CONFIGFS_ATTR(ro, S_IRUGO | S_IWUSR, fsg_lun_opts_ro_show,
-                       fsg_lun_opts_ro_store);
-
-static ssize_t fsg_lun_opts_removable_show(struct fsg_lun_opts *opts,
-                                          char *page)
-{
-       return fsg_show_removable(opts->lun, page);
-}
-
-static ssize_t fsg_lun_opts_removable_store(struct fsg_lun_opts *opts,
-                                      const char *page, size_t len)
-{
-       return fsg_store_removable(opts->lun, page, len);
-}
-
-static struct fsg_lun_opts_attribute fsg_lun_opts_removable =
-       __CONFIGFS_ATTR(removable, S_IRUGO | S_IWUSR,
-                       fsg_lun_opts_removable_show,
-                       fsg_lun_opts_removable_store);
-
-static ssize_t fsg_lun_opts_cdrom_show(struct fsg_lun_opts *opts, char *page)
-{
-       return fsg_show_cdrom(opts->lun, page);
-}
-
-static ssize_t fsg_lun_opts_cdrom_store(struct fsg_lun_opts *opts,
-                                      const char *page, size_t len)
-{
-       struct fsg_opts *fsg_opts;
-
-       fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent);
-
-       return fsg_store_cdrom(opts->lun, &fsg_opts->common->filesem, page,
-                              len);
-}
-
-static struct fsg_lun_opts_attribute fsg_lun_opts_cdrom =
-       __CONFIGFS_ATTR(cdrom, S_IRUGO | S_IWUSR, fsg_lun_opts_cdrom_show,
-                       fsg_lun_opts_cdrom_store);
-
-static ssize_t fsg_lun_opts_nofua_show(struct fsg_lun_opts *opts, char *page)
-{
-       return fsg_show_nofua(opts->lun, page);
-}
-
-static ssize_t fsg_lun_opts_nofua_store(struct fsg_lun_opts *opts,
-                                      const char *page, size_t len)
-{
-       return fsg_store_nofua(opts->lun, page, len);
-}
-
-static struct fsg_lun_opts_attribute fsg_lun_opts_nofua =
-       __CONFIGFS_ATTR(nofua, S_IRUGO | S_IWUSR, fsg_lun_opts_nofua_show,
-                       fsg_lun_opts_nofua_store);
-
-static struct configfs_attribute *fsg_lun_attrs[] = {
-       &fsg_lun_opts_file.attr,
-       &fsg_lun_opts_ro.attr,
-       &fsg_lun_opts_removable.attr,
-       &fsg_lun_opts_cdrom.attr,
-       &fsg_lun_opts_nofua.attr,
-       NULL,
-};
-
-static struct config_item_type fsg_lun_type = {
-       .ct_item_ops    = &fsg_lun_item_ops,
-       .ct_attrs       = fsg_lun_attrs,
-       .ct_owner       = THIS_MODULE,
-};
-
-static struct config_group *fsg_lun_make(struct config_group *group,
-                                        const char *name)
-{
-       struct fsg_lun_opts *opts;
-       struct fsg_opts *fsg_opts;
-       struct fsg_lun_config config;
-       char *num_str;
-       u8 num;
-       int ret;
-
-       num_str = strchr(name, '.');
-       if (!num_str) {
-               pr_err("Unable to locate . in LUN.NUMBER\n");
-               return ERR_PTR(-EINVAL);
-       }
-       num_str++;
-
-       ret = kstrtou8(num_str, 0, &num);
-       if (ret)
-               return ERR_PTR(ret);
-
-       fsg_opts = to_fsg_opts(&group->cg_item);
-       if (num >= FSG_MAX_LUNS)
-               return ERR_PTR(-ERANGE);
-
-       mutex_lock(&fsg_opts->lock);
-       if (fsg_opts->refcnt || fsg_opts->common->luns[num]) {
-               ret = -EBUSY;
-               goto out;
-       }
-
-       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
-       if (!opts) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       memset(&config, 0, sizeof(config));
-       config.removable = true;
-
-       ret = fsg_common_create_lun(fsg_opts->common, &config, num, name,
-                                   (const char **)&group->cg_item.ci_name);
-       if (ret) {
-               kfree(opts);
-               goto out;
-       }
-       opts->lun = fsg_opts->common->luns[num];
-       opts->lun_id = num;
-       mutex_unlock(&fsg_opts->lock);
-
-       config_group_init_type_name(&opts->group, name, &fsg_lun_type);
-
-       return &opts->group;
-out:
-       mutex_unlock(&fsg_opts->lock);
-       return ERR_PTR(ret);
-}
-
-static void fsg_lun_drop(struct config_group *group, struct config_item *item)
-{
-       struct fsg_lun_opts *lun_opts;
-       struct fsg_opts *fsg_opts;
-
-       lun_opts = to_fsg_lun_opts(item);
-       fsg_opts = to_fsg_opts(&group->cg_item);
-
-       mutex_lock(&fsg_opts->lock);
-       if (fsg_opts->refcnt) {
-               struct config_item *gadget;
-
-               gadget = group->cg_item.ci_parent->ci_parent;
-               unregister_gadget_item(gadget);
-       }
-
-       fsg_common_remove_lun(lun_opts->lun, fsg_opts->common->sysfs);
-       fsg_opts->common->luns[lun_opts->lun_id] = NULL;
-       lun_opts->lun_id = 0;
-       mutex_unlock(&fsg_opts->lock);
-
-       config_item_put(item);
-}
-
-CONFIGFS_ATTR_STRUCT(fsg_opts);
-CONFIGFS_ATTR_OPS(fsg_opts);
-
-static void fsg_attr_release(struct config_item *item)
-{
-       struct fsg_opts *opts = to_fsg_opts(item);
-
-       usb_put_function_instance(&opts->func_inst);
-}
-
-static struct configfs_item_operations fsg_item_ops = {
-       .release                = fsg_attr_release,
-       .show_attribute         = fsg_opts_attr_show,
-       .store_attribute        = fsg_opts_attr_store,
-};
-
-static ssize_t fsg_opts_stall_show(struct fsg_opts *opts, char *page)
-{
-       int result;
-
-       mutex_lock(&opts->lock);
-       result = sprintf(page, "%d", opts->common->can_stall);
-       mutex_unlock(&opts->lock);
-
-       return result;
-}
-
-static ssize_t fsg_opts_stall_store(struct fsg_opts *opts, const char *page,
-                                   size_t len)
-{
-       int ret;
-       bool stall;
-
-       mutex_lock(&opts->lock);
-
-       if (opts->refcnt) {
-               mutex_unlock(&opts->lock);
-               return -EBUSY;
-       }
-
-       ret = strtobool(page, &stall);
-       if (!ret) {
-               opts->common->can_stall = stall;
-               ret = len;
-       }
-
-       mutex_unlock(&opts->lock);
-
-       return ret;
-}
-
-static struct fsg_opts_attribute fsg_opts_stall =
-       __CONFIGFS_ATTR(stall, S_IRUGO | S_IWUSR, fsg_opts_stall_show,
-                       fsg_opts_stall_store);
-
-#ifdef CONFIG_USB_GADGET_DEBUG_FILES
-static ssize_t fsg_opts_num_buffers_show(struct fsg_opts *opts, char *page)
-{
-       int result;
-
-       mutex_lock(&opts->lock);
-       result = sprintf(page, "%d", opts->common->fsg_num_buffers);
-       mutex_unlock(&opts->lock);
-
-       return result;
-}
-
-static ssize_t fsg_opts_num_buffers_store(struct fsg_opts *opts,
-                                         const char *page, size_t len)
-{
-       int ret;
-       u8 num;
-
-       mutex_lock(&opts->lock);
-       if (opts->refcnt) {
-               ret = -EBUSY;
-               goto end;
-       }
-       ret = kstrtou8(page, 0, &num);
-       if (ret)
-               goto end;
-
-       ret = fsg_num_buffers_validate(num);
-       if (ret)
-               goto end;
-
-       fsg_common_set_num_buffers(opts->common, num);
-       ret = len;
-
-end:
-       mutex_unlock(&opts->lock);
-       return ret;
-}
-
-static struct fsg_opts_attribute fsg_opts_num_buffers =
-       __CONFIGFS_ATTR(num_buffers, S_IRUGO | S_IWUSR,
-                       fsg_opts_num_buffers_show,
-                       fsg_opts_num_buffers_store);
-
-#endif
-
-static struct configfs_attribute *fsg_attrs[] = {
-       &fsg_opts_stall.attr,
-#ifdef CONFIG_USB_GADGET_DEBUG_FILES
-       &fsg_opts_num_buffers.attr,
-#endif
-       NULL,
-};
-
-static struct configfs_group_operations fsg_group_ops = {
-       .make_group     = fsg_lun_make,
-       .drop_item      = fsg_lun_drop,
-};
-
-static struct config_item_type fsg_func_type = {
-       .ct_item_ops    = &fsg_item_ops,
-       .ct_group_ops   = &fsg_group_ops,
-       .ct_attrs       = fsg_attrs,
-       .ct_owner       = THIS_MODULE,
-};
-
-static void fsg_free_inst(struct usb_function_instance *fi)
-{
-       struct fsg_opts *opts;
-
-       opts = fsg_opts_from_func_inst(fi);
-       fsg_common_put(opts->common);
-       kfree(opts);
-}
-
-static struct usb_function_instance *fsg_alloc_inst(void)
-{
-       struct fsg_opts *opts;
-       struct fsg_lun_config config;
-       int rc;
-
-       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
-       if (!opts)
-               return ERR_PTR(-ENOMEM);
-       mutex_init(&opts->lock);
-       opts->func_inst.free_func_inst = fsg_free_inst;
-       opts->common = fsg_common_setup(opts->common);
-       if (IS_ERR(opts->common)) {
-               rc = PTR_ERR(opts->common);
-               goto release_opts;
-       }
-       rc = fsg_common_set_nluns(opts->common, FSG_MAX_LUNS);
-       if (rc)
-               goto release_opts;
-
-       rc = fsg_common_set_num_buffers(opts->common,
-                                       CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS);
-       if (rc)
-               goto release_luns;
-
-       pr_info(FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n");
-
-       memset(&config, 0, sizeof(config));
-       config.removable = true;
-       rc = fsg_common_create_lun(opts->common, &config, 0, "lun.0",
-                       (const char **)&opts->func_inst.group.cg_item.ci_name);
-       opts->lun0.lun = opts->common->luns[0];
-       opts->lun0.lun_id = 0;
-       config_group_init_type_name(&opts->lun0.group, "lun.0", &fsg_lun_type);
-       opts->default_groups[0] = &opts->lun0.group;
-       opts->func_inst.group.default_groups = opts->default_groups;
-
-       config_group_init_type_name(&opts->func_inst.group, "", &fsg_func_type);
-
-       return &opts->func_inst;
-
-release_luns:
-       kfree(opts->common->luns);
-release_opts:
-       kfree(opts);
-       return ERR_PTR(rc);
-}
-
-static void fsg_free(struct usb_function *f)
-{
-       struct fsg_dev *fsg;
-       struct fsg_opts *opts;
-
-       fsg = container_of(f, struct fsg_dev, function);
-       opts = container_of(f->fi, struct fsg_opts, func_inst);
-
-       mutex_lock(&opts->lock);
-       opts->refcnt--;
-       mutex_unlock(&opts->lock);
-
-       kfree(fsg);
-}
-
-static struct usb_function *fsg_alloc(struct usb_function_instance *fi)
-{
-       struct fsg_opts *opts = fsg_opts_from_func_inst(fi);
-       struct fsg_common *common = opts->common;
-       struct fsg_dev *fsg;
-
-       fsg = kzalloc(sizeof(*fsg), GFP_KERNEL);
-       if (unlikely(!fsg))
-               return ERR_PTR(-ENOMEM);
-
-       mutex_lock(&opts->lock);
-       opts->refcnt++;
-       mutex_unlock(&opts->lock);
-       fsg->function.name      = FSG_DRIVER_DESC;
-       fsg->function.bind      = fsg_bind;
-       fsg->function.unbind    = fsg_unbind;
-       fsg->function.setup     = fsg_setup;
-       fsg->function.set_alt   = fsg_set_alt;
-       fsg->function.disable   = fsg_disable;
-       fsg->function.free_func = fsg_free;
-
-       fsg->common               = common;
-
-       return &fsg->function;
-}
-
-DECLARE_USB_FUNCTION_INIT(mass_storage, fsg_alloc_inst, fsg_alloc);
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Michal Nazarewicz");
-
-/************************* Module parameters *************************/
-
-
-void fsg_config_from_params(struct fsg_config *cfg,
-                      const struct fsg_module_parameters *params,
-                      unsigned int fsg_num_buffers)
-{
-       struct fsg_lun_config *lun;
-       unsigned i;
-
-       /* Configure LUNs */
-       cfg->nluns =
-               min(params->luns ?: (params->file_count ?: 1u),
-                   (unsigned)FSG_MAX_LUNS);
-       for (i = 0, lun = cfg->luns; i < cfg->nluns; ++i, ++lun) {
-               lun->ro = !!params->ro[i];
-               lun->cdrom = !!params->cdrom[i];
-               lun->removable = !!params->removable[i];
-               lun->filename =
-                       params->file_count > i && params->file[i][0]
-                       ? params->file[i]
-                       : NULL;
-       }
-
-       /* Let MSF use defaults */
-       cfg->vendor_name = NULL;
-       cfg->product_name = NULL;
-
-       cfg->ops = NULL;
-       cfg->private_data = NULL;
-
-       /* Finalise */
-       cfg->can_stall = params->stall;
-       cfg->fsg_num_buffers = fsg_num_buffers;
-}
-EXPORT_SYMBOL_GPL(fsg_config_from_params);
-
diff --git a/drivers/usb/gadget/f_mass_storage.h b/drivers/usb/gadget/f_mass_storage.h
deleted file mode 100644 (file)
index b4866fc..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-#ifndef USB_F_MASS_STORAGE_H
-#define USB_F_MASS_STORAGE_H
-
-#include <linux/usb/composite.h>
-#include "storage_common.h"
-
-struct fsg_module_parameters {
-       char            *file[FSG_MAX_LUNS];
-       bool            ro[FSG_MAX_LUNS];
-       bool            removable[FSG_MAX_LUNS];
-       bool            cdrom[FSG_MAX_LUNS];
-       bool            nofua[FSG_MAX_LUNS];
-
-       unsigned int    file_count, ro_count, removable_count, cdrom_count;
-       unsigned int    nofua_count;
-       unsigned int    luns;   /* nluns */
-       bool            stall;  /* can_stall */
-};
-
-#define _FSG_MODULE_PARAM_ARRAY(prefix, params, name, type, desc)      \
-       module_param_array_named(prefix ## name, params.name, type,     \
-                                &prefix ## params.name ## _count,      \
-                                S_IRUGO);                              \
-       MODULE_PARM_DESC(prefix ## name, desc)
-
-#define _FSG_MODULE_PARAM(prefix, params, name, type, desc)            \
-       module_param_named(prefix ## name, params.name, type,           \
-                          S_IRUGO);                                    \
-       MODULE_PARM_DESC(prefix ## name, desc)
-
-#define __FSG_MODULE_PARAMETERS(prefix, params)                                \
-       _FSG_MODULE_PARAM_ARRAY(prefix, params, file, charp,            \
-                               "names of backing files or devices");   \
-       _FSG_MODULE_PARAM_ARRAY(prefix, params, ro, bool,               \
-                               "true to force read-only");             \
-       _FSG_MODULE_PARAM_ARRAY(prefix, params, removable, bool,        \
-                               "true to simulate removable media");    \
-       _FSG_MODULE_PARAM_ARRAY(prefix, params, cdrom, bool,            \
-                               "true to simulate CD-ROM instead of disk"); \
-       _FSG_MODULE_PARAM_ARRAY(prefix, params, nofua, bool,            \
-                               "true to ignore SCSI WRITE(10,12) FUA bit"); \
-       _FSG_MODULE_PARAM(prefix, params, luns, uint,                   \
-                         "number of LUNs");                            \
-       _FSG_MODULE_PARAM(prefix, params, stall, bool,                  \
-                         "false to prevent bulk stalls")
-
-#ifdef CONFIG_USB_GADGET_DEBUG_FILES
-
-#define FSG_MODULE_PARAMETERS(prefix, params)                          \
-       __FSG_MODULE_PARAMETERS(prefix, params);                        \
-       module_param_named(num_buffers, fsg_num_buffers, uint, S_IRUGO);\
-       MODULE_PARM_DESC(num_buffers, "Number of pipeline buffers")
-#else
-
-#define FSG_MODULE_PARAMETERS(prefix, params)                          \
-       __FSG_MODULE_PARAMETERS(prefix, params)
-
-#endif
-
-struct fsg_common;
-
-/* FSF callback functions */
-struct fsg_operations {
-       /*
-        * Callback function to call when thread exits.  If no
-        * callback is set or it returns value lower then zero MSF
-        * will force eject all LUNs it operates on (including those
-        * marked as non-removable or with prevent_medium_removal flag
-        * set).
-        */
-       int (*thread_exits)(struct fsg_common *common);
-};
-
-struct fsg_lun_opts {
-       struct config_group group;
-       struct fsg_lun *lun;
-       int lun_id;
-};
-
-struct fsg_opts {
-       struct fsg_common *common;
-       struct usb_function_instance func_inst;
-       struct fsg_lun_opts lun0;
-       struct config_group *default_groups[2];
-       bool no_configfs; /* for legacy gadgets */
-
-       /*
-        * Read/write access to configfs attributes is handled by configfs.
-        *
-        * This is to protect the data from concurrent access by read/write
-        * and create symlink/remove symlink.
-        */
-       struct mutex                    lock;
-       int                             refcnt;
-};
-
-struct fsg_lun_config {
-       const char *filename;
-       char ro;
-       char removable;
-       char cdrom;
-       char nofua;
-};
-
-struct fsg_config {
-       unsigned nluns;
-       struct fsg_lun_config luns[FSG_MAX_LUNS];
-
-       /* Callback functions. */
-       const struct fsg_operations     *ops;
-       /* Gadget's private data. */
-       void                    *private_data;
-
-       const char *vendor_name;                /*  8 characters or less */
-       const char *product_name;               /* 16 characters or less */
-
-       char                    can_stall;
-       unsigned int            fsg_num_buffers;
-};
-
-static inline struct fsg_opts *
-fsg_opts_from_func_inst(const struct usb_function_instance *fi)
-{
-       return container_of(fi, struct fsg_opts, func_inst);
-}
-
-void fsg_common_get(struct fsg_common *common);
-
-void fsg_common_put(struct fsg_common *common);
-
-void fsg_common_set_sysfs(struct fsg_common *common, bool sysfs);
-
-int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n);
-
-void fsg_common_free_buffers(struct fsg_common *common);
-
-int fsg_common_set_cdev(struct fsg_common *common,
-                       struct usb_composite_dev *cdev, bool can_stall);
-
-void fsg_common_remove_lun(struct fsg_lun *lun, bool sysfs);
-
-void fsg_common_remove_luns(struct fsg_common *common);
-
-void fsg_common_free_luns(struct fsg_common *common);
-
-int fsg_common_set_nluns(struct fsg_common *common, int nluns);
-
-void fsg_common_set_ops(struct fsg_common *common,
-                       const struct fsg_operations *ops);
-
-int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg,
-                         unsigned int id, const char *name,
-                         const char **name_pfx);
-
-int fsg_common_create_luns(struct fsg_common *common, struct fsg_config *cfg);
-
-void fsg_common_set_inquiry_string(struct fsg_common *common, const char *vn,
-                                  const char *pn);
-
-int fsg_common_run_thread(struct fsg_common *common);
-
-void fsg_config_from_params(struct fsg_config *cfg,
-                           const struct fsg_module_parameters *params,
-                           unsigned int fsg_num_buffers);
-
-#endif /* USB_F_MASS_STORAGE_H */
diff --git a/drivers/usb/gadget/f_midi.c b/drivers/usb/gadget/f_midi.c
deleted file mode 100644 (file)
index 807b31c..0000000
+++ /dev/null
@@ -1,986 +0,0 @@
-/*
- * f_midi.c -- USB MIDI class function driver
- *
- * Copyright (C) 2006 Thumtronics Pty Ltd.
- * Developed for Thumtronics by Grey Innovation
- * Ben Williamson <ben.williamson@greyinnovation.com>
- *
- * Rewritten for the composite framework
- *   Copyright (C) 2011 Daniel Mack <zonque@gmail.com>
- *
- * Based on drivers/usb/gadget/f_audio.c,
- *   Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
- *   Copyright (C) 2008 Analog Devices, Inc
- *
- * and drivers/usb/gadget/midi.c,
- *   Copyright (C) 2006 Thumtronics Pty Ltd.
- *   Ben Williamson <ben.williamson@greyinnovation.com>
- *
- * Licensed under the GPL-2 or later.
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/device.h>
-
-#include <sound/core.h>
-#include <sound/initval.h>
-#include <sound/rawmidi.h>
-
-#include <linux/usb/ch9.h>
-#include <linux/usb/gadget.h>
-#include <linux/usb/audio.h>
-#include <linux/usb/midi.h>
-
-#include "u_f.h"
-
-MODULE_AUTHOR("Ben Williamson");
-MODULE_LICENSE("GPL v2");
-
-static const char f_midi_shortname[] = "f_midi";
-static const char f_midi_longname[] = "MIDI Gadget";
-
-/*
- * We can only handle 16 cables on one single endpoint, as cable numbers are
- * stored in 4-bit fields. And as the interface currently only holds one
- * single endpoint, this is the maximum number of ports we can allow.
- */
-#define MAX_PORTS 16
-
-/*
- * This is a gadget, and the IN/OUT naming is from the host's perspective.
- * USB -> OUT endpoint -> rawmidi
- * USB <- IN endpoint  <- rawmidi
- */
-struct gmidi_in_port {
-       struct f_midi *midi;
-       int active;
-       uint8_t cable;
-       uint8_t state;
-#define STATE_UNKNOWN  0
-#define STATE_1PARAM   1
-#define STATE_2PARAM_1 2
-#define STATE_2PARAM_2 3
-#define STATE_SYSEX_0  4
-#define STATE_SYSEX_1  5
-#define STATE_SYSEX_2  6
-       uint8_t data[2];
-};
-
-struct f_midi {
-       struct usb_function     func;
-       struct usb_gadget       *gadget;
-       struct usb_ep           *in_ep, *out_ep;
-       struct snd_card         *card;
-       struct snd_rawmidi      *rmidi;
-
-       struct snd_rawmidi_substream *in_substream[MAX_PORTS];
-       struct snd_rawmidi_substream *out_substream[MAX_PORTS];
-       struct gmidi_in_port    *in_port[MAX_PORTS];
-
-       unsigned long           out_triggered;
-       struct tasklet_struct   tasklet;
-       unsigned int in_ports;
-       unsigned int out_ports;
-       int index;
-       char *id;
-       unsigned int buflen, qlen;
-};
-
-static inline struct f_midi *func_to_midi(struct usb_function *f)
-{
-       return container_of(f, struct f_midi, func);
-}
-
-static void f_midi_transmit(struct f_midi *midi, struct usb_request *req);
-
-DECLARE_UAC_AC_HEADER_DESCRIPTOR(1);
-DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
-DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(16);
-
-/* B.3.1  Standard AC Interface Descriptor */
-static struct usb_interface_descriptor ac_interface_desc __initdata = {
-       .bLength =              USB_DT_INTERFACE_SIZE,
-       .bDescriptorType =      USB_DT_INTERFACE,
-       /* .bInterfaceNumber =  DYNAMIC */
-       /* .bNumEndpoints =     DYNAMIC */
-       .bInterfaceClass =      USB_CLASS_AUDIO,
-       .bInterfaceSubClass =   USB_SUBCLASS_AUDIOCONTROL,
-       /* .iInterface =        DYNAMIC */
-};
-
-/* B.3.2  Class-Specific AC Interface Descriptor */
-static struct uac1_ac_header_descriptor_1 ac_header_desc __initdata = {
-       .bLength =              UAC_DT_AC_HEADER_SIZE(1),
-       .bDescriptorType =      USB_DT_CS_INTERFACE,
-       .bDescriptorSubtype =   USB_MS_HEADER,
-       .bcdADC =               cpu_to_le16(0x0100),
-       .wTotalLength =         cpu_to_le16(UAC_DT_AC_HEADER_SIZE(1)),
-       .bInCollection =        1,
-       /* .baInterfaceNr =     DYNAMIC */
-};
-
-/* B.4.1  Standard MS Interface Descriptor */
-static struct usb_interface_descriptor ms_interface_desc __initdata = {
-       .bLength =              USB_DT_INTERFACE_SIZE,
-       .bDescriptorType =      USB_DT_INTERFACE,
-       /* .bInterfaceNumber =  DYNAMIC */
-       .bNumEndpoints =        2,
-       .bInterfaceClass =      USB_CLASS_AUDIO,
-       .bInterfaceSubClass =   USB_SUBCLASS_MIDISTREAMING,
-       /* .iInterface =        DYNAMIC */
-};
-
-/* B.4.2  Class-Specific MS Interface Descriptor */
-static struct usb_ms_header_descriptor ms_header_desc __initdata = {
-       .bLength =              USB_DT_MS_HEADER_SIZE,
-       .bDescriptorType =      USB_DT_CS_INTERFACE,
-       .bDescriptorSubtype =   USB_MS_HEADER,
-       .bcdMSC =               cpu_to_le16(0x0100),
-       /* .wTotalLength =      DYNAMIC */
-};
-
-/* B.5.1  Standard Bulk OUT Endpoint Descriptor */
-static struct usb_endpoint_descriptor bulk_out_desc = {
-       .bLength =              USB_DT_ENDPOINT_AUDIO_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-       .bEndpointAddress =     USB_DIR_OUT,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-};
-
-/* B.5.2  Class-specific MS Bulk OUT Endpoint Descriptor */
-static struct usb_ms_endpoint_descriptor_16 ms_out_desc = {
-       /* .bLength =           DYNAMIC */
-       .bDescriptorType =      USB_DT_CS_ENDPOINT,
-       .bDescriptorSubtype =   USB_MS_GENERAL,
-       /* .bNumEmbMIDIJack =   DYNAMIC */
-       /* .baAssocJackID =     DYNAMIC */
-};
-
-/* B.6.1  Standard Bulk IN Endpoint Descriptor */
-static struct usb_endpoint_descriptor bulk_in_desc = {
-       .bLength =              USB_DT_ENDPOINT_AUDIO_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-};
-
-/* B.6.2  Class-specific MS Bulk IN Endpoint Descriptor */
-static struct usb_ms_endpoint_descriptor_16 ms_in_desc = {
-       /* .bLength =           DYNAMIC */
-       .bDescriptorType =      USB_DT_CS_ENDPOINT,
-       .bDescriptorSubtype =   USB_MS_GENERAL,
-       /* .bNumEmbMIDIJack =   DYNAMIC */
-       /* .baAssocJackID =     DYNAMIC */
-};
-
-/* string IDs are assigned dynamically */
-
-#define STRING_FUNC_IDX                        0
-
-static struct usb_string midi_string_defs[] = {
-       [STRING_FUNC_IDX].s = "MIDI function",
-       {  } /* end of list */
-};
-
-static struct usb_gadget_strings midi_stringtab = {
-       .language       = 0x0409,       /* en-us */
-       .strings        = midi_string_defs,
-};
-
-static struct usb_gadget_strings *midi_strings[] = {
-       &midi_stringtab,
-       NULL,
-};
-
-static inline struct usb_request *midi_alloc_ep_req(struct usb_ep *ep,
-                                                   unsigned length)
-{
-       return alloc_ep_req(ep, length, length);
-}
-
-static void free_ep_req(struct usb_ep *ep, struct usb_request *req)
-{
-       kfree(req->buf);
-       usb_ep_free_request(ep, req);
-}
-
-static const uint8_t f_midi_cin_length[] = {
-       0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1
-};
-
-/*
- * Receives a chunk of MIDI data.
- */
-static void f_midi_read_data(struct usb_ep *ep, int cable,
-                            uint8_t *data, int length)
-{
-       struct f_midi *midi = ep->driver_data;
-       struct snd_rawmidi_substream *substream = midi->out_substream[cable];
-
-       if (!substream)
-               /* Nobody is listening - throw it on the floor. */
-               return;
-
-       if (!test_bit(cable, &midi->out_triggered))
-               return;
-
-       snd_rawmidi_receive(substream, data, length);
-}
-
-static void f_midi_handle_out_data(struct usb_ep *ep, struct usb_request *req)
-{
-       unsigned int i;
-       u8 *buf = req->buf;
-
-       for (i = 0; i + 3 < req->actual; i += 4)
-               if (buf[i] != 0) {
-                       int cable = buf[i] >> 4;
-                       int length = f_midi_cin_length[buf[i] & 0x0f];
-                       f_midi_read_data(ep, cable, &buf[i + 1], length);
-               }
-}
-
-static void
-f_midi_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       struct f_midi *midi = ep->driver_data;
-       struct usb_composite_dev *cdev = midi->func.config->cdev;
-       int status = req->status;
-
-       switch (status) {
-       case 0:                  /* normal completion */
-               if (ep == midi->out_ep) {
-                       /* We received stuff. req is queued again, below */
-                       f_midi_handle_out_data(ep, req);
-               } else if (ep == midi->in_ep) {
-                       /* Our transmit completed. See if there's more to go.
-                        * f_midi_transmit eats req, don't queue it again. */
-                       f_midi_transmit(midi, req);
-                       return;
-               }
-               break;
-
-       /* this endpoint is normally active while we're configured */
-       case -ECONNABORTED:     /* hardware forced ep reset */
-       case -ECONNRESET:       /* request dequeued */
-       case -ESHUTDOWN:        /* disconnect from host */
-               VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status,
-                               req->actual, req->length);
-               if (ep == midi->out_ep)
-                       f_midi_handle_out_data(ep, req);
-
-               free_ep_req(ep, req);
-               return;
-
-       case -EOVERFLOW:        /* buffer overrun on read means that
-                                * we didn't provide a big enough buffer.
-                                */
-       default:
-               DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name,
-                               status, req->actual, req->length);
-               break;
-       case -EREMOTEIO:        /* short read */
-               break;
-       }
-
-       status = usb_ep_queue(ep, req, GFP_ATOMIC);
-       if (status) {
-               ERROR(cdev, "kill %s:  resubmit %d bytes --> %d\n",
-                               ep->name, req->length, status);
-               usb_ep_set_halt(ep);
-               /* FIXME recover later ... somehow */
-       }
-}
-
-static int f_midi_start_ep(struct f_midi *midi,
-                          struct usb_function *f,
-                          struct usb_ep *ep)
-{
-       int err;
-       struct usb_composite_dev *cdev = f->config->cdev;
-
-       if (ep->driver_data)
-               usb_ep_disable(ep);
-
-       err = config_ep_by_speed(midi->gadget, f, ep);
-       if (err) {
-               ERROR(cdev, "can't configure %s: %d\n", ep->name, err);
-               return err;
-       }
-
-       err = usb_ep_enable(ep);
-       if (err) {
-               ERROR(cdev, "can't start %s: %d\n", ep->name, err);
-               return err;
-       }
-
-       ep->driver_data = midi;
-
-       return 0;
-}
-
-static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
-{
-       struct f_midi *midi = func_to_midi(f);
-       struct usb_composite_dev *cdev = f->config->cdev;
-       unsigned i;
-       int err;
-
-       err = f_midi_start_ep(midi, f, midi->in_ep);
-       if (err)
-               return err;
-
-       err = f_midi_start_ep(midi, f, midi->out_ep);
-       if (err)
-               return err;
-
-       if (midi->out_ep->driver_data)
-               usb_ep_disable(midi->out_ep);
-
-       err = config_ep_by_speed(midi->gadget, f, midi->out_ep);
-       if (err) {
-               ERROR(cdev, "can't configure %s: %d\n",
-                     midi->out_ep->name, err);
-               return err;
-       }
-
-       err = usb_ep_enable(midi->out_ep);
-       if (err) {
-               ERROR(cdev, "can't start %s: %d\n",
-                     midi->out_ep->name, err);
-               return err;
-       }
-
-       midi->out_ep->driver_data = midi;
-
-       /* allocate a bunch of read buffers and queue them all at once. */
-       for (i = 0; i < midi->qlen && err == 0; i++) {
-               struct usb_request *req =
-                       midi_alloc_ep_req(midi->out_ep, midi->buflen);
-               if (req == NULL)
-                       return -ENOMEM;
-
-               req->complete = f_midi_complete;
-               err = usb_ep_queue(midi->out_ep, req, GFP_ATOMIC);
-               if (err) {
-                       ERROR(midi, "%s queue req: %d\n",
-                                   midi->out_ep->name, err);
-               }
-       }
-
-       return 0;
-}
-
-static void f_midi_disable(struct usb_function *f)
-{
-       struct f_midi *midi = func_to_midi(f);
-       struct usb_composite_dev *cdev = f->config->cdev;
-
-       DBG(cdev, "disable\n");
-
-       /*
-        * just disable endpoints, forcing completion of pending i/o.
-        * all our completion handlers free their requests in this case.
-        */
-       usb_ep_disable(midi->in_ep);
-       usb_ep_disable(midi->out_ep);
-}
-
-static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f)
-{
-       struct usb_composite_dev *cdev = f->config->cdev;
-       struct f_midi *midi = func_to_midi(f);
-       struct snd_card *card;
-
-       DBG(cdev, "unbind\n");
-
-       /* just to be sure */
-       f_midi_disable(f);
-
-       card = midi->card;
-       midi->card = NULL;
-       if (card)
-               snd_card_free(card);
-
-       kfree(midi->id);
-       midi->id = NULL;
-
-       usb_free_all_descriptors(f);
-       kfree(midi);
-}
-
-static int f_midi_snd_free(struct snd_device *device)
-{
-       return 0;
-}
-
-static void f_midi_transmit_packet(struct usb_request *req, uint8_t p0,
-                                       uint8_t p1, uint8_t p2, uint8_t p3)
-{
-       unsigned length = req->length;
-       u8 *buf = (u8 *)req->buf + length;
-
-       buf[0] = p0;
-       buf[1] = p1;
-       buf[2] = p2;
-       buf[3] = p3;
-       req->length = length + 4;
-}
-
-/*
- * Converts MIDI commands to USB MIDI packets.
- */
-static void f_midi_transmit_byte(struct usb_request *req,
-                                struct gmidi_in_port *port, uint8_t b)
-{
-       uint8_t p0 = port->cable << 4;
-
-       if (b >= 0xf8) {
-               f_midi_transmit_packet(req, p0 | 0x0f, b, 0, 0);
-       } else if (b >= 0xf0) {
-               switch (b) {
-               case 0xf0:
-                       port->data[0] = b;
-                       port->state = STATE_SYSEX_1;
-                       break;
-               case 0xf1:
-               case 0xf3:
-                       port->data[0] = b;
-                       port->state = STATE_1PARAM;
-                       break;
-               case 0xf2:
-                       port->data[0] = b;
-                       port->state = STATE_2PARAM_1;
-                       break;
-               case 0xf4:
-               case 0xf5:
-                       port->state = STATE_UNKNOWN;
-                       break;
-               case 0xf6:
-                       f_midi_transmit_packet(req, p0 | 0x05, 0xf6, 0, 0);
-                       port->state = STATE_UNKNOWN;
-                       break;
-               case 0xf7:
-                       switch (port->state) {
-                       case STATE_SYSEX_0:
-                               f_midi_transmit_packet(req,
-                                       p0 | 0x05, 0xf7, 0, 0);
-                               break;
-                       case STATE_SYSEX_1:
-                               f_midi_transmit_packet(req,
-                                       p0 | 0x06, port->data[0], 0xf7, 0);
-                               break;
-                       case STATE_SYSEX_2:
-                               f_midi_transmit_packet(req,
-                                       p0 | 0x07, port->data[0],
-                                       port->data[1], 0xf7);
-                               break;
-                       }
-                       port->state = STATE_UNKNOWN;
-                       break;
-               }
-       } else if (b >= 0x80) {
-               port->data[0] = b;
-               if (b >= 0xc0 && b <= 0xdf)
-                       port->state = STATE_1PARAM;
-               else
-                       port->state = STATE_2PARAM_1;
-       } else { /* b < 0x80 */
-               switch (port->state) {
-               case STATE_1PARAM:
-                       if (port->data[0] < 0xf0) {
-                               p0 |= port->data[0] >> 4;
-                       } else {
-                               p0 |= 0x02;
-                               port->state = STATE_UNKNOWN;
-                       }
-                       f_midi_transmit_packet(req, p0, port->data[0], b, 0);
-                       break;
-               case STATE_2PARAM_1:
-                       port->data[1] = b;
-                       port->state = STATE_2PARAM_2;
-                       break;
-               case STATE_2PARAM_2:
-                       if (port->data[0] < 0xf0) {
-                               p0 |= port->data[0] >> 4;
-                               port->state = STATE_2PARAM_1;
-                       } else {
-                               p0 |= 0x03;
-                               port->state = STATE_UNKNOWN;
-                       }
-                       f_midi_transmit_packet(req,
-                               p0, port->data[0], port->data[1], b);
-                       break;
-               case STATE_SYSEX_0:
-                       port->data[0] = b;
-                       port->state = STATE_SYSEX_1;
-                       break;
-               case STATE_SYSEX_1:
-                       port->data[1] = b;
-                       port->state = STATE_SYSEX_2;
-                       break;
-               case STATE_SYSEX_2:
-                       f_midi_transmit_packet(req,
-                               p0 | 0x04, port->data[0], port->data[1], b);
-                       port->state = STATE_SYSEX_0;
-                       break;
-               }
-       }
-}
-
-static void f_midi_transmit(struct f_midi *midi, struct usb_request *req)
-{
-       struct usb_ep *ep = midi->in_ep;
-       int i;
-
-       if (!ep)
-               return;
-
-       if (!req)
-               req = midi_alloc_ep_req(ep, midi->buflen);
-
-       if (!req) {
-               ERROR(midi, "gmidi_transmit: alloc_ep_request failed\n");
-               return;
-       }
-       req->length = 0;
-       req->complete = f_midi_complete;
-
-       for (i = 0; i < MAX_PORTS; i++) {
-               struct gmidi_in_port *port = midi->in_port[i];
-               struct snd_rawmidi_substream *substream = midi->in_substream[i];
-
-               if (!port || !port->active || !substream)
-                       continue;
-
-               while (req->length + 3 < midi->buflen) {
-                       uint8_t b;
-                       if (snd_rawmidi_transmit(substream, &b, 1) != 1) {
-                               port->active = 0;
-                               break;
-                       }
-                       f_midi_transmit_byte(req, port, b);
-               }
-       }
-
-       if (req->length > 0)
-               usb_ep_queue(ep, req, GFP_ATOMIC);
-       else
-               free_ep_req(ep, req);
-}
-
-static void f_midi_in_tasklet(unsigned long data)
-{
-       struct f_midi *midi = (struct f_midi *) data;
-       f_midi_transmit(midi, NULL);
-}
-
-static int f_midi_in_open(struct snd_rawmidi_substream *substream)
-{
-       struct f_midi *midi = substream->rmidi->private_data;
-
-       if (!midi->in_port[substream->number])
-               return -EINVAL;
-
-       VDBG(midi, "%s()\n", __func__);
-       midi->in_substream[substream->number] = substream;
-       midi->in_port[substream->number]->state = STATE_UNKNOWN;
-       return 0;
-}
-
-static int f_midi_in_close(struct snd_rawmidi_substream *substream)
-{
-       struct f_midi *midi = substream->rmidi->private_data;
-
-       VDBG(midi, "%s()\n", __func__);
-       return 0;
-}
-
-static void f_midi_in_trigger(struct snd_rawmidi_substream *substream, int up)
-{
-       struct f_midi *midi = substream->rmidi->private_data;
-
-       if (!midi->in_port[substream->number])
-               return;
-
-       VDBG(midi, "%s() %d\n", __func__, up);
-       midi->in_port[substream->number]->active = up;
-       if (up)
-               tasklet_hi_schedule(&midi->tasklet);
-}
-
-static int f_midi_out_open(struct snd_rawmidi_substream *substream)
-{
-       struct f_midi *midi = substream->rmidi->private_data;
-
-       if (substream->number >= MAX_PORTS)
-               return -EINVAL;
-
-       VDBG(midi, "%s()\n", __func__);
-       midi->out_substream[substream->number] = substream;
-       return 0;
-}
-
-static int f_midi_out_close(struct snd_rawmidi_substream *substream)
-{
-       struct f_midi *midi = substream->rmidi->private_data;
-
-       VDBG(midi, "%s()\n", __func__);
-       return 0;
-}
-
-static void f_midi_out_trigger(struct snd_rawmidi_substream *substream, int up)
-{
-       struct f_midi *midi = substream->rmidi->private_data;
-
-       VDBG(midi, "%s()\n", __func__);
-
-       if (up)
-               set_bit(substream->number, &midi->out_triggered);
-       else
-               clear_bit(substream->number, &midi->out_triggered);
-}
-
-static struct snd_rawmidi_ops gmidi_in_ops = {
-       .open = f_midi_in_open,
-       .close = f_midi_in_close,
-       .trigger = f_midi_in_trigger,
-};
-
-static struct snd_rawmidi_ops gmidi_out_ops = {
-       .open = f_midi_out_open,
-       .close = f_midi_out_close,
-       .trigger = f_midi_out_trigger
-};
-
-/* register as a sound "card" */
-static int f_midi_register_card(struct f_midi *midi)
-{
-       struct snd_card *card;
-       struct snd_rawmidi *rmidi;
-       int err;
-       static struct snd_device_ops ops = {
-               .dev_free = f_midi_snd_free,
-       };
-
-       err = snd_card_new(&midi->gadget->dev, midi->index, midi->id,
-                          THIS_MODULE, 0, &card);
-       if (err < 0) {
-               ERROR(midi, "snd_card_new() failed\n");
-               goto fail;
-       }
-       midi->card = card;
-
-       err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, midi, &ops);
-       if (err < 0) {
-               ERROR(midi, "snd_device_new() failed: error %d\n", err);
-               goto fail;
-       }
-
-       strcpy(card->driver, f_midi_longname);
-       strcpy(card->longname, f_midi_longname);
-       strcpy(card->shortname, f_midi_shortname);
-
-       /* Set up rawmidi */
-       snd_component_add(card, "MIDI");
-       err = snd_rawmidi_new(card, card->longname, 0,
-                             midi->out_ports, midi->in_ports, &rmidi);
-       if (err < 0) {
-               ERROR(midi, "snd_rawmidi_new() failed: error %d\n", err);
-               goto fail;
-       }
-       midi->rmidi = rmidi;
-       strcpy(rmidi->name, card->shortname);
-       rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
-                           SNDRV_RAWMIDI_INFO_INPUT |
-                           SNDRV_RAWMIDI_INFO_DUPLEX;
-       rmidi->private_data = midi;
-
-       /*
-        * Yes, rawmidi OUTPUT = USB IN, and rawmidi INPUT = USB OUT.
-        * It's an upside-down world being a gadget.
-        */
-       snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &gmidi_in_ops);
-       snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &gmidi_out_ops);
-
-       /* register it - we're ready to go */
-       err = snd_card_register(card);
-       if (err < 0) {
-               ERROR(midi, "snd_card_register() failed\n");
-               goto fail;
-       }
-
-       VDBG(midi, "%s() finished ok\n", __func__);
-       return 0;
-
-fail:
-       if (midi->card) {
-               snd_card_free(midi->card);
-               midi->card = NULL;
-       }
-       return err;
-}
-
-/* MIDI function driver setup/binding */
-
-static int __init
-f_midi_bind(struct usb_configuration *c, struct usb_function *f)
-{
-       struct usb_descriptor_header **midi_function;
-       struct usb_midi_in_jack_descriptor jack_in_ext_desc[MAX_PORTS];
-       struct usb_midi_in_jack_descriptor jack_in_emb_desc[MAX_PORTS];
-       struct usb_midi_out_jack_descriptor_1 jack_out_ext_desc[MAX_PORTS];
-       struct usb_midi_out_jack_descriptor_1 jack_out_emb_desc[MAX_PORTS];
-       struct usb_composite_dev *cdev = c->cdev;
-       struct f_midi *midi = func_to_midi(f);
-       int status, n, jack = 1, i = 0;
-
-       /* maybe allocate device-global string ID */
-       if (midi_string_defs[0].id == 0) {
-               status = usb_string_id(c->cdev);
-               if (status < 0)
-                       goto fail;
-               midi_string_defs[0].id = status;
-       }
-
-       /* We have two interfaces, AudioControl and MIDIStreaming */
-       status = usb_interface_id(c, f);
-       if (status < 0)
-               goto fail;
-       ac_interface_desc.bInterfaceNumber = status;
-
-       status = usb_interface_id(c, f);
-       if (status < 0)
-               goto fail;
-       ms_interface_desc.bInterfaceNumber = status;
-       ac_header_desc.baInterfaceNr[0] = status;
-
-       status = -ENODEV;
-
-       /* allocate instance-specific endpoints */
-       midi->in_ep = usb_ep_autoconfig(cdev->gadget, &bulk_in_desc);
-       if (!midi->in_ep)
-               goto fail;
-       midi->in_ep->driver_data = cdev;        /* claim */
-
-       midi->out_ep = usb_ep_autoconfig(cdev->gadget, &bulk_out_desc);
-       if (!midi->out_ep)
-               goto fail;
-       midi->out_ep->driver_data = cdev;       /* claim */
-
-       /* allocate temporary function list */
-       midi_function = kcalloc((MAX_PORTS * 4) + 9, sizeof(*midi_function),
-                               GFP_KERNEL);
-       if (!midi_function) {
-               status = -ENOMEM;
-               goto fail;
-       }
-
-       /*
-        * construct the function's descriptor set. As the number of
-        * input and output MIDI ports is configurable, we have to do
-        * it that way.
-        */
-
-       /* add the headers - these are always the same */
-       midi_function[i++] = (struct usb_descriptor_header *) &ac_interface_desc;
-       midi_function[i++] = (struct usb_descriptor_header *) &ac_header_desc;
-       midi_function[i++] = (struct usb_descriptor_header *) &ms_interface_desc;
-
-       /* calculate the header's wTotalLength */
-       n = USB_DT_MS_HEADER_SIZE
-               + (midi->in_ports + midi->out_ports) *
-                       (USB_DT_MIDI_IN_SIZE + USB_DT_MIDI_OUT_SIZE(1));
-       ms_header_desc.wTotalLength = cpu_to_le16(n);
-
-       midi_function[i++] = (struct usb_descriptor_header *) &ms_header_desc;
-
-       /* configure the external IN jacks, each linked to an embedded OUT jack */
-       for (n = 0; n < midi->in_ports; n++) {
-               struct usb_midi_in_jack_descriptor *in_ext = &jack_in_ext_desc[n];
-               struct usb_midi_out_jack_descriptor_1 *out_emb = &jack_out_emb_desc[n];
-
-               in_ext->bLength                 = USB_DT_MIDI_IN_SIZE;
-               in_ext->bDescriptorType         = USB_DT_CS_INTERFACE;
-               in_ext->bDescriptorSubtype      = USB_MS_MIDI_IN_JACK;
-               in_ext->bJackType               = USB_MS_EXTERNAL;
-               in_ext->bJackID                 = jack++;
-               in_ext->iJack                   = 0;
-               midi_function[i++] = (struct usb_descriptor_header *) in_ext;
-
-               out_emb->bLength                = USB_DT_MIDI_OUT_SIZE(1);
-               out_emb->bDescriptorType        = USB_DT_CS_INTERFACE;
-               out_emb->bDescriptorSubtype     = USB_MS_MIDI_OUT_JACK;
-               out_emb->bJackType              = USB_MS_EMBEDDED;
-               out_emb->bJackID                = jack++;
-               out_emb->bNrInputPins           = 1;
-               out_emb->pins[0].baSourcePin    = 1;
-               out_emb->pins[0].baSourceID     = in_ext->bJackID;
-               out_emb->iJack                  = 0;
-               midi_function[i++] = (struct usb_descriptor_header *) out_emb;
-
-               /* link it to the endpoint */
-               ms_in_desc.baAssocJackID[n] = out_emb->bJackID;
-       }
-
-       /* configure the external OUT jacks, each linked to an embedded IN jack */
-       for (n = 0; n < midi->out_ports; n++) {
-               struct usb_midi_in_jack_descriptor *in_emb = &jack_in_emb_desc[n];
-               struct usb_midi_out_jack_descriptor_1 *out_ext = &jack_out_ext_desc[n];
-
-               in_emb->bLength                 = USB_DT_MIDI_IN_SIZE;
-               in_emb->bDescriptorType         = USB_DT_CS_INTERFACE;
-               in_emb->bDescriptorSubtype      = USB_MS_MIDI_IN_JACK;
-               in_emb->bJackType               = USB_MS_EMBEDDED;
-               in_emb->bJackID                 = jack++;
-               in_emb->iJack                   = 0;
-               midi_function[i++] = (struct usb_descriptor_header *) in_emb;
-
-               out_ext->bLength =              USB_DT_MIDI_OUT_SIZE(1);
-               out_ext->bDescriptorType =      USB_DT_CS_INTERFACE;
-               out_ext->bDescriptorSubtype =   USB_MS_MIDI_OUT_JACK;
-               out_ext->bJackType =            USB_MS_EXTERNAL;
-               out_ext->bJackID =              jack++;
-               out_ext->bNrInputPins =         1;
-               out_ext->iJack =                0;
-               out_ext->pins[0].baSourceID =   in_emb->bJackID;
-               out_ext->pins[0].baSourcePin =  1;
-               midi_function[i++] = (struct usb_descriptor_header *) out_ext;
-
-               /* link it to the endpoint */
-               ms_out_desc.baAssocJackID[n] = in_emb->bJackID;
-       }
-
-       /* configure the endpoint descriptors ... */
-       ms_out_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->in_ports);
-       ms_out_desc.bNumEmbMIDIJack = midi->in_ports;
-
-       ms_in_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->out_ports);
-       ms_in_desc.bNumEmbMIDIJack = midi->out_ports;
-
-       /* ... and add them to the list */
-       midi_function[i++] = (struct usb_descriptor_header *) &bulk_out_desc;
-       midi_function[i++] = (struct usb_descriptor_header *) &ms_out_desc;
-       midi_function[i++] = (struct usb_descriptor_header *) &bulk_in_desc;
-       midi_function[i++] = (struct usb_descriptor_header *) &ms_in_desc;
-       midi_function[i++] = NULL;
-
-       /*
-        * support all relevant hardware speeds... we expect that when
-        * hardware is dual speed, all bulk-capable endpoints work at
-        * both speeds
-        */
-       /* copy descriptors, and track endpoint copies */
-       f->fs_descriptors = usb_copy_descriptors(midi_function);
-       if (!f->fs_descriptors)
-               goto fail_f_midi;
-
-       if (gadget_is_dualspeed(c->cdev->gadget)) {
-               bulk_in_desc.wMaxPacketSize = cpu_to_le16(512);
-               bulk_out_desc.wMaxPacketSize = cpu_to_le16(512);
-               f->hs_descriptors = usb_copy_descriptors(midi_function);
-               if (!f->hs_descriptors)
-                       goto fail_f_midi;
-       }
-
-       kfree(midi_function);
-
-       return 0;
-
-fail_f_midi:
-       kfree(midi_function);
-       usb_free_descriptors(f->hs_descriptors);
-fail:
-       /* we might as well release our claims on endpoints */
-       if (midi->out_ep)
-               midi->out_ep->driver_data = NULL;
-       if (midi->in_ep)
-               midi->in_ep->driver_data = NULL;
-
-       ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
-
-       return status;
-}
-
-/**
- * f_midi_bind_config - add USB MIDI function to a configuration
- * @c: the configuration to supcard the USB audio function
- * @index: the soundcard index to use for the ALSA device creation
- * @id: the soundcard id to use for the ALSA device creation
- * @buflen: the buffer length to use
- * @qlen the number of read requests to pre-allocate
- * Context: single threaded during gadget setup
- *
- * Returns zero on success, else negative errno.
- */
-int __init f_midi_bind_config(struct usb_configuration *c,
-                             int index, char *id,
-                             unsigned int in_ports,
-                             unsigned int out_ports,
-                             unsigned int buflen,
-                             unsigned int qlen)
-{
-       struct f_midi *midi;
-       int status, i;
-
-       /* sanity check */
-       if (in_ports > MAX_PORTS || out_ports > MAX_PORTS)
-               return -EINVAL;
-
-       /* allocate and initialize one new instance */
-       midi = kzalloc(sizeof *midi, GFP_KERNEL);
-       if (!midi) {
-               status = -ENOMEM;
-               goto fail;
-       }
-
-       for (i = 0; i < in_ports; i++) {
-               struct gmidi_in_port *port = kzalloc(sizeof(*port), GFP_KERNEL);
-               if (!port) {
-                       status = -ENOMEM;
-                       goto setup_fail;
-               }
-
-               port->midi = midi;
-               port->active = 0;
-               port->cable = i;
-               midi->in_port[i] = port;
-       }
-
-       midi->gadget = c->cdev->gadget;
-       tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi);
-
-       /* set up ALSA midi devices */
-       midi->in_ports = in_ports;
-       midi->out_ports = out_ports;
-       status = f_midi_register_card(midi);
-       if (status < 0)
-               goto setup_fail;
-
-       midi->func.name        = "gmidi function";
-       midi->func.strings     = midi_strings;
-       midi->func.bind        = f_midi_bind;
-       midi->func.unbind      = f_midi_unbind;
-       midi->func.set_alt     = f_midi_set_alt;
-       midi->func.disable     = f_midi_disable;
-
-       midi->id = kstrdup(id, GFP_KERNEL);
-       midi->index = index;
-       midi->buflen = buflen;
-       midi->qlen = qlen;
-
-       status = usb_add_function(c, &midi->func);
-       if (status)
-               goto setup_fail;
-
-       return 0;
-
-setup_fail:
-       for (--i; i >= 0; i--)
-               kfree(midi->in_port[i]);
-       kfree(midi);
-fail:
-       return status;
-}
-
diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/f_ncm.c
deleted file mode 100644 (file)
index bcdc882..0000000
+++ /dev/null
@@ -1,1622 +0,0 @@
-/*
- * f_ncm.c -- USB CDC Network (NCM) link function driver
- *
- * Copyright (C) 2010 Nokia Corporation
- * Contact: Yauheni Kaliuta <yauheni.kaliuta@nokia.com>
- *
- * The driver borrows from f_ecm.c which is:
- *
- * Copyright (C) 2003-2005,2008 David Brownell
- * Copyright (C) 2008 Nokia Corporation
- *
- * 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.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/etherdevice.h>
-#include <linux/crc32.h>
-
-#include <linux/usb/cdc.h>
-
-#include "u_ether.h"
-#include "u_ether_configfs.h"
-#include "u_ncm.h"
-
-/*
- * This function is a "CDC Network Control Model" (CDC NCM) Ethernet link.
- * NCM is intended to be used with high-speed network attachments.
- *
- * Note that NCM requires the use of "alternate settings" for its data
- * interface.  This means that the set_alt() method has real work to do,
- * and also means that a get_alt() method is required.
- */
-
-/* to trigger crc/non-crc ndp signature */
-
-#define NCM_NDP_HDR_CRC_MASK   0x01000000
-#define NCM_NDP_HDR_CRC                0x01000000
-#define NCM_NDP_HDR_NOCRC      0x00000000
-
-enum ncm_notify_state {
-       NCM_NOTIFY_NONE,                /* don't notify */
-       NCM_NOTIFY_CONNECT,             /* issue CONNECT next */
-       NCM_NOTIFY_SPEED,               /* issue SPEED_CHANGE next */
-};
-
-struct f_ncm {
-       struct gether                   port;
-       u8                              ctrl_id, data_id;
-
-       char                            ethaddr[14];
-
-       struct usb_ep                   *notify;
-       struct usb_request              *notify_req;
-       u8                              notify_state;
-       bool                            is_open;
-
-       const struct ndp_parser_opts    *parser_opts;
-       bool                            is_crc;
-       u32                             ndp_sign;
-
-       /*
-        * for notification, it is accessed from both
-        * callback and ethernet open/close
-        */
-       spinlock_t                      lock;
-
-       struct net_device               *netdev;
-
-       /* For multi-frame NDP TX */
-       struct sk_buff                  *skb_tx_data;
-       struct sk_buff                  *skb_tx_ndp;
-       u16                             ndp_dgram_count;
-       bool                            timer_force_tx;
-       struct tasklet_struct           tx_tasklet;
-       struct hrtimer                  task_timer;
-
-       bool                            timer_stopping;
-};
-
-static inline struct f_ncm *func_to_ncm(struct usb_function *f)
-{
-       return container_of(f, struct f_ncm, port.func);
-}
-
-/* peak (theoretical) bulk transfer rate in bits-per-second */
-static inline unsigned ncm_bitrate(struct usb_gadget *g)
-{
-       if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
-               return 13 * 512 * 8 * 1000 * 8;
-       else
-               return 19 *  64 * 1 * 1000 * 8;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * We cannot group frames so use just the minimal size which ok to put
- * one max-size ethernet frame.
- * If the host can group frames, allow it to do that, 16K is selected,
- * because it's used by default by the current linux host driver
- */
-#define NTB_DEFAULT_IN_SIZE    16384
-#define NTB_OUT_SIZE           16384
-
-/* Allocation for storing the NDP, 32 should suffice for a
- * 16k packet. This allows a maximum of 32 * 507 Byte packets to
- * be transmitted in a single 16kB skb, though when sending full size
- * packets this limit will be plenty.
- * Smaller packets are not likely to be trying to maximize the
- * throughput and will be mstly sending smaller infrequent frames.
- */
-#define TX_MAX_NUM_DPE         32
-
-/* Delay for the transmit to wait before sending an unfilled NTB frame. */
-#define TX_TIMEOUT_NSECS       300000
-
-#define FORMATS_SUPPORTED      (USB_CDC_NCM_NTB16_SUPPORTED |  \
-                                USB_CDC_NCM_NTB32_SUPPORTED)
-
-static struct usb_cdc_ncm_ntb_parameters ntb_parameters = {
-       .wLength = cpu_to_le16(sizeof(ntb_parameters)),
-       .bmNtbFormatsSupported = cpu_to_le16(FORMATS_SUPPORTED),
-       .dwNtbInMaxSize = cpu_to_le32(NTB_DEFAULT_IN_SIZE),
-       .wNdpInDivisor = cpu_to_le16(4),
-       .wNdpInPayloadRemainder = cpu_to_le16(0),
-       .wNdpInAlignment = cpu_to_le16(4),
-
-       .dwNtbOutMaxSize = cpu_to_le32(NTB_OUT_SIZE),
-       .wNdpOutDivisor = cpu_to_le16(4),
-       .wNdpOutPayloadRemainder = cpu_to_le16(0),
-       .wNdpOutAlignment = cpu_to_le16(4),
-};
-
-/*
- * Use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one
- * packet, to simplify cancellation; and a big transfer interval, to
- * waste less bandwidth.
- */
-
-#define NCM_STATUS_INTERVAL_MS         32
-#define NCM_STATUS_BYTECOUNT           16      /* 8 byte header + data */
-
-static struct usb_interface_assoc_descriptor ncm_iad_desc = {
-       .bLength =              sizeof ncm_iad_desc,
-       .bDescriptorType =      USB_DT_INTERFACE_ASSOCIATION,
-
-       /* .bFirstInterface =   DYNAMIC, */
-       .bInterfaceCount =      2,      /* control + data */
-       .bFunctionClass =       USB_CLASS_COMM,
-       .bFunctionSubClass =    USB_CDC_SUBCLASS_NCM,
-       .bFunctionProtocol =    USB_CDC_PROTO_NONE,
-       /* .iFunction =         DYNAMIC */
-};
-
-/* interface descriptor: */
-
-static struct usb_interface_descriptor ncm_control_intf = {
-       .bLength =              sizeof ncm_control_intf,
-       .bDescriptorType =      USB_DT_INTERFACE,
-
-       /* .bInterfaceNumber = DYNAMIC */
-       .bNumEndpoints =        1,
-       .bInterfaceClass =      USB_CLASS_COMM,
-       .bInterfaceSubClass =   USB_CDC_SUBCLASS_NCM,
-       .bInterfaceProtocol =   USB_CDC_PROTO_NONE,
-       /* .iInterface = DYNAMIC */
-};
-
-static struct usb_cdc_header_desc ncm_header_desc = {
-       .bLength =              sizeof ncm_header_desc,
-       .bDescriptorType =      USB_DT_CS_INTERFACE,
-       .bDescriptorSubType =   USB_CDC_HEADER_TYPE,
-
-       .bcdCDC =               cpu_to_le16(0x0110),
-};
-
-static struct usb_cdc_union_desc ncm_union_desc = {
-       .bLength =              sizeof(ncm_union_desc),
-       .bDescriptorType =      USB_DT_CS_INTERFACE,
-       .bDescriptorSubType =   USB_CDC_UNION_TYPE,
-       /* .bMasterInterface0 = DYNAMIC */
-       /* .bSlaveInterface0 =  DYNAMIC */
-};
-
-static struct usb_cdc_ether_desc ecm_desc = {
-       .bLength =              sizeof ecm_desc,
-       .bDescriptorType =      USB_DT_CS_INTERFACE,
-       .bDescriptorSubType =   USB_CDC_ETHERNET_TYPE,
-
-       /* this descriptor actually adds value, surprise! */
-       /* .iMACAddress = DYNAMIC */
-       .bmEthernetStatistics = cpu_to_le32(0), /* no statistics */
-       .wMaxSegmentSize =      cpu_to_le16(ETH_FRAME_LEN),
-       .wNumberMCFilters =     cpu_to_le16(0),
-       .bNumberPowerFilters =  0,
-};
-
-#define NCAPS  (USB_CDC_NCM_NCAP_ETH_FILTER | USB_CDC_NCM_NCAP_CRC_MODE)
-
-static struct usb_cdc_ncm_desc ncm_desc = {
-       .bLength =              sizeof ncm_desc,
-       .bDescriptorType =      USB_DT_CS_INTERFACE,
-       .bDescriptorSubType =   USB_CDC_NCM_TYPE,
-
-       .bcdNcmVersion =        cpu_to_le16(0x0100),
-       /* can process SetEthernetPacketFilter */
-       .bmNetworkCapabilities = NCAPS,
-};
-
-/* the default data interface has no endpoints ... */
-
-static struct usb_interface_descriptor ncm_data_nop_intf = {
-       .bLength =              sizeof ncm_data_nop_intf,
-       .bDescriptorType =      USB_DT_INTERFACE,
-
-       .bInterfaceNumber =     1,
-       .bAlternateSetting =    0,
-       .bNumEndpoints =        0,
-       .bInterfaceClass =      USB_CLASS_CDC_DATA,
-       .bInterfaceSubClass =   0,
-       .bInterfaceProtocol =   USB_CDC_NCM_PROTO_NTB,
-       /* .iInterface = DYNAMIC */
-};
-
-/* ... but the "real" data interface has two bulk endpoints */
-
-static struct usb_interface_descriptor ncm_data_intf = {
-       .bLength =              sizeof ncm_data_intf,
-       .bDescriptorType =      USB_DT_INTERFACE,
-
-       .bInterfaceNumber =     1,
-       .bAlternateSetting =    1,
-       .bNumEndpoints =        2,
-       .bInterfaceClass =      USB_CLASS_CDC_DATA,
-       .bInterfaceSubClass =   0,
-       .bInterfaceProtocol =   USB_CDC_NCM_PROTO_NTB,
-       /* .iInterface = DYNAMIC */
-};
-
-/* full speed support: */
-
-static struct usb_endpoint_descriptor fs_ncm_notify_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_INT,
-       .wMaxPacketSize =       cpu_to_le16(NCM_STATUS_BYTECOUNT),
-       .bInterval =            NCM_STATUS_INTERVAL_MS,
-};
-
-static struct usb_endpoint_descriptor fs_ncm_in_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-};
-
-static struct usb_endpoint_descriptor fs_ncm_out_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_OUT,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-};
-
-static struct usb_descriptor_header *ncm_fs_function[] = {
-       (struct usb_descriptor_header *) &ncm_iad_desc,
-       /* CDC NCM control descriptors */
-       (struct usb_descriptor_header *) &ncm_control_intf,
-       (struct usb_descriptor_header *) &ncm_header_desc,
-       (struct usb_descriptor_header *) &ncm_union_desc,
-       (struct usb_descriptor_header *) &ecm_desc,
-       (struct usb_descriptor_header *) &ncm_desc,
-       (struct usb_descriptor_header *) &fs_ncm_notify_desc,
-       /* data interface, altsettings 0 and 1 */
-       (struct usb_descriptor_header *) &ncm_data_nop_intf,
-       (struct usb_descriptor_header *) &ncm_data_intf,
-       (struct usb_descriptor_header *) &fs_ncm_in_desc,
-       (struct usb_descriptor_header *) &fs_ncm_out_desc,
-       NULL,
-};
-
-/* high speed support: */
-
-static struct usb_endpoint_descriptor hs_ncm_notify_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_INT,
-       .wMaxPacketSize =       cpu_to_le16(NCM_STATUS_BYTECOUNT),
-       .bInterval =            USB_MS_TO_HS_INTERVAL(NCM_STATUS_INTERVAL_MS),
-};
-static struct usb_endpoint_descriptor hs_ncm_in_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(512),
-};
-
-static struct usb_endpoint_descriptor hs_ncm_out_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_OUT,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(512),
-};
-
-static struct usb_descriptor_header *ncm_hs_function[] = {
-       (struct usb_descriptor_header *) &ncm_iad_desc,
-       /* CDC NCM control descriptors */
-       (struct usb_descriptor_header *) &ncm_control_intf,
-       (struct usb_descriptor_header *) &ncm_header_desc,
-       (struct usb_descriptor_header *) &ncm_union_desc,
-       (struct usb_descriptor_header *) &ecm_desc,
-       (struct usb_descriptor_header *) &ncm_desc,
-       (struct usb_descriptor_header *) &hs_ncm_notify_desc,
-       /* data interface, altsettings 0 and 1 */
-       (struct usb_descriptor_header *) &ncm_data_nop_intf,
-       (struct usb_descriptor_header *) &ncm_data_intf,
-       (struct usb_descriptor_header *) &hs_ncm_in_desc,
-       (struct usb_descriptor_header *) &hs_ncm_out_desc,
-       NULL,
-};
-
-/* string descriptors: */
-
-#define STRING_CTRL_IDX        0
-#define STRING_MAC_IDX 1
-#define STRING_DATA_IDX        2
-#define STRING_IAD_IDX 3
-
-static struct usb_string ncm_string_defs[] = {
-       [STRING_CTRL_IDX].s = "CDC Network Control Model (NCM)",
-       [STRING_MAC_IDX].s = "",
-       [STRING_DATA_IDX].s = "CDC Network Data",
-       [STRING_IAD_IDX].s = "CDC NCM",
-       {  } /* end of list */
-};
-
-static struct usb_gadget_strings ncm_string_table = {
-       .language =             0x0409, /* en-us */
-       .strings =              ncm_string_defs,
-};
-
-static struct usb_gadget_strings *ncm_strings[] = {
-       &ncm_string_table,
-       NULL,
-};
-
-/*
- * Here are options for NCM Datagram Pointer table (NDP) parser.
- * There are 2 different formats: NDP16 and NDP32 in the spec (ch. 3),
- * in NDP16 offsets and sizes fields are 1 16bit word wide,
- * in NDP32 -- 2 16bit words wide. Also signatures are different.
- * To make the parser code the same, put the differences in the structure,
- * and switch pointers to the structures when the format is changed.
- */
-
-struct ndp_parser_opts {
-       u32             nth_sign;
-       u32             ndp_sign;
-       unsigned        nth_size;
-       unsigned        ndp_size;
-       unsigned        dpe_size;
-       unsigned        ndplen_align;
-       /* sizes in u16 units */
-       unsigned        dgram_item_len; /* index or length */
-       unsigned        block_length;
-       unsigned        ndp_index;
-       unsigned        reserved1;
-       unsigned        reserved2;
-       unsigned        next_ndp_index;
-};
-
-#define INIT_NDP16_OPTS {                                      \
-               .nth_sign = USB_CDC_NCM_NTH16_SIGN,             \
-               .ndp_sign = USB_CDC_NCM_NDP16_NOCRC_SIGN,       \
-               .nth_size = sizeof(struct usb_cdc_ncm_nth16),   \
-               .ndp_size = sizeof(struct usb_cdc_ncm_ndp16),   \
-               .dpe_size = sizeof(struct usb_cdc_ncm_dpe16),   \
-               .ndplen_align = 4,                              \
-               .dgram_item_len = 1,                            \
-               .block_length = 1,                              \
-               .ndp_index = 1,                                 \
-               .reserved1 = 0,                                 \
-               .reserved2 = 0,                                 \
-               .next_ndp_index = 1,                            \
-       }
-
-
-#define INIT_NDP32_OPTS {                                      \
-               .nth_sign = USB_CDC_NCM_NTH32_SIGN,             \
-               .ndp_sign = USB_CDC_NCM_NDP32_NOCRC_SIGN,       \
-               .nth_size = sizeof(struct usb_cdc_ncm_nth32),   \
-               .ndp_size = sizeof(struct usb_cdc_ncm_ndp32),   \
-               .dpe_size = sizeof(struct usb_cdc_ncm_dpe32),   \
-               .ndplen_align = 8,                              \
-               .dgram_item_len = 2,                            \
-               .block_length = 2,                              \
-               .ndp_index = 2,                                 \
-               .reserved1 = 1,                                 \
-               .reserved2 = 2,                                 \
-               .next_ndp_index = 2,                            \
-       }
-
-static const struct ndp_parser_opts ndp16_opts = INIT_NDP16_OPTS;
-static const struct ndp_parser_opts ndp32_opts = INIT_NDP32_OPTS;
-
-static inline void put_ncm(__le16 **p, unsigned size, unsigned val)
-{
-       switch (size) {
-       case 1:
-               put_unaligned_le16((u16)val, *p);
-               break;
-       case 2:
-               put_unaligned_le32((u32)val, *p);
-
-               break;
-       default:
-               BUG();
-       }
-
-       *p += size;
-}
-
-static inline unsigned get_ncm(__le16 **p, unsigned size)
-{
-       unsigned tmp;
-
-       switch (size) {
-       case 1:
-               tmp = get_unaligned_le16(*p);
-               break;
-       case 2:
-               tmp = get_unaligned_le32(*p);
-               break;
-       default:
-               BUG();
-       }
-
-       *p += size;
-       return tmp;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static inline void ncm_reset_values(struct f_ncm *ncm)
-{
-       ncm->parser_opts = &ndp16_opts;
-       ncm->is_crc = false;
-       ncm->port.cdc_filter = DEFAULT_FILTER;
-
-       /* doesn't make sense for ncm, fixed size used */
-       ncm->port.header_len = 0;
-
-       ncm->port.fixed_out_len = le32_to_cpu(ntb_parameters.dwNtbOutMaxSize);
-       ncm->port.fixed_in_len = NTB_DEFAULT_IN_SIZE;
-}
-
-/*
- * Context: ncm->lock held
- */
-static void ncm_do_notify(struct f_ncm *ncm)
-{
-       struct usb_request              *req = ncm->notify_req;
-       struct usb_cdc_notification     *event;
-       struct usb_composite_dev        *cdev = ncm->port.func.config->cdev;
-       __le32                          *data;
-       int                             status;
-
-       /* notification already in flight? */
-       if (!req)
-               return;
-
-       event = req->buf;
-       switch (ncm->notify_state) {
-       case NCM_NOTIFY_NONE:
-               return;
-
-       case NCM_NOTIFY_CONNECT:
-               event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
-               if (ncm->is_open)
-                       event->wValue = cpu_to_le16(1);
-               else
-                       event->wValue = cpu_to_le16(0);
-               event->wLength = 0;
-               req->length = sizeof *event;
-
-               DBG(cdev, "notify connect %s\n",
-                               ncm->is_open ? "true" : "false");
-               ncm->notify_state = NCM_NOTIFY_NONE;
-               break;
-
-       case NCM_NOTIFY_SPEED:
-               event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE;
-               event->wValue = cpu_to_le16(0);
-               event->wLength = cpu_to_le16(8);
-               req->length = NCM_STATUS_BYTECOUNT;
-
-               /* SPEED_CHANGE data is up/down speeds in bits/sec */
-               data = req->buf + sizeof *event;
-               data[0] = cpu_to_le32(ncm_bitrate(cdev->gadget));
-               data[1] = data[0];
-
-               DBG(cdev, "notify speed %d\n", ncm_bitrate(cdev->gadget));
-               ncm->notify_state = NCM_NOTIFY_CONNECT;
-               break;
-       }
-       event->bmRequestType = 0xA1;
-       event->wIndex = cpu_to_le16(ncm->ctrl_id);
-
-       ncm->notify_req = NULL;
-       /*
-        * In double buffering if there is a space in FIFO,
-        * completion callback can be called right after the call,
-        * so unlocking
-        */
-       spin_unlock(&ncm->lock);
-       status = usb_ep_queue(ncm->notify, req, GFP_ATOMIC);
-       spin_lock(&ncm->lock);
-       if (status < 0) {
-               ncm->notify_req = req;
-               DBG(cdev, "notify --> %d\n", status);
-       }
-}
-
-/*
- * Context: ncm->lock held
- */
-static void ncm_notify(struct f_ncm *ncm)
-{
-       /*
-        * NOTE on most versions of Linux, host side cdc-ethernet
-        * won't listen for notifications until its netdevice opens.
-        * The first notification then sits in the FIFO for a long
-        * time, and the second one is queued.
-        *
-        * If ncm_notify() is called before the second (CONNECT)
-        * notification is sent, then it will reset to send the SPEED
-        * notificaion again (and again, and again), but it's not a problem
-        */
-       ncm->notify_state = NCM_NOTIFY_SPEED;
-       ncm_do_notify(ncm);
-}
-
-static void ncm_notify_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       struct f_ncm                    *ncm = req->context;
-       struct usb_composite_dev        *cdev = ncm->port.func.config->cdev;
-       struct usb_cdc_notification     *event = req->buf;
-
-       spin_lock(&ncm->lock);
-       switch (req->status) {
-       case 0:
-               VDBG(cdev, "Notification %02x sent\n",
-                    event->bNotificationType);
-               break;
-       case -ECONNRESET:
-       case -ESHUTDOWN:
-               ncm->notify_state = NCM_NOTIFY_NONE;
-               break;
-       default:
-               DBG(cdev, "event %02x --> %d\n",
-                       event->bNotificationType, req->status);
-               break;
-       }
-       ncm->notify_req = req;
-       ncm_do_notify(ncm);
-       spin_unlock(&ncm->lock);
-}
-
-static void ncm_ep0out_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       /* now for SET_NTB_INPUT_SIZE only */
-       unsigned                in_size;
-       struct usb_function     *f = req->context;
-       struct f_ncm            *ncm = func_to_ncm(f);
-       struct usb_composite_dev *cdev = ep->driver_data;
-
-       req->context = NULL;
-       if (req->status || req->actual != req->length) {
-               DBG(cdev, "Bad control-OUT transfer\n");
-               goto invalid;
-       }
-
-       in_size = get_unaligned_le32(req->buf);
-       if (in_size < USB_CDC_NCM_NTB_MIN_IN_SIZE ||
-           in_size > le32_to_cpu(ntb_parameters.dwNtbInMaxSize)) {
-               DBG(cdev, "Got wrong INPUT SIZE (%d) from host\n", in_size);
-               goto invalid;
-       }
-
-       ncm->port.fixed_in_len = in_size;
-       VDBG(cdev, "Set NTB INPUT SIZE %d\n", in_size);
-       return;
-
-invalid:
-       usb_ep_set_halt(ep);
-       return;
-}
-
-static int ncm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
-{
-       struct f_ncm            *ncm = func_to_ncm(f);
-       struct usb_composite_dev *cdev = f->config->cdev;
-       struct usb_request      *req = cdev->req;
-       int                     value = -EOPNOTSUPP;
-       u16                     w_index = le16_to_cpu(ctrl->wIndex);
-       u16                     w_value = le16_to_cpu(ctrl->wValue);
-       u16                     w_length = le16_to_cpu(ctrl->wLength);
-
-       /*
-        * composite driver infrastructure handles everything except
-        * CDC class messages; interface activation uses set_alt().
-        */
-       switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
-       case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
-                       | USB_CDC_SET_ETHERNET_PACKET_FILTER:
-               /*
-                * see 6.2.30: no data, wIndex = interface,
-                * wValue = packet filter bitmap
-                */
-               if (w_length != 0 || w_index != ncm->ctrl_id)
-                       goto invalid;
-               DBG(cdev, "packet filter %02x\n", w_value);
-               /*
-                * REVISIT locking of cdc_filter.  This assumes the UDC
-                * driver won't have a concurrent packet TX irq running on
-                * another CPU; or that if it does, this write is atomic...
-                */
-               ncm->port.cdc_filter = w_value;
-               value = 0;
-               break;
-       /*
-        * and optionally:
-        * case USB_CDC_SEND_ENCAPSULATED_COMMAND:
-        * case USB_CDC_GET_ENCAPSULATED_RESPONSE:
-        * case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS:
-        * case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER:
-        * case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER:
-        * case USB_CDC_GET_ETHERNET_STATISTIC:
-        */
-
-       case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
-               | USB_CDC_GET_NTB_PARAMETERS:
-
-               if (w_length == 0 || w_value != 0 || w_index != ncm->ctrl_id)
-                       goto invalid;
-               value = w_length > sizeof ntb_parameters ?
-                       sizeof ntb_parameters : w_length;
-               memcpy(req->buf, &ntb_parameters, value);
-               VDBG(cdev, "Host asked NTB parameters\n");
-               break;
-
-       case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
-               | USB_CDC_GET_NTB_INPUT_SIZE:
-
-               if (w_length < 4 || w_value != 0 || w_index != ncm->ctrl_id)
-                       goto invalid;
-               put_unaligned_le32(ncm->port.fixed_in_len, req->buf);
-               value = 4;
-               VDBG(cdev, "Host asked INPUT SIZE, sending %d\n",
-                    ncm->port.fixed_in_len);
-               break;
-
-       case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
-               | USB_CDC_SET_NTB_INPUT_SIZE:
-       {
-               if (w_length != 4 || w_value != 0 || w_index != ncm->ctrl_id)
-                       goto invalid;
-               req->complete = ncm_ep0out_complete;
-               req->length = w_length;
-               req->context = f;
-
-               value = req->length;
-               break;
-       }
-
-       case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
-               | USB_CDC_GET_NTB_FORMAT:
-       {
-               uint16_t format;
-
-               if (w_length < 2 || w_value != 0 || w_index != ncm->ctrl_id)
-                       goto invalid;
-               format = (ncm->parser_opts == &ndp16_opts) ? 0x0000 : 0x0001;
-               put_unaligned_le16(format, req->buf);
-               value = 2;
-               VDBG(cdev, "Host asked NTB FORMAT, sending %d\n", format);
-               break;
-       }
-
-       case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
-               | USB_CDC_SET_NTB_FORMAT:
-       {
-               if (w_length != 0 || w_index != ncm->ctrl_id)
-                       goto invalid;
-               switch (w_value) {
-               case 0x0000:
-                       ncm->parser_opts = &ndp16_opts;
-                       DBG(cdev, "NCM16 selected\n");
-                       break;
-               case 0x0001:
-                       ncm->parser_opts = &ndp32_opts;
-                       DBG(cdev, "NCM32 selected\n");
-                       break;
-               default:
-                       goto invalid;
-               }
-               value = 0;
-               break;
-       }
-       case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
-               | USB_CDC_GET_CRC_MODE:
-       {
-               uint16_t is_crc;
-
-               if (w_length < 2 || w_value != 0 || w_index != ncm->ctrl_id)
-                       goto invalid;
-               is_crc = ncm->is_crc ? 0x0001 : 0x0000;
-               put_unaligned_le16(is_crc, req->buf);
-               value = 2;
-               VDBG(cdev, "Host asked CRC MODE, sending %d\n", is_crc);
-               break;
-       }
-
-       case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
-               | USB_CDC_SET_CRC_MODE:
-       {
-               int ndp_hdr_crc = 0;
-
-               if (w_length != 0 || w_index != ncm->ctrl_id)
-                       goto invalid;
-               switch (w_value) {
-               case 0x0000:
-                       ncm->is_crc = false;
-                       ndp_hdr_crc = NCM_NDP_HDR_NOCRC;
-                       DBG(cdev, "non-CRC mode selected\n");
-                       break;
-               case 0x0001:
-                       ncm->is_crc = true;
-                       ndp_hdr_crc = NCM_NDP_HDR_CRC;
-                       DBG(cdev, "CRC mode selected\n");
-                       break;
-               default:
-                       goto invalid;
-               }
-               ncm->ndp_sign = ncm->parser_opts->ndp_sign | ndp_hdr_crc;
-               value = 0;
-               break;
-       }
-
-       /* and disabled in ncm descriptor: */
-       /* case USB_CDC_GET_NET_ADDRESS: */
-       /* case USB_CDC_SET_NET_ADDRESS: */
-       /* case USB_CDC_GET_MAX_DATAGRAM_SIZE: */
-       /* case USB_CDC_SET_MAX_DATAGRAM_SIZE: */
-
-       default:
-invalid:
-               DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
-                       ctrl->bRequestType, ctrl->bRequest,
-                       w_value, w_index, w_length);
-       }
-
-       /* respond with data transfer or status phase? */
-       if (value >= 0) {
-               DBG(cdev, "ncm req%02x.%02x v%04x i%04x l%d\n",
-                       ctrl->bRequestType, ctrl->bRequest,
-                       w_value, w_index, w_length);
-               req->zero = 0;
-               req->length = value;
-               value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
-               if (value < 0)
-                       ERROR(cdev, "ncm req %02x.%02x response err %d\n",
-                                       ctrl->bRequestType, ctrl->bRequest,
-                                       value);
-       }
-
-       /* device either stalls (value < 0) or reports success */
-       return value;
-}
-
-
-static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
-{
-       struct f_ncm            *ncm = func_to_ncm(f);
-       struct usb_composite_dev *cdev = f->config->cdev;
-
-       /* Control interface has only altsetting 0 */
-       if (intf == ncm->ctrl_id) {
-               if (alt != 0)
-                       goto fail;
-
-               if (ncm->notify->driver_data) {
-                       DBG(cdev, "reset ncm control %d\n", intf);
-                       usb_ep_disable(ncm->notify);
-               }
-
-               if (!(ncm->notify->desc)) {
-                       DBG(cdev, "init ncm ctrl %d\n", intf);
-                       if (config_ep_by_speed(cdev->gadget, f, ncm->notify))
-                               goto fail;
-               }
-               usb_ep_enable(ncm->notify);
-               ncm->notify->driver_data = ncm;
-
-       /* Data interface has two altsettings, 0 and 1 */
-       } else if (intf == ncm->data_id) {
-               if (alt > 1)
-                       goto fail;
-
-               if (ncm->port.in_ep->driver_data) {
-                       DBG(cdev, "reset ncm\n");
-                       ncm->timer_stopping = true;
-                       ncm->netdev = NULL;
-                       gether_disconnect(&ncm->port);
-                       ncm_reset_values(ncm);
-               }
-
-               /*
-                * CDC Network only sends data in non-default altsettings.
-                * Changing altsettings resets filters, statistics, etc.
-                */
-               if (alt == 1) {
-                       struct net_device       *net;
-
-                       if (!ncm->port.in_ep->desc ||
-                           !ncm->port.out_ep->desc) {
-                               DBG(cdev, "init ncm\n");
-                               if (config_ep_by_speed(cdev->gadget, f,
-                                                      ncm->port.in_ep) ||
-                                   config_ep_by_speed(cdev->gadget, f,
-                                                      ncm->port.out_ep)) {
-                                       ncm->port.in_ep->desc = NULL;
-                                       ncm->port.out_ep->desc = NULL;
-                                       goto fail;
-                               }
-                       }
-
-                       /* TODO */
-                       /* Enable zlps by default for NCM conformance;
-                        * override for musb_hdrc (avoids txdma ovhead)
-                        */
-                       ncm->port.is_zlp_ok = !(
-                               gadget_is_musbhdrc(cdev->gadget)
-                               );
-                       ncm->port.cdc_filter = DEFAULT_FILTER;
-                       DBG(cdev, "activate ncm\n");
-                       net = gether_connect(&ncm->port);
-                       if (IS_ERR(net))
-                               return PTR_ERR(net);
-                       ncm->netdev = net;
-                       ncm->timer_stopping = false;
-               }
-
-               spin_lock(&ncm->lock);
-               ncm_notify(ncm);
-               spin_unlock(&ncm->lock);
-       } else
-               goto fail;
-
-       return 0;
-fail:
-       return -EINVAL;
-}
-
-/*
- * Because the data interface supports multiple altsettings,
- * this NCM function *MUST* implement a get_alt() method.
- */
-static int ncm_get_alt(struct usb_function *f, unsigned intf)
-{
-       struct f_ncm            *ncm = func_to_ncm(f);
-
-       if (intf == ncm->ctrl_id)
-               return 0;
-       return ncm->port.in_ep->driver_data ? 1 : 0;
-}
-
-static struct sk_buff *package_for_tx(struct f_ncm *ncm)
-{
-       __le16          *ntb_iter;
-       struct sk_buff  *skb2 = NULL;
-       unsigned        ndp_pad;
-       unsigned        ndp_index;
-       unsigned        new_len;
-
-       const struct ndp_parser_opts *opts = ncm->parser_opts;
-       const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment);
-       const int dgram_idx_len = 2 * 2 * opts->dgram_item_len;
-
-       /* Stop the timer */
-       hrtimer_try_to_cancel(&ncm->task_timer);
-
-       ndp_pad = ALIGN(ncm->skb_tx_data->len, ndp_align) -
-                       ncm->skb_tx_data->len;
-       ndp_index = ncm->skb_tx_data->len + ndp_pad;
-       new_len = ndp_index + dgram_idx_len + ncm->skb_tx_ndp->len;
-
-       /* Set the final BlockLength and wNdpIndex */
-       ntb_iter = (void *) ncm->skb_tx_data->data;
-       /* Increment pointer to BlockLength */
-       ntb_iter += 2 + 1 + 1;
-       put_ncm(&ntb_iter, opts->block_length, new_len);
-       put_ncm(&ntb_iter, opts->ndp_index, ndp_index);
-
-       /* Set the final NDP wLength */
-       new_len = opts->ndp_size +
-                       (ncm->ndp_dgram_count * dgram_idx_len);
-       ncm->ndp_dgram_count = 0;
-       /* Increment from start to wLength */
-       ntb_iter = (void *) ncm->skb_tx_ndp->data;
-       ntb_iter += 2;
-       put_unaligned_le16(new_len, ntb_iter);
-
-       /* Merge the skbs */
-       swap(skb2, ncm->skb_tx_data);
-       if (ncm->skb_tx_data) {
-               dev_kfree_skb_any(ncm->skb_tx_data);
-               ncm->skb_tx_data = NULL;
-       }
-
-       /* Insert NDP alignment. */
-       ntb_iter = (void *) skb_put(skb2, ndp_pad);
-       memset(ntb_iter, 0, ndp_pad);
-
-       /* Copy NTB across. */
-       ntb_iter = (void *) skb_put(skb2, ncm->skb_tx_ndp->len);
-       memcpy(ntb_iter, ncm->skb_tx_ndp->data, ncm->skb_tx_ndp->len);
-       dev_kfree_skb_any(ncm->skb_tx_ndp);
-       ncm->skb_tx_ndp = NULL;
-
-       /* Insert zero'd datagram. */
-       ntb_iter = (void *) skb_put(skb2, dgram_idx_len);
-       memset(ntb_iter, 0, dgram_idx_len);
-
-       return skb2;
-}
-
-static struct sk_buff *ncm_wrap_ntb(struct gether *port,
-                                   struct sk_buff *skb)
-{
-       struct f_ncm    *ncm = func_to_ncm(&port->func);
-       struct sk_buff  *skb2 = NULL;
-       int             ncb_len = 0;
-       __le16          *ntb_data;
-       __le16          *ntb_ndp;
-       int             dgram_pad;
-
-       unsigned        max_size = ncm->port.fixed_in_len;
-       const struct ndp_parser_opts *opts = ncm->parser_opts;
-       const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment);
-       const int div = le16_to_cpu(ntb_parameters.wNdpInDivisor);
-       const int rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder);
-       const int dgram_idx_len = 2 * 2 * opts->dgram_item_len;
-
-       if (!skb && !ncm->skb_tx_data)
-               return NULL;
-
-       if (skb) {
-               /* Add the CRC if required up front */
-               if (ncm->is_crc) {
-                       uint32_t        crc;
-                       __le16          *crc_pos;
-
-                       crc = ~crc32_le(~0,
-                                       skb->data,
-                                       skb->len);
-                       crc_pos = (void *) skb_put(skb, sizeof(uint32_t));
-                       put_unaligned_le32(crc, crc_pos);
-               }
-
-               /* If the new skb is too big for the current NCM NTB then
-                * set the current stored skb to be sent now and clear it
-                * ready for new data.
-                * NOTE: Assume maximum align for speed of calculation.
-                */
-               if (ncm->skb_tx_data
-                   && (ncm->ndp_dgram_count >= TX_MAX_NUM_DPE
-                   || (ncm->skb_tx_data->len +
-                   div + rem + skb->len +
-                   ncm->skb_tx_ndp->len + ndp_align + (2 * dgram_idx_len))
-                   > max_size)) {
-                       skb2 = package_for_tx(ncm);
-                       if (!skb2)
-                               goto err;
-               }
-
-               if (!ncm->skb_tx_data) {
-                       ncb_len = opts->nth_size;
-                       dgram_pad = ALIGN(ncb_len, div) + rem - ncb_len;
-                       ncb_len += dgram_pad;
-
-                       /* Create a new skb for the NTH and datagrams. */
-                       ncm->skb_tx_data = alloc_skb(max_size, GFP_ATOMIC);
-                       if (!ncm->skb_tx_data)
-                               goto err;
-
-                       ntb_data = (void *) skb_put(ncm->skb_tx_data, ncb_len);
-                       memset(ntb_data, 0, ncb_len);
-                       /* dwSignature */
-                       put_unaligned_le32(opts->nth_sign, ntb_data);
-                       ntb_data += 2;
-                       /* wHeaderLength */
-                       put_unaligned_le16(opts->nth_size, ntb_data++);
-
-                       /* Allocate an skb for storing the NDP,
-                        * TX_MAX_NUM_DPE should easily suffice for a
-                        * 16k packet.
-                        */
-                       ncm->skb_tx_ndp = alloc_skb((int)(opts->ndp_size
-                                                   + opts->dpe_size
-                                                   * TX_MAX_NUM_DPE),
-                                                   GFP_ATOMIC);
-                       if (!ncm->skb_tx_ndp)
-                               goto err;
-                       ntb_ndp = (void *) skb_put(ncm->skb_tx_ndp,
-                                                   opts->ndp_size);
-                       memset(ntb_ndp, 0, ncb_len);
-                       /* dwSignature */
-                       put_unaligned_le32(ncm->ndp_sign, ntb_ndp);
-                       ntb_ndp += 2;
-
-                       /* There is always a zeroed entry */
-                       ncm->ndp_dgram_count = 1;
-
-                       /* Note: we skip opts->next_ndp_index */
-               }
-
-               /* Delay the timer. */
-               hrtimer_start(&ncm->task_timer,
-                             ktime_set(0, TX_TIMEOUT_NSECS),
-                             HRTIMER_MODE_REL);
-
-               /* Add the datagram position entries */
-               ntb_ndp = (void *) skb_put(ncm->skb_tx_ndp, dgram_idx_len);
-               memset(ntb_ndp, 0, dgram_idx_len);
-
-               ncb_len = ncm->skb_tx_data->len;
-               dgram_pad = ALIGN(ncb_len, div) + rem - ncb_len;
-               ncb_len += dgram_pad;
-
-               /* (d)wDatagramIndex */
-               put_ncm(&ntb_ndp, opts->dgram_item_len, ncb_len);
-               /* (d)wDatagramLength */
-               put_ncm(&ntb_ndp, opts->dgram_item_len, skb->len);
-               ncm->ndp_dgram_count++;
-
-               /* Add the new data to the skb */
-               ntb_data = (void *) skb_put(ncm->skb_tx_data, dgram_pad);
-               memset(ntb_data, 0, dgram_pad);
-               ntb_data = (void *) skb_put(ncm->skb_tx_data, skb->len);
-               memcpy(ntb_data, skb->data, skb->len);
-               dev_kfree_skb_any(skb);
-               skb = NULL;
-
-       } else if (ncm->skb_tx_data && ncm->timer_force_tx) {
-               /* If the tx was requested because of a timeout then send */
-               skb2 = package_for_tx(ncm);
-               if (!skb2)
-                       goto err;
-       }
-
-       return skb2;
-
-err:
-       ncm->netdev->stats.tx_dropped++;
-
-       if (skb)
-               dev_kfree_skb_any(skb);
-       if (ncm->skb_tx_data)
-               dev_kfree_skb_any(ncm->skb_tx_data);
-       if (ncm->skb_tx_ndp)
-               dev_kfree_skb_any(ncm->skb_tx_ndp);
-
-       return NULL;
-}
-
-/*
- * This transmits the NTB if there are frames waiting.
- */
-static void ncm_tx_tasklet(unsigned long data)
-{
-       struct f_ncm    *ncm = (void *)data;
-
-       if (ncm->timer_stopping)
-               return;
-
-       /* Only send if data is available. */
-       if (ncm->skb_tx_data) {
-               ncm->timer_force_tx = true;
-               ncm->netdev->netdev_ops->ndo_start_xmit(NULL, ncm->netdev);
-               ncm->timer_force_tx = false;
-       }
-}
-
-/*
- * The transmit should only be run if no skb data has been sent
- * for a certain duration.
- */
-static enum hrtimer_restart ncm_tx_timeout(struct hrtimer *data)
-{
-       struct f_ncm *ncm = container_of(data, struct f_ncm, task_timer);
-       tasklet_schedule(&ncm->tx_tasklet);
-       return HRTIMER_NORESTART;
-}
-
-static int ncm_unwrap_ntb(struct gether *port,
-                         struct sk_buff *skb,
-                         struct sk_buff_head *list)
-{
-       struct f_ncm    *ncm = func_to_ncm(&port->func);
-       __le16          *tmp = (void *) skb->data;
-       unsigned        index, index2;
-       int             ndp_index;
-       unsigned        dg_len, dg_len2;
-       unsigned        ndp_len;
-       struct sk_buff  *skb2;
-       int             ret = -EINVAL;
-       unsigned        max_size = le32_to_cpu(ntb_parameters.dwNtbOutMaxSize);
-       const struct ndp_parser_opts *opts = ncm->parser_opts;
-       unsigned        crc_len = ncm->is_crc ? sizeof(uint32_t) : 0;
-       int             dgram_counter;
-
-       /* dwSignature */
-       if (get_unaligned_le32(tmp) != opts->nth_sign) {
-               INFO(port->func.config->cdev, "Wrong NTH SIGN, skblen %d\n",
-                       skb->len);
-               print_hex_dump(KERN_INFO, "HEAD:", DUMP_PREFIX_ADDRESS, 32, 1,
-                              skb->data, 32, false);
-
-               goto err;
-       }
-       tmp += 2;
-       /* wHeaderLength */
-       if (get_unaligned_le16(tmp++) != opts->nth_size) {
-               INFO(port->func.config->cdev, "Wrong NTB headersize\n");
-               goto err;
-       }
-       tmp++; /* skip wSequence */
-
-       /* (d)wBlockLength */
-       if (get_ncm(&tmp, opts->block_length) > max_size) {
-               INFO(port->func.config->cdev, "OUT size exceeded\n");
-               goto err;
-       }
-
-       ndp_index = get_ncm(&tmp, opts->ndp_index);
-
-       /* Run through all the NDP's in the NTB */
-       do {
-               /* NCM 3.2 */
-               if (((ndp_index % 4) != 0) &&
-                               (ndp_index < opts->nth_size)) {
-                       INFO(port->func.config->cdev, "Bad index: %#X\n",
-                            ndp_index);
-                       goto err;
-               }
-
-               /* walk through NDP */
-               tmp = (void *)(skb->data + ndp_index);
-               if (get_unaligned_le32(tmp) != ncm->ndp_sign) {
-                       INFO(port->func.config->cdev, "Wrong NDP SIGN\n");
-                       goto err;
-               }
-               tmp += 2;
-
-               ndp_len = get_unaligned_le16(tmp++);
-               /*
-                * NCM 3.3.1
-                * entry is 2 items
-                * item size is 16/32 bits, opts->dgram_item_len * 2 bytes
-                * minimal: struct usb_cdc_ncm_ndpX + normal entry + zero entry
-                * Each entry is a dgram index and a dgram length.
-                */
-               if ((ndp_len < opts->ndp_size
-                               + 2 * 2 * (opts->dgram_item_len * 2))
-                               || (ndp_len % opts->ndplen_align != 0)) {
-                       INFO(port->func.config->cdev, "Bad NDP length: %#X\n",
-                            ndp_len);
-                       goto err;
-               }
-               tmp += opts->reserved1;
-               /* Check for another NDP (d)wNextNdpIndex */
-               ndp_index = get_ncm(&tmp, opts->next_ndp_index);
-               tmp += opts->reserved2;
-
-               ndp_len -= opts->ndp_size;
-               index2 = get_ncm(&tmp, opts->dgram_item_len);
-               dg_len2 = get_ncm(&tmp, opts->dgram_item_len);
-               dgram_counter = 0;
-
-               do {
-                       index = index2;
-                       dg_len = dg_len2;
-                       if (dg_len < 14 + crc_len) { /* ethernet hdr + crc */
-                               INFO(port->func.config->cdev,
-                                    "Bad dgram length: %#X\n", dg_len);
-                               goto err;
-                       }
-                       if (ncm->is_crc) {
-                               uint32_t crc, crc2;
-
-                               crc = get_unaligned_le32(skb->data +
-                                                        index + dg_len -
-                                                        crc_len);
-                               crc2 = ~crc32_le(~0,
-                                                skb->data + index,
-                                                dg_len - crc_len);
-                               if (crc != crc2) {
-                                       INFO(port->func.config->cdev,
-                                            "Bad CRC\n");
-                                       goto err;
-                               }
-                       }
-
-                       index2 = get_ncm(&tmp, opts->dgram_item_len);
-                       dg_len2 = get_ncm(&tmp, opts->dgram_item_len);
-
-                       /*
-                        * Copy the data into a new skb.
-                        * This ensures the truesize is correct
-                        */
-                       skb2 = netdev_alloc_skb_ip_align(ncm->netdev,
-                                                        dg_len - crc_len);
-                       if (skb2 == NULL)
-                               goto err;
-                       memcpy(skb_put(skb2, dg_len - crc_len),
-                              skb->data + index, dg_len - crc_len);
-
-                       skb_queue_tail(list, skb2);
-
-                       ndp_len -= 2 * (opts->dgram_item_len * 2);
-
-                       dgram_counter++;
-
-                       if (index2 == 0 || dg_len2 == 0)
-                               break;
-               } while (ndp_len > 2 * (opts->dgram_item_len * 2));
-       } while (ndp_index);
-
-       dev_kfree_skb_any(skb);
-
-       VDBG(port->func.config->cdev,
-            "Parsed NTB with %d frames\n", dgram_counter);
-       return 0;
-err:
-       skb_queue_purge(list);
-       dev_kfree_skb_any(skb);
-       return ret;
-}
-
-static void ncm_disable(struct usb_function *f)
-{
-       struct f_ncm            *ncm = func_to_ncm(f);
-       struct usb_composite_dev *cdev = f->config->cdev;
-
-       DBG(cdev, "ncm deactivated\n");
-
-       if (ncm->port.in_ep->driver_data) {
-               ncm->timer_stopping = true;
-               ncm->netdev = NULL;
-               gether_disconnect(&ncm->port);
-       }
-
-       if (ncm->notify->driver_data) {
-               usb_ep_disable(ncm->notify);
-               ncm->notify->driver_data = NULL;
-               ncm->notify->desc = NULL;
-       }
-}
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * Callbacks let us notify the host about connect/disconnect when the
- * net device is opened or closed.
- *
- * For testing, note that link states on this side include both opened
- * and closed variants of:
- *
- *   - disconnected/unconfigured
- *   - configured but inactive (data alt 0)
- *   - configured and active (data alt 1)
- *
- * Each needs to be tested with unplug, rmmod, SET_CONFIGURATION, and
- * SET_INTERFACE (altsetting).  Remember also that "configured" doesn't
- * imply the host is actually polling the notification endpoint, and
- * likewise that "active" doesn't imply it's actually using the data
- * endpoints for traffic.
- */
-
-static void ncm_open(struct gether *geth)
-{
-       struct f_ncm            *ncm = func_to_ncm(&geth->func);
-
-       DBG(ncm->port.func.config->cdev, "%s\n", __func__);
-
-       spin_lock(&ncm->lock);
-       ncm->is_open = true;
-       ncm_notify(ncm);
-       spin_unlock(&ncm->lock);
-}
-
-static void ncm_close(struct gether *geth)
-{
-       struct f_ncm            *ncm = func_to_ncm(&geth->func);
-
-       DBG(ncm->port.func.config->cdev, "%s\n", __func__);
-
-       spin_lock(&ncm->lock);
-       ncm->is_open = false;
-       ncm_notify(ncm);
-       spin_unlock(&ncm->lock);
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* ethernet function driver setup/binding */
-
-static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
-{
-       struct usb_composite_dev *cdev = c->cdev;
-       struct f_ncm            *ncm = func_to_ncm(f);
-       struct usb_string       *us;
-       int                     status;
-       struct usb_ep           *ep;
-       struct f_ncm_opts       *ncm_opts;
-
-       if (!can_support_ecm(cdev->gadget))
-               return -EINVAL;
-
-       ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst);
-       /*
-        * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
-        * configurations are bound in sequence with list_for_each_entry,
-        * in each configuration its functions are bound in sequence
-        * with list_for_each_entry, so we assume no race condition
-        * with regard to ncm_opts->bound access
-        */
-       if (!ncm_opts->bound) {
-               mutex_lock(&ncm_opts->lock);
-               gether_set_gadget(ncm_opts->net, cdev->gadget);
-               status = gether_register_netdev(ncm_opts->net);
-               mutex_unlock(&ncm_opts->lock);
-               if (status)
-                       return status;
-               ncm_opts->bound = true;
-       }
-       us = usb_gstrings_attach(cdev, ncm_strings,
-                                ARRAY_SIZE(ncm_string_defs));
-       if (IS_ERR(us))
-               return PTR_ERR(us);
-       ncm_control_intf.iInterface = us[STRING_CTRL_IDX].id;
-       ncm_data_nop_intf.iInterface = us[STRING_DATA_IDX].id;
-       ncm_data_intf.iInterface = us[STRING_DATA_IDX].id;
-       ecm_desc.iMACAddress = us[STRING_MAC_IDX].id;
-       ncm_iad_desc.iFunction = us[STRING_IAD_IDX].id;
-
-       /* allocate instance-specific interface IDs */
-       status = usb_interface_id(c, f);
-       if (status < 0)
-               goto fail;
-       ncm->ctrl_id = status;
-       ncm_iad_desc.bFirstInterface = status;
-
-       ncm_control_intf.bInterfaceNumber = status;
-       ncm_union_desc.bMasterInterface0 = status;
-
-       status = usb_interface_id(c, f);
-       if (status < 0)
-               goto fail;
-       ncm->data_id = status;
-
-       ncm_data_nop_intf.bInterfaceNumber = status;
-       ncm_data_intf.bInterfaceNumber = status;
-       ncm_union_desc.bSlaveInterface0 = status;
-
-       status = -ENODEV;
-
-       /* allocate instance-specific endpoints */
-       ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_in_desc);
-       if (!ep)
-               goto fail;
-       ncm->port.in_ep = ep;
-       ep->driver_data = cdev; /* claim */
-
-       ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_out_desc);
-       if (!ep)
-               goto fail;
-       ncm->port.out_ep = ep;
-       ep->driver_data = cdev; /* claim */
-
-       ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_notify_desc);
-       if (!ep)
-               goto fail;
-       ncm->notify = ep;
-       ep->driver_data = cdev; /* claim */
-
-       status = -ENOMEM;
-
-       /* allocate notification request and buffer */
-       ncm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
-       if (!ncm->notify_req)
-               goto fail;
-       ncm->notify_req->buf = kmalloc(NCM_STATUS_BYTECOUNT, GFP_KERNEL);
-       if (!ncm->notify_req->buf)
-               goto fail;
-       ncm->notify_req->context = ncm;
-       ncm->notify_req->complete = ncm_notify_complete;
-
-       /*
-        * support all relevant hardware speeds... we expect that when
-        * hardware is dual speed, all bulk-capable endpoints work at
-        * both speeds
-        */
-       hs_ncm_in_desc.bEndpointAddress = fs_ncm_in_desc.bEndpointAddress;
-       hs_ncm_out_desc.bEndpointAddress = fs_ncm_out_desc.bEndpointAddress;
-       hs_ncm_notify_desc.bEndpointAddress =
-               fs_ncm_notify_desc.bEndpointAddress;
-
-       status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function,
-                       NULL);
-       /*
-        * NOTE:  all that is done without knowing or caring about
-        * the network link ... which is unavailable to this code
-        * until we're activated via set_alt().
-        */
-
-       ncm->port.open = ncm_open;
-       ncm->port.close = ncm_close;
-
-       tasklet_init(&ncm->tx_tasklet, ncm_tx_tasklet, (unsigned long) ncm);
-       hrtimer_init(&ncm->task_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
-       ncm->task_timer.function = ncm_tx_timeout;
-
-       DBG(cdev, "CDC Network: %s speed IN/%s OUT/%s NOTIFY/%s\n",
-                       gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
-                       ncm->port.in_ep->name, ncm->port.out_ep->name,
-                       ncm->notify->name);
-       return 0;
-
-fail:
-       usb_free_all_descriptors(f);
-       if (ncm->notify_req) {
-               kfree(ncm->notify_req->buf);
-               usb_ep_free_request(ncm->notify, ncm->notify_req);
-       }
-
-       /* we might as well release our claims on endpoints */
-       if (ncm->notify)
-               ncm->notify->driver_data = NULL;
-       if (ncm->port.out_ep)
-               ncm->port.out_ep->driver_data = NULL;
-       if (ncm->port.in_ep)
-               ncm->port.in_ep->driver_data = NULL;
-
-       ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
-
-       return status;
-}
-
-static inline struct f_ncm_opts *to_f_ncm_opts(struct config_item *item)
-{
-       return container_of(to_config_group(item), struct f_ncm_opts,
-                           func_inst.group);
-}
-
-/* f_ncm_item_ops */
-USB_ETHERNET_CONFIGFS_ITEM(ncm);
-
-/* f_ncm_opts_dev_addr */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ncm);
-
-/* f_ncm_opts_host_addr */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ncm);
-
-/* f_ncm_opts_qmult */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ncm);
-
-/* f_ncm_opts_ifname */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ncm);
-
-static struct configfs_attribute *ncm_attrs[] = {
-       &f_ncm_opts_dev_addr.attr,
-       &f_ncm_opts_host_addr.attr,
-       &f_ncm_opts_qmult.attr,
-       &f_ncm_opts_ifname.attr,
-       NULL,
-};
-
-static struct config_item_type ncm_func_type = {
-       .ct_item_ops    = &ncm_item_ops,
-       .ct_attrs       = ncm_attrs,
-       .ct_owner       = THIS_MODULE,
-};
-
-static void ncm_free_inst(struct usb_function_instance *f)
-{
-       struct f_ncm_opts *opts;
-
-       opts = container_of(f, struct f_ncm_opts, func_inst);
-       if (opts->bound)
-               gether_cleanup(netdev_priv(opts->net));
-       else
-               free_netdev(opts->net);
-       kfree(opts);
-}
-
-static struct usb_function_instance *ncm_alloc_inst(void)
-{
-       struct f_ncm_opts *opts;
-
-       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
-       if (!opts)
-               return ERR_PTR(-ENOMEM);
-       mutex_init(&opts->lock);
-       opts->func_inst.free_func_inst = ncm_free_inst;
-       opts->net = gether_setup_default();
-       if (IS_ERR(opts->net)) {
-               struct net_device *net = opts->net;
-               kfree(opts);
-               return ERR_CAST(net);
-       }
-
-       config_group_init_type_name(&opts->func_inst.group, "", &ncm_func_type);
-
-       return &opts->func_inst;
-}
-
-static void ncm_free(struct usb_function *f)
-{
-       struct f_ncm *ncm;
-       struct f_ncm_opts *opts;
-
-       ncm = func_to_ncm(f);
-       opts = container_of(f->fi, struct f_ncm_opts, func_inst);
-       kfree(ncm);
-       mutex_lock(&opts->lock);
-       opts->refcnt--;
-       mutex_unlock(&opts->lock);
-}
-
-static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
-{
-       struct f_ncm *ncm = func_to_ncm(f);
-
-       DBG(c->cdev, "ncm unbind\n");
-
-       hrtimer_cancel(&ncm->task_timer);
-       tasklet_kill(&ncm->tx_tasklet);
-
-       ncm_string_defs[0].id = 0;
-       usb_free_all_descriptors(f);
-
-       kfree(ncm->notify_req->buf);
-       usb_ep_free_request(ncm->notify, ncm->notify_req);
-}
-
-static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
-{
-       struct f_ncm            *ncm;
-       struct f_ncm_opts       *opts;
-       int status;
-
-       /* allocate and initialize one new instance */
-       ncm = kzalloc(sizeof(*ncm), GFP_KERNEL);
-       if (!ncm)
-               return ERR_PTR(-ENOMEM);
-
-       opts = container_of(fi, struct f_ncm_opts, func_inst);
-       mutex_lock(&opts->lock);
-       opts->refcnt++;
-
-       /* export host's Ethernet address in CDC format */
-       status = gether_get_host_addr_cdc(opts->net, ncm->ethaddr,
-                                     sizeof(ncm->ethaddr));
-       if (status < 12) { /* strlen("01234567890a") */
-               kfree(ncm);
-               mutex_unlock(&opts->lock);
-               return ERR_PTR(-EINVAL);
-       }
-       ncm_string_defs[STRING_MAC_IDX].s = ncm->ethaddr;
-
-       spin_lock_init(&ncm->lock);
-       ncm_reset_values(ncm);
-       ncm->port.ioport = netdev_priv(opts->net);
-       mutex_unlock(&opts->lock);
-       ncm->port.is_fixed = true;
-       ncm->port.supports_multi_frame = true;
-
-       ncm->port.func.name = "cdc_network";
-       /* descriptors are per-instance copies */
-       ncm->port.func.bind = ncm_bind;
-       ncm->port.func.unbind = ncm_unbind;
-       ncm->port.func.set_alt = ncm_set_alt;
-       ncm->port.func.get_alt = ncm_get_alt;
-       ncm->port.func.setup = ncm_setup;
-       ncm->port.func.disable = ncm_disable;
-       ncm->port.func.free_func = ncm_free;
-
-       ncm->port.wrap = ncm_wrap_ntb;
-       ncm->port.unwrap = ncm_unwrap_ntb;
-
-       return &ncm->port.func;
-}
-
-DECLARE_USB_FUNCTION_INIT(ncm, ncm_alloc_inst, ncm_alloc);
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Yauheni Kaliuta");
diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/f_obex.c
deleted file mode 100644 (file)
index aebae18..0000000
+++ /dev/null
@@ -1,533 +0,0 @@
-/*
- * f_obex.c -- USB CDC OBEX function driver
- *
- * Copyright (C) 2008 Nokia Corporation
- * Contact: Felipe Balbi <felipe.balbi@nokia.com>
- *
- * Based on f_acm.c by Al Borchers and 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.
- */
-
-/* #define VERBOSE_DEBUG */
-
-#include <linux/slab.h>
-#include <linux/kernel.h>
-#include <linux/device.h>
-#include <linux/module.h>
-
-#include "u_serial.h"
-#include "gadget_chips.h"
-
-
-/*
- * This CDC OBEX function support just packages a TTY-ish byte stream.
- * A user mode server will put it into "raw" mode and handle all the
- * relevant protocol details ... this is just a kernel passthrough.
- * When possible, we prevent gadget enumeration until that server is
- * ready to handle the commands.
- */
-
-struct f_obex {
-       struct gserial                  port;
-       u8                              ctrl_id;
-       u8                              data_id;
-       u8                              port_num;
-       u8                              can_activate;
-};
-
-static inline struct f_obex *func_to_obex(struct usb_function *f)
-{
-       return container_of(f, struct f_obex, port.func);
-}
-
-static inline struct f_obex *port_to_obex(struct gserial *p)
-{
-       return container_of(p, struct f_obex, port);
-}
-
-/*-------------------------------------------------------------------------*/
-
-#define OBEX_CTRL_IDX  0
-#define OBEX_DATA_IDX  1
-
-static struct usb_string obex_string_defs[] = {
-       [OBEX_CTRL_IDX].s       = "CDC Object Exchange (OBEX)",
-       [OBEX_DATA_IDX].s       = "CDC OBEX Data",
-       {  },   /* end of list */
-};
-
-static struct usb_gadget_strings obex_string_table = {
-       .language               = 0x0409,       /* en-US */
-       .strings                = obex_string_defs,
-};
-
-static struct usb_gadget_strings *obex_strings[] = {
-       &obex_string_table,
-       NULL,
-};
-
-/*-------------------------------------------------------------------------*/
-
-static struct usb_interface_descriptor obex_control_intf = {
-       .bLength                = sizeof(obex_control_intf),
-       .bDescriptorType        = USB_DT_INTERFACE,
-       .bInterfaceNumber       = 0,
-
-       .bAlternateSetting      = 0,
-       .bNumEndpoints          = 0,
-       .bInterfaceClass        = USB_CLASS_COMM,
-       .bInterfaceSubClass     = USB_CDC_SUBCLASS_OBEX,
-};
-
-static struct usb_interface_descriptor obex_data_nop_intf = {
-       .bLength                = sizeof(obex_data_nop_intf),
-       .bDescriptorType        = USB_DT_INTERFACE,
-       .bInterfaceNumber       = 1,
-
-       .bAlternateSetting      = 0,
-       .bNumEndpoints          = 0,
-       .bInterfaceClass        = USB_CLASS_CDC_DATA,
-};
-
-static struct usb_interface_descriptor obex_data_intf = {
-       .bLength                = sizeof(obex_data_intf),
-       .bDescriptorType        = USB_DT_INTERFACE,
-       .bInterfaceNumber       = 2,
-
-       .bAlternateSetting      = 1,
-       .bNumEndpoints          = 2,
-       .bInterfaceClass        = USB_CLASS_CDC_DATA,
-};
-
-static struct usb_cdc_header_desc obex_cdc_header_desc = {
-       .bLength                = sizeof(obex_cdc_header_desc),
-       .bDescriptorType        = USB_DT_CS_INTERFACE,
-       .bDescriptorSubType     = USB_CDC_HEADER_TYPE,
-       .bcdCDC                 = cpu_to_le16(0x0120),
-};
-
-static struct usb_cdc_union_desc obex_cdc_union_desc = {
-       .bLength                = sizeof(obex_cdc_union_desc),
-       .bDescriptorType        = USB_DT_CS_INTERFACE,
-       .bDescriptorSubType     = USB_CDC_UNION_TYPE,
-       .bMasterInterface0      = 1,
-       .bSlaveInterface0       = 2,
-};
-
-static struct usb_cdc_obex_desc obex_desc = {
-       .bLength                = sizeof(obex_desc),
-       .bDescriptorType        = USB_DT_CS_INTERFACE,
-       .bDescriptorSubType     = USB_CDC_OBEX_TYPE,
-       .bcdVersion             = cpu_to_le16(0x0100),
-};
-
-/* High-Speed Support */
-
-static struct usb_endpoint_descriptor obex_hs_ep_out_desc = {
-       .bLength                = USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType        = USB_DT_ENDPOINT,
-
-       .bEndpointAddress       = USB_DIR_OUT,
-       .bmAttributes           = USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize         = cpu_to_le16(512),
-};
-
-static struct usb_endpoint_descriptor obex_hs_ep_in_desc = {
-       .bLength                = USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType        = USB_DT_ENDPOINT,
-
-       .bEndpointAddress       = USB_DIR_IN,
-       .bmAttributes           = USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize         = cpu_to_le16(512),
-};
-
-static struct usb_descriptor_header *hs_function[] = {
-       (struct usb_descriptor_header *) &obex_control_intf,
-       (struct usb_descriptor_header *) &obex_cdc_header_desc,
-       (struct usb_descriptor_header *) &obex_desc,
-       (struct usb_descriptor_header *) &obex_cdc_union_desc,
-
-       (struct usb_descriptor_header *) &obex_data_nop_intf,
-       (struct usb_descriptor_header *) &obex_data_intf,
-       (struct usb_descriptor_header *) &obex_hs_ep_in_desc,
-       (struct usb_descriptor_header *) &obex_hs_ep_out_desc,
-       NULL,
-};
-
-/* Full-Speed Support */
-
-static struct usb_endpoint_descriptor obex_fs_ep_in_desc = {
-       .bLength                = USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType        = USB_DT_ENDPOINT,
-
-       .bEndpointAddress       = USB_DIR_IN,
-       .bmAttributes           = USB_ENDPOINT_XFER_BULK,
-};
-
-static struct usb_endpoint_descriptor obex_fs_ep_out_desc = {
-       .bLength                = USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType        = USB_DT_ENDPOINT,
-
-       .bEndpointAddress       = USB_DIR_OUT,
-       .bmAttributes           = USB_ENDPOINT_XFER_BULK,
-};
-
-static struct usb_descriptor_header *fs_function[] = {
-       (struct usb_descriptor_header *) &obex_control_intf,
-       (struct usb_descriptor_header *) &obex_cdc_header_desc,
-       (struct usb_descriptor_header *) &obex_desc,
-       (struct usb_descriptor_header *) &obex_cdc_union_desc,
-
-       (struct usb_descriptor_header *) &obex_data_nop_intf,
-       (struct usb_descriptor_header *) &obex_data_intf,
-       (struct usb_descriptor_header *) &obex_fs_ep_in_desc,
-       (struct usb_descriptor_header *) &obex_fs_ep_out_desc,
-       NULL,
-};
-
-/*-------------------------------------------------------------------------*/
-
-static int obex_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
-{
-       struct f_obex           *obex = func_to_obex(f);
-       struct usb_composite_dev *cdev = f->config->cdev;
-
-       if (intf == obex->ctrl_id) {
-               if (alt != 0)
-                       goto fail;
-               /* NOP */
-               DBG(cdev, "reset obex ttyGS%d control\n", obex->port_num);
-
-       } else if (intf == obex->data_id) {
-               if (alt > 1)
-                       goto fail;
-
-               if (obex->port.in->driver_data) {
-                       DBG(cdev, "reset obex ttyGS%d\n", obex->port_num);
-                       gserial_disconnect(&obex->port);
-               }
-
-               if (!obex->port.in->desc || !obex->port.out->desc) {
-                       DBG(cdev, "init obex ttyGS%d\n", obex->port_num);
-                       if (config_ep_by_speed(cdev->gadget, f,
-                                              obex->port.in) ||
-                           config_ep_by_speed(cdev->gadget, f,
-                                              obex->port.out)) {
-                               obex->port.out->desc = NULL;
-                               obex->port.in->desc = NULL;
-                               goto fail;
-                       }
-               }
-
-               if (alt == 1) {
-                       DBG(cdev, "activate obex ttyGS%d\n", obex->port_num);
-                       gserial_connect(&obex->port, obex->port_num);
-               }
-
-       } else
-               goto fail;
-
-       return 0;
-
-fail:
-       return -EINVAL;
-}
-
-static int obex_get_alt(struct usb_function *f, unsigned intf)
-{
-       struct f_obex           *obex = func_to_obex(f);
-
-       if (intf == obex->ctrl_id)
-               return 0;
-
-       return obex->port.in->driver_data ? 1 : 0;
-}
-
-static void obex_disable(struct usb_function *f)
-{
-       struct f_obex   *obex = func_to_obex(f);
-       struct usb_composite_dev *cdev = f->config->cdev;
-
-       DBG(cdev, "obex ttyGS%d disable\n", obex->port_num);
-       gserial_disconnect(&obex->port);
-}
-
-/*-------------------------------------------------------------------------*/
-
-static void obex_connect(struct gserial *g)
-{
-       struct f_obex           *obex = port_to_obex(g);
-       struct usb_composite_dev *cdev = g->func.config->cdev;
-       int                     status;
-
-       if (!obex->can_activate)
-               return;
-
-       status = usb_function_activate(&g->func);
-       if (status)
-               DBG(cdev, "obex ttyGS%d function activate --> %d\n",
-                       obex->port_num, status);
-}
-
-static void obex_disconnect(struct gserial *g)
-{
-       struct f_obex           *obex = port_to_obex(g);
-       struct usb_composite_dev *cdev = g->func.config->cdev;
-       int                     status;
-
-       if (!obex->can_activate)
-               return;
-
-       status = usb_function_deactivate(&g->func);
-       if (status)
-               DBG(cdev, "obex ttyGS%d function deactivate --> %d\n",
-                       obex->port_num, status);
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* Some controllers can't support CDC OBEX ... */
-static inline bool can_support_obex(struct usb_configuration *c)
-{
-       /* Since the first interface is a NOP, we can ignore the
-        * issue of multi-interface support on most controllers.
-        *
-        * Altsettings are mandatory, however...
-        */
-       if (!gadget_supports_altsettings(c->cdev->gadget))
-               return false;
-
-       /* everything else is *probably* fine ... */
-       return true;
-}
-
-static int obex_bind(struct usb_configuration *c, struct usb_function *f)
-{
-       struct usb_composite_dev *cdev = c->cdev;
-       struct f_obex           *obex = func_to_obex(f);
-       struct usb_string       *us;
-       int                     status;
-       struct usb_ep           *ep;
-
-       if (!can_support_obex(c))
-               return -EINVAL;
-
-       us = usb_gstrings_attach(cdev, obex_strings,
-                                ARRAY_SIZE(obex_string_defs));
-       if (IS_ERR(us))
-               return PTR_ERR(us);
-       obex_control_intf.iInterface = us[OBEX_CTRL_IDX].id;
-       obex_data_nop_intf.iInterface = us[OBEX_DATA_IDX].id;
-       obex_data_intf.iInterface = us[OBEX_DATA_IDX].id;
-
-       /* allocate instance-specific interface IDs, and patch descriptors */
-
-       status = usb_interface_id(c, f);
-       if (status < 0)
-               goto fail;
-       obex->ctrl_id = status;
-
-       obex_control_intf.bInterfaceNumber = status;
-       obex_cdc_union_desc.bMasterInterface0 = status;
-
-       status = usb_interface_id(c, f);
-       if (status < 0)
-               goto fail;
-       obex->data_id = status;
-
-       obex_data_nop_intf.bInterfaceNumber = status;
-       obex_data_intf.bInterfaceNumber = status;
-       obex_cdc_union_desc.bSlaveInterface0 = status;
-
-       /* allocate instance-specific endpoints */
-
-       status = -ENODEV;
-       ep = usb_ep_autoconfig(cdev->gadget, &obex_fs_ep_in_desc);
-       if (!ep)
-               goto fail;
-       obex->port.in = ep;
-       ep->driver_data = cdev; /* claim */
-
-       ep = usb_ep_autoconfig(cdev->gadget, &obex_fs_ep_out_desc);
-       if (!ep)
-               goto fail;
-       obex->port.out = ep;
-       ep->driver_data = cdev; /* claim */
-
-       /* support all relevant hardware speeds... we expect that when
-        * hardware is dual speed, all bulk-capable endpoints work at
-        * both speeds
-        */
-
-       obex_hs_ep_in_desc.bEndpointAddress =
-               obex_fs_ep_in_desc.bEndpointAddress;
-       obex_hs_ep_out_desc.bEndpointAddress =
-               obex_fs_ep_out_desc.bEndpointAddress;
-
-       status = usb_assign_descriptors(f, fs_function, hs_function, NULL);
-       if (status)
-               goto fail;
-
-       /* Avoid letting this gadget enumerate until the userspace
-        * OBEX server is active.
-        */
-       status = usb_function_deactivate(f);
-       if (status < 0)
-               WARNING(cdev, "obex ttyGS%d: can't prevent enumeration, %d\n",
-                       obex->port_num, status);
-       else
-               obex->can_activate = true;
-
-
-       DBG(cdev, "obex ttyGS%d: %s speed IN/%s OUT/%s\n",
-                       obex->port_num,
-                       gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
-                       obex->port.in->name, obex->port.out->name);
-
-       return 0;
-
-fail:
-       usb_free_all_descriptors(f);
-       /* we might as well release our claims on endpoints */
-       if (obex->port.out)
-               obex->port.out->driver_data = NULL;
-       if (obex->port.in)
-               obex->port.in->driver_data = NULL;
-
-       ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status);
-
-       return status;
-}
-
-static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item)
-{
-       return container_of(to_config_group(item), struct f_serial_opts,
-                           func_inst.group);
-}
-
-CONFIGFS_ATTR_STRUCT(f_serial_opts);
-static ssize_t f_obex_attr_show(struct config_item *item,
-                               struct configfs_attribute *attr,
-                               char *page)
-{
-       struct f_serial_opts *opts = to_f_serial_opts(item);
-       struct f_serial_opts_attribute *f_serial_opts_attr =
-               container_of(attr, struct f_serial_opts_attribute, attr);
-       ssize_t ret = 0;
-
-       if (f_serial_opts_attr->show)
-               ret = f_serial_opts_attr->show(opts, page);
-
-       return ret;
-}
-
-static void obex_attr_release(struct config_item *item)
-{
-       struct f_serial_opts *opts = to_f_serial_opts(item);
-
-       usb_put_function_instance(&opts->func_inst);
-}
-
-static struct configfs_item_operations obex_item_ops = {
-       .release        = obex_attr_release,
-       .show_attribute = f_obex_attr_show,
-};
-
-static ssize_t f_obex_port_num_show(struct f_serial_opts *opts, char *page)
-{
-       return sprintf(page, "%u\n", opts->port_num);
-}
-
-static struct f_serial_opts_attribute f_obex_port_num =
-       __CONFIGFS_ATTR_RO(port_num, f_obex_port_num_show);
-
-static struct configfs_attribute *acm_attrs[] = {
-       &f_obex_port_num.attr,
-       NULL,
-};
-
-static struct config_item_type obex_func_type = {
-       .ct_item_ops    = &obex_item_ops,
-       .ct_attrs       = acm_attrs,
-       .ct_owner       = THIS_MODULE,
-};
-
-static void obex_free_inst(struct usb_function_instance *f)
-{
-       struct f_serial_opts *opts;
-
-       opts = container_of(f, struct f_serial_opts, func_inst);
-       gserial_free_line(opts->port_num);
-       kfree(opts);
-}
-
-static struct usb_function_instance *obex_alloc_inst(void)
-{
-       struct f_serial_opts *opts;
-       int ret;
-
-       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
-       if (!opts)
-               return ERR_PTR(-ENOMEM);
-
-       opts->func_inst.free_func_inst = obex_free_inst;
-       ret = gserial_alloc_line(&opts->port_num);
-       if (ret) {
-               kfree(opts);
-               return ERR_PTR(ret);
-       }
-       config_group_init_type_name(&opts->func_inst.group, "",
-                                   &obex_func_type);
-
-       return &opts->func_inst;
-}
-
-static void obex_free(struct usb_function *f)
-{
-       struct f_obex *obex;
-
-       obex = func_to_obex(f);
-       kfree(obex);
-}
-
-static void obex_unbind(struct usb_configuration *c, struct usb_function *f)
-{
-       usb_free_all_descriptors(f);
-}
-
-static struct usb_function *obex_alloc(struct usb_function_instance *fi)
-{
-       struct f_obex   *obex;
-       struct f_serial_opts *opts;
-
-       /* allocate and initialize one new instance */
-       obex = kzalloc(sizeof(*obex), GFP_KERNEL);
-       if (!obex)
-               return ERR_PTR(-ENOMEM);
-
-       opts = container_of(fi, struct f_serial_opts, func_inst);
-
-       obex->port_num = opts->port_num;
-
-       obex->port.connect = obex_connect;
-       obex->port.disconnect = obex_disconnect;
-
-       obex->port.func.name = "obex";
-       /* descriptors are per-instance copies */
-       obex->port.func.bind = obex_bind;
-       obex->port.func.unbind = obex_unbind;
-       obex->port.func.set_alt = obex_set_alt;
-       obex->port.func.get_alt = obex_get_alt;
-       obex->port.func.disable = obex_disable;
-       obex->port.func.free_func = obex_free;
-
-       return &obex->port.func;
-}
-
-DECLARE_USB_FUNCTION_INIT(obex, obex_alloc_inst, obex_alloc);
-MODULE_AUTHOR("Felipe Balbi");
-MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c
deleted file mode 100644 (file)
index f2b7817..0000000
+++ /dev/null
@@ -1,758 +0,0 @@
-/*
- * f_phonet.c -- USB CDC Phonet function
- *
- * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
- *
- * Author: Rémi Denis-Courmont
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- */
-
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/device.h>
-
-#include <linux/netdevice.h>
-#include <linux/if_ether.h>
-#include <linux/if_phonet.h>
-#include <linux/if_arp.h>
-
-#include <linux/usb/ch9.h>
-#include <linux/usb/cdc.h>
-#include <linux/usb/composite.h>
-
-#include "u_phonet.h"
-#include "u_ether.h"
-
-#define PN_MEDIA_USB   0x1B
-#define MAXPACKET      512
-#if (PAGE_SIZE % MAXPACKET)
-#error MAXPACKET must divide PAGE_SIZE!
-#endif
-
-/*-------------------------------------------------------------------------*/
-
-struct phonet_port {
-       struct f_phonet                 *usb;
-       spinlock_t                      lock;
-};
-
-struct f_phonet {
-       struct usb_function             function;
-       struct {
-               struct sk_buff          *skb;
-               spinlock_t              lock;
-       } rx;
-       struct net_device               *dev;
-       struct usb_ep                   *in_ep, *out_ep;
-
-       struct usb_request              *in_req;
-       struct usb_request              *out_reqv[0];
-};
-
-static int phonet_rxq_size = 17;
-
-static inline struct f_phonet *func_to_pn(struct usb_function *f)
-{
-       return container_of(f, struct f_phonet, function);
-}
-
-/*-------------------------------------------------------------------------*/
-
-#define USB_CDC_SUBCLASS_PHONET        0xfe
-#define USB_CDC_PHONET_TYPE    0xab
-
-static struct usb_interface_descriptor
-pn_control_intf_desc = {
-       .bLength =              sizeof pn_control_intf_desc,
-       .bDescriptorType =      USB_DT_INTERFACE,
-
-       /* .bInterfaceNumber =  DYNAMIC, */
-       .bInterfaceClass =      USB_CLASS_COMM,
-       .bInterfaceSubClass =   USB_CDC_SUBCLASS_PHONET,
-};
-
-static const struct usb_cdc_header_desc
-pn_header_desc = {
-       .bLength =              sizeof pn_header_desc,
-       .bDescriptorType =      USB_DT_CS_INTERFACE,
-       .bDescriptorSubType =   USB_CDC_HEADER_TYPE,
-       .bcdCDC =               cpu_to_le16(0x0110),
-};
-
-static const struct usb_cdc_header_desc
-pn_phonet_desc = {
-       .bLength =              sizeof pn_phonet_desc,
-       .bDescriptorType =      USB_DT_CS_INTERFACE,
-       .bDescriptorSubType =   USB_CDC_PHONET_TYPE,
-       .bcdCDC =               cpu_to_le16(0x1505), /* ??? */
-};
-
-static struct usb_cdc_union_desc
-pn_union_desc = {
-       .bLength =              sizeof pn_union_desc,
-       .bDescriptorType =      USB_DT_CS_INTERFACE,
-       .bDescriptorSubType =   USB_CDC_UNION_TYPE,
-
-       /* .bMasterInterface0 = DYNAMIC, */
-       /* .bSlaveInterface0 =  DYNAMIC, */
-};
-
-static struct usb_interface_descriptor
-pn_data_nop_intf_desc = {
-       .bLength =              sizeof pn_data_nop_intf_desc,
-       .bDescriptorType =      USB_DT_INTERFACE,
-
-       /* .bInterfaceNumber =  DYNAMIC, */
-       .bAlternateSetting =    0,
-       .bNumEndpoints =        0,
-       .bInterfaceClass =      USB_CLASS_CDC_DATA,
-};
-
-static struct usb_interface_descriptor
-pn_data_intf_desc = {
-       .bLength =              sizeof pn_data_intf_desc,
-       .bDescriptorType =      USB_DT_INTERFACE,
-
-       /* .bInterfaceNumber =  DYNAMIC, */
-       .bAlternateSetting =    1,
-       .bNumEndpoints =        2,
-       .bInterfaceClass =      USB_CLASS_CDC_DATA,
-};
-
-static struct usb_endpoint_descriptor
-pn_fs_sink_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_OUT,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-};
-
-static struct usb_endpoint_descriptor
-pn_hs_sink_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_OUT,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(MAXPACKET),
-};
-
-static struct usb_endpoint_descriptor
-pn_fs_source_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-};
-
-static struct usb_endpoint_descriptor
-pn_hs_source_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(512),
-};
-
-static struct usb_descriptor_header *fs_pn_function[] = {
-       (struct usb_descriptor_header *) &pn_control_intf_desc,
-       (struct usb_descriptor_header *) &pn_header_desc,
-       (struct usb_descriptor_header *) &pn_phonet_desc,
-       (struct usb_descriptor_header *) &pn_union_desc,
-       (struct usb_descriptor_header *) &pn_data_nop_intf_desc,
-       (struct usb_descriptor_header *) &pn_data_intf_desc,
-       (struct usb_descriptor_header *) &pn_fs_sink_desc,
-       (struct usb_descriptor_header *) &pn_fs_source_desc,
-       NULL,
-};
-
-static struct usb_descriptor_header *hs_pn_function[] = {
-       (struct usb_descriptor_header *) &pn_control_intf_desc,
-       (struct usb_descriptor_header *) &pn_header_desc,
-       (struct usb_descriptor_header *) &pn_phonet_desc,
-       (struct usb_descriptor_header *) &pn_union_desc,
-       (struct usb_descriptor_header *) &pn_data_nop_intf_desc,
-       (struct usb_descriptor_header *) &pn_data_intf_desc,
-       (struct usb_descriptor_header *) &pn_hs_sink_desc,
-       (struct usb_descriptor_header *) &pn_hs_source_desc,
-       NULL,
-};
-
-/*-------------------------------------------------------------------------*/
-
-static int pn_net_open(struct net_device *dev)
-{
-       netif_wake_queue(dev);
-       return 0;
-}
-
-static int pn_net_close(struct net_device *dev)
-{
-       netif_stop_queue(dev);
-       return 0;
-}
-
-static void pn_tx_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       struct f_phonet *fp = ep->driver_data;
-       struct net_device *dev = fp->dev;
-       struct sk_buff *skb = req->context;
-
-       switch (req->status) {
-       case 0:
-               dev->stats.tx_packets++;
-               dev->stats.tx_bytes += skb->len;
-               break;
-
-       case -ESHUTDOWN: /* disconnected */
-       case -ECONNRESET: /* disabled */
-               dev->stats.tx_aborted_errors++;
-       default:
-               dev->stats.tx_errors++;
-       }
-
-       dev_kfree_skb_any(skb);
-       netif_wake_queue(dev);
-}
-
-static int pn_net_xmit(struct sk_buff *skb, struct net_device *dev)
-{
-       struct phonet_port *port = netdev_priv(dev);
-       struct f_phonet *fp;
-       struct usb_request *req;
-       unsigned long flags;
-
-       if (skb->protocol != htons(ETH_P_PHONET))
-               goto out;
-
-       spin_lock_irqsave(&port->lock, flags);
-       fp = port->usb;
-       if (unlikely(!fp)) /* race with carrier loss */
-               goto out_unlock;
-
-       req = fp->in_req;
-       req->buf = skb->data;
-       req->length = skb->len;
-       req->complete = pn_tx_complete;
-       req->zero = 1;
-       req->context = skb;
-
-       if (unlikely(usb_ep_queue(fp->in_ep, req, GFP_ATOMIC)))
-               goto out_unlock;
-
-       netif_stop_queue(dev);
-       skb = NULL;
-
-out_unlock:
-       spin_unlock_irqrestore(&port->lock, flags);
-out:
-       if (unlikely(skb)) {
-               dev_kfree_skb(skb);
-               dev->stats.tx_dropped++;
-       }
-       return NETDEV_TX_OK;
-}
-
-static int pn_net_mtu(struct net_device *dev, int new_mtu)
-{
-       if ((new_mtu < PHONET_MIN_MTU) || (new_mtu > PHONET_MAX_MTU))
-               return -EINVAL;
-       dev->mtu = new_mtu;
-       return 0;
-}
-
-static const struct net_device_ops pn_netdev_ops = {
-       .ndo_open       = pn_net_open,
-       .ndo_stop       = pn_net_close,
-       .ndo_start_xmit = pn_net_xmit,
-       .ndo_change_mtu = pn_net_mtu,
-};
-
-static void pn_net_setup(struct net_device *dev)
-{
-       dev->features           = 0;
-       dev->type               = ARPHRD_PHONET;
-       dev->flags              = IFF_POINTOPOINT | IFF_NOARP;
-       dev->mtu                = PHONET_DEV_MTU;
-       dev->hard_header_len    = 1;
-       dev->dev_addr[0]        = PN_MEDIA_USB;
-       dev->addr_len           = 1;
-       dev->tx_queue_len       = 1;
-
-       dev->netdev_ops         = &pn_netdev_ops;
-       dev->destructor         = free_netdev;
-       dev->header_ops         = &phonet_header_ops;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * Queue buffer for data from the host
- */
-static int
-pn_rx_submit(struct f_phonet *fp, struct usb_request *req, gfp_t gfp_flags)
-{
-       struct page *page;
-       int err;
-
-       page = __skb_alloc_page(gfp_flags | __GFP_NOMEMALLOC, NULL);
-       if (!page)
-               return -ENOMEM;
-
-       req->buf = page_address(page);
-       req->length = PAGE_SIZE;
-       req->context = page;
-
-       err = usb_ep_queue(fp->out_ep, req, gfp_flags);
-       if (unlikely(err))
-               put_page(page);
-       return err;
-}
-
-static void pn_rx_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       struct f_phonet *fp = ep->driver_data;
-       struct net_device *dev = fp->dev;
-       struct page *page = req->context;
-       struct sk_buff *skb;
-       unsigned long flags;
-       int status = req->status;
-
-       switch (status) {
-       case 0:
-               spin_lock_irqsave(&fp->rx.lock, flags);
-               skb = fp->rx.skb;
-               if (!skb)
-                       skb = fp->rx.skb = netdev_alloc_skb(dev, 12);
-               if (req->actual < req->length) /* Last fragment */
-                       fp->rx.skb = NULL;
-               spin_unlock_irqrestore(&fp->rx.lock, flags);
-
-               if (unlikely(!skb))
-                       break;
-
-               if (skb->len == 0) { /* First fragment */
-                       skb->protocol = htons(ETH_P_PHONET);
-                       skb_reset_mac_header(skb);
-                       /* Can't use pskb_pull() on page in IRQ */
-                       memcpy(skb_put(skb, 1), page_address(page), 1);
-               }
-
-               skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page,
-                               skb->len <= 1, req->actual, PAGE_SIZE);
-               page = NULL;
-
-               if (req->actual < req->length) { /* Last fragment */
-                       skb->dev = dev;
-                       dev->stats.rx_packets++;
-                       dev->stats.rx_bytes += skb->len;
-
-                       netif_rx(skb);
-               }
-               break;
-
-       /* Do not resubmit in these cases: */
-       case -ESHUTDOWN: /* disconnect */
-       case -ECONNABORTED: /* hw reset */
-       case -ECONNRESET: /* dequeued (unlink or netif down) */
-               req = NULL;
-               break;
-
-       /* Do resubmit in these cases: */
-       case -EOVERFLOW: /* request buffer overflow */
-               dev->stats.rx_over_errors++;
-       default:
-               dev->stats.rx_errors++;
-               break;
-       }
-
-       if (page)
-               put_page(page);
-       if (req)
-               pn_rx_submit(fp, req, GFP_ATOMIC | __GFP_COLD);
-}
-
-/*-------------------------------------------------------------------------*/
-
-static void __pn_reset(struct usb_function *f)
-{
-       struct f_phonet *fp = func_to_pn(f);
-       struct net_device *dev = fp->dev;
-       struct phonet_port *port = netdev_priv(dev);
-
-       netif_carrier_off(dev);
-       port->usb = NULL;
-
-       usb_ep_disable(fp->out_ep);
-       usb_ep_disable(fp->in_ep);
-       if (fp->rx.skb) {
-               dev_kfree_skb_irq(fp->rx.skb);
-               fp->rx.skb = NULL;
-       }
-}
-
-static int pn_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
-{
-       struct f_phonet *fp = func_to_pn(f);
-       struct usb_gadget *gadget = fp->function.config->cdev->gadget;
-
-       if (intf == pn_control_intf_desc.bInterfaceNumber)
-               /* control interface, no altsetting */
-               return (alt > 0) ? -EINVAL : 0;
-
-       if (intf == pn_data_intf_desc.bInterfaceNumber) {
-               struct net_device *dev = fp->dev;
-               struct phonet_port *port = netdev_priv(dev);
-
-               /* data intf (0: inactive, 1: active) */
-               if (alt > 1)
-                       return -EINVAL;
-
-               spin_lock(&port->lock);
-               __pn_reset(f);
-               if (alt == 1) {
-                       int i;
-
-                       if (config_ep_by_speed(gadget, f, fp->in_ep) ||
-                           config_ep_by_speed(gadget, f, fp->out_ep)) {
-                               fp->in_ep->desc = NULL;
-                               fp->out_ep->desc = NULL;
-                               spin_unlock(&port->lock);
-                               return -EINVAL;
-                       }
-                       usb_ep_enable(fp->out_ep);
-                       usb_ep_enable(fp->in_ep);
-
-                       port->usb = fp;
-                       fp->out_ep->driver_data = fp;
-                       fp->in_ep->driver_data = fp;
-
-                       netif_carrier_on(dev);
-                       for (i = 0; i < phonet_rxq_size; i++)
-                               pn_rx_submit(fp, fp->out_reqv[i], GFP_ATOMIC | __GFP_COLD);
-               }
-               spin_unlock(&port->lock);
-               return 0;
-       }
-
-       return -EINVAL;
-}
-
-static int pn_get_alt(struct usb_function *f, unsigned intf)
-{
-       struct f_phonet *fp = func_to_pn(f);
-
-       if (intf == pn_control_intf_desc.bInterfaceNumber)
-               return 0;
-
-       if (intf == pn_data_intf_desc.bInterfaceNumber) {
-               struct phonet_port *port = netdev_priv(fp->dev);
-               u8 alt;
-
-               spin_lock(&port->lock);
-               alt = port->usb != NULL;
-               spin_unlock(&port->lock);
-               return alt;
-       }
-
-       return -EINVAL;
-}
-
-static void pn_disconnect(struct usb_function *f)
-{
-       struct f_phonet *fp = func_to_pn(f);
-       struct phonet_port *port = netdev_priv(fp->dev);
-       unsigned long flags;
-
-       /* remain disabled until set_alt */
-       spin_lock_irqsave(&port->lock, flags);
-       __pn_reset(f);
-       spin_unlock_irqrestore(&port->lock, flags);
-}
-
-/*-------------------------------------------------------------------------*/
-
-static int pn_bind(struct usb_configuration *c, struct usb_function *f)
-{
-       struct usb_composite_dev *cdev = c->cdev;
-       struct usb_gadget *gadget = cdev->gadget;
-       struct f_phonet *fp = func_to_pn(f);
-       struct usb_ep *ep;
-       int status, i;
-
-       struct f_phonet_opts *phonet_opts;
-
-       phonet_opts = container_of(f->fi, struct f_phonet_opts, func_inst);
-
-       /*
-        * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
-        * configurations are bound in sequence with list_for_each_entry,
-        * in each configuration its functions are bound in sequence
-        * with list_for_each_entry, so we assume no race condition
-        * with regard to phonet_opts->bound access
-        */
-       if (!phonet_opts->bound) {
-               gphonet_set_gadget(phonet_opts->net, gadget);
-               status = gphonet_register_netdev(phonet_opts->net);
-               if (status)
-                       return status;
-               phonet_opts->bound = true;
-       }
-
-       /* Reserve interface IDs */
-       status = usb_interface_id(c, f);
-       if (status < 0)
-               goto err;
-       pn_control_intf_desc.bInterfaceNumber = status;
-       pn_union_desc.bMasterInterface0 = status;
-
-       status = usb_interface_id(c, f);
-       if (status < 0)
-               goto err;
-       pn_data_nop_intf_desc.bInterfaceNumber = status;
-       pn_data_intf_desc.bInterfaceNumber = status;
-       pn_union_desc.bSlaveInterface0 = status;
-
-       /* Reserve endpoints */
-       status = -ENODEV;
-       ep = usb_ep_autoconfig(gadget, &pn_fs_sink_desc);
-       if (!ep)
-               goto err;
-       fp->out_ep = ep;
-       ep->driver_data = fp; /* Claim */
-
-       ep = usb_ep_autoconfig(gadget, &pn_fs_source_desc);
-       if (!ep)
-               goto err;
-       fp->in_ep = ep;
-       ep->driver_data = fp; /* Claim */
-
-       pn_hs_sink_desc.bEndpointAddress = pn_fs_sink_desc.bEndpointAddress;
-       pn_hs_source_desc.bEndpointAddress = pn_fs_source_desc.bEndpointAddress;
-
-       /* Do not try to bind Phonet twice... */
-       status = usb_assign_descriptors(f, fs_pn_function, hs_pn_function,
-                       NULL);
-       if (status)
-               goto err;
-
-       /* Incoming USB requests */
-       status = -ENOMEM;
-       for (i = 0; i < phonet_rxq_size; i++) {
-               struct usb_request *req;
-
-               req = usb_ep_alloc_request(fp->out_ep, GFP_KERNEL);
-               if (!req)
-                       goto err_req;
-
-               req->complete = pn_rx_complete;
-               fp->out_reqv[i] = req;
-       }
-
-       /* Outgoing USB requests */
-       fp->in_req = usb_ep_alloc_request(fp->in_ep, GFP_KERNEL);
-       if (!fp->in_req)
-               goto err_req;
-
-       INFO(cdev, "USB CDC Phonet function\n");
-       INFO(cdev, "using %s, OUT %s, IN %s\n", cdev->gadget->name,
-               fp->out_ep->name, fp->in_ep->name);
-       return 0;
-
-err_req:
-       for (i = 0; i < phonet_rxq_size && fp->out_reqv[i]; i++)
-               usb_ep_free_request(fp->out_ep, fp->out_reqv[i]);
-err:
-       usb_free_all_descriptors(f);
-       if (fp->out_ep)
-               fp->out_ep->driver_data = NULL;
-       if (fp->in_ep)
-               fp->in_ep->driver_data = NULL;
-       ERROR(cdev, "USB CDC Phonet: cannot autoconfigure\n");
-       return status;
-}
-
-static inline struct f_phonet_opts *to_f_phonet_opts(struct config_item *item)
-{
-       return container_of(to_config_group(item), struct f_phonet_opts,
-                       func_inst.group);
-}
-
-CONFIGFS_ATTR_STRUCT(f_phonet_opts);
-static ssize_t f_phonet_attr_show(struct config_item *item,
-                               struct configfs_attribute *attr,
-                               char *page)
-{
-       struct f_phonet_opts *opts = to_f_phonet_opts(item);
-       struct f_phonet_opts_attribute *f_phonet_opts_attr =
-               container_of(attr, struct f_phonet_opts_attribute, attr);
-       ssize_t ret = 0;
-
-       if (f_phonet_opts_attr->show)
-               ret = f_phonet_opts_attr->show(opts, page);
-       return ret;
-}
-
-static void phonet_attr_release(struct config_item *item)
-{
-       struct f_phonet_opts *opts = to_f_phonet_opts(item);
-
-       usb_put_function_instance(&opts->func_inst);
-}
-
-static struct configfs_item_operations phonet_item_ops = {
-       .release                = phonet_attr_release,
-       .show_attribute         = f_phonet_attr_show,
-};
-
-static ssize_t f_phonet_ifname_show(struct f_phonet_opts *opts, char *page)
-{
-       return gether_get_ifname(opts->net, page, PAGE_SIZE);
-}
-
-static struct f_phonet_opts_attribute f_phonet_ifname =
-       __CONFIGFS_ATTR_RO(ifname, f_phonet_ifname_show);
-
-static struct configfs_attribute *phonet_attrs[] = {
-       &f_phonet_ifname.attr,
-       NULL,
-};
-
-static struct config_item_type phonet_func_type = {
-       .ct_item_ops    = &phonet_item_ops,
-       .ct_attrs       = phonet_attrs,
-       .ct_owner       = THIS_MODULE,
-};
-
-static void phonet_free_inst(struct usb_function_instance *f)
-{
-       struct f_phonet_opts *opts;
-
-       opts = container_of(f, struct f_phonet_opts, func_inst);
-       if (opts->bound)
-               gphonet_cleanup(opts->net);
-       else
-               free_netdev(opts->net);
-       kfree(opts);
-}
-
-static struct usb_function_instance *phonet_alloc_inst(void)
-{
-       struct f_phonet_opts *opts;
-
-       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
-       if (!opts)
-               return ERR_PTR(-ENOMEM);
-
-       opts->func_inst.free_func_inst = phonet_free_inst;
-       opts->net = gphonet_setup_default();
-       if (IS_ERR(opts->net)) {
-               struct net_device *net = opts->net;
-               kfree(opts);
-               return ERR_CAST(net);
-       }
-
-       config_group_init_type_name(&opts->func_inst.group, "",
-                       &phonet_func_type);
-
-       return &opts->func_inst;
-}
-
-static void phonet_free(struct usb_function *f)
-{
-       struct f_phonet *phonet;
-
-       phonet = func_to_pn(f);
-       kfree(phonet);
-}
-
-static void pn_unbind(struct usb_configuration *c, struct usb_function *f)
-{
-       struct f_phonet *fp = func_to_pn(f);
-       int i;
-
-       /* We are already disconnected */
-       if (fp->in_req)
-               usb_ep_free_request(fp->in_ep, fp->in_req);
-       for (i = 0; i < phonet_rxq_size; i++)
-               if (fp->out_reqv[i])
-                       usb_ep_free_request(fp->out_ep, fp->out_reqv[i]);
-
-       usb_free_all_descriptors(f);
-}
-
-static struct usb_function *phonet_alloc(struct usb_function_instance *fi)
-{
-       struct f_phonet *fp;
-       struct f_phonet_opts *opts;
-       int size;
-
-       size = sizeof(*fp) + (phonet_rxq_size * sizeof(struct usb_request *));
-       fp = kzalloc(size, GFP_KERNEL);
-       if (!fp)
-               return ERR_PTR(-ENOMEM);
-
-       opts = container_of(fi, struct f_phonet_opts, func_inst);
-
-       fp->dev = opts->net;
-       fp->function.name = "phonet";
-       fp->function.bind = pn_bind;
-       fp->function.unbind = pn_unbind;
-       fp->function.set_alt = pn_set_alt;
-       fp->function.get_alt = pn_get_alt;
-       fp->function.disable = pn_disconnect;
-       fp->function.free_func = phonet_free;
-       spin_lock_init(&fp->rx.lock);
-
-       return &fp->function;
-}
-
-struct net_device *gphonet_setup_default(void)
-{
-       struct net_device *dev;
-       struct phonet_port *port;
-
-       /* Create net device */
-       dev = alloc_netdev(sizeof(*port), "upnlink%d", pn_net_setup);
-       if (!dev)
-               return ERR_PTR(-ENOMEM);
-
-       port = netdev_priv(dev);
-       spin_lock_init(&port->lock);
-       netif_carrier_off(dev);
-
-       return dev;
-}
-
-void gphonet_set_gadget(struct net_device *net, struct usb_gadget *g)
-{
-       SET_NETDEV_DEV(net, &g->dev);
-}
-
-int gphonet_register_netdev(struct net_device *net)
-{
-       int status;
-
-       status = register_netdev(net);
-       if (status)
-               free_netdev(net);
-
-       return status;
-}
-
-void gphonet_cleanup(struct net_device *dev)
-{
-       unregister_netdev(dev);
-}
-
-DECLARE_USB_FUNCTION_INIT(phonet, phonet_alloc_inst, phonet_alloc);
-MODULE_AUTHOR("Rémi Denis-Courmont");
-MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c
deleted file mode 100644 (file)
index eed3ad8..0000000
+++ /dev/null
@@ -1,1029 +0,0 @@
-/*
- * f_rndis.c -- RNDIS link function driver
- *
- * Copyright (C) 2003-2005,2008 David Brownell
- * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
- * Copyright (C) 2008 Nokia Corporation
- * Copyright (C) 2009 Samsung Electronics
- *                    Author: Michal Nazarewicz (mina86@mina86.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-/* #define VERBOSE_DEBUG */
-
-#include <linux/slab.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/etherdevice.h>
-
-#include <linux/atomic.h>
-
-#include "u_ether.h"
-#include "u_ether_configfs.h"
-#include "u_rndis.h"
-#include "rndis.h"
-#include "configfs.h"
-
-/*
- * This function is an RNDIS Ethernet port -- a Microsoft protocol that's
- * been promoted instead of the standard CDC Ethernet.  The published RNDIS
- * spec is ambiguous, incomplete, and needlessly complex.  Variants such as
- * ActiveSync have even worse status in terms of specification.
- *
- * In short:  it's a protocol controlled by (and for) Microsoft, not for an
- * Open ecosystem or markets.  Linux supports it *only* because Microsoft
- * doesn't support the CDC Ethernet standard.
- *
- * The RNDIS data transfer model is complex, with multiple Ethernet packets
- * per USB message, and out of band data.  The control model is built around
- * what's essentially an "RNDIS RPC" protocol.  It's all wrapped in a CDC ACM
- * (modem, not Ethernet) veneer, with those ACM descriptors being entirely
- * useless (they're ignored).  RNDIS expects to be the only function in its
- * configuration, so it's no real help if you need composite devices; and
- * it expects to be the first configuration too.
- *
- * There is a single technical advantage of RNDIS over CDC Ethernet, if you
- * discount the fluff that its RPC can be made to deliver: it doesn't need
- * a NOP altsetting for the data interface.  That lets it work on some of the
- * "so smart it's stupid" hardware which takes over configuration changes
- * from the software, and adds restrictions like "no altsettings".
- *
- * Unfortunately MSFT's RNDIS drivers are buggy.  They hang or oops, and
- * have all sorts of contrary-to-specification oddities that can prevent
- * them from working sanely.  Since bugfixes (or accurate specs, letting
- * Linux work around those bugs) are unlikely to ever come from MSFT, you
- * may want to avoid using RNDIS on purely operational grounds.
- *
- * Omissions from the RNDIS 1.0 specification include:
- *
- *   - Power management ... references data that's scattered around lots
- *     of other documentation, which is incorrect/incomplete there too.
- *
- *   - There are various undocumented protocol requirements, like the need
- *     to send garbage in some control-OUT messages.
- *
- *   - MS-Windows drivers sometimes emit undocumented requests.
- */
-
-struct f_rndis {
-       struct gether                   port;
-       u8                              ctrl_id, data_id;
-       u8                              ethaddr[ETH_ALEN];
-       u32                             vendorID;
-       const char                      *manufacturer;
-       int                             config;
-
-       struct usb_ep                   *notify;
-       struct usb_request              *notify_req;
-       atomic_t                        notify_count;
-};
-
-static inline struct f_rndis *func_to_rndis(struct usb_function *f)
-{
-       return container_of(f, struct f_rndis, port.func);
-}
-
-/* peak (theoretical) bulk transfer rate in bits-per-second */
-static unsigned int bitrate(struct usb_gadget *g)
-{
-       if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
-               return 13 * 1024 * 8 * 1000 * 8;
-       else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
-               return 13 * 512 * 8 * 1000 * 8;
-       else
-               return 19 * 64 * 1 * 1000 * 8;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/*
- */
-
-#define RNDIS_STATUS_INTERVAL_MS       32
-#define STATUS_BYTECOUNT               8       /* 8 bytes data */
-
-
-/* interface descriptor: */
-
-static struct usb_interface_descriptor rndis_control_intf = {
-       .bLength =              sizeof rndis_control_intf,
-       .bDescriptorType =      USB_DT_INTERFACE,
-
-       /* .bInterfaceNumber = DYNAMIC */
-       /* status endpoint is optional; this could be patched later */
-       .bNumEndpoints =        1,
-       .bInterfaceClass =      USB_CLASS_COMM,
-       .bInterfaceSubClass =   USB_CDC_SUBCLASS_ACM,
-       .bInterfaceProtocol =   USB_CDC_ACM_PROTO_VENDOR,
-       /* .iInterface = DYNAMIC */
-};
-
-static struct usb_cdc_header_desc header_desc = {
-       .bLength =              sizeof header_desc,
-       .bDescriptorType =      USB_DT_CS_INTERFACE,
-       .bDescriptorSubType =   USB_CDC_HEADER_TYPE,
-
-       .bcdCDC =               cpu_to_le16(0x0110),
-};
-
-static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor = {
-       .bLength =              sizeof call_mgmt_descriptor,
-       .bDescriptorType =      USB_DT_CS_INTERFACE,
-       .bDescriptorSubType =   USB_CDC_CALL_MANAGEMENT_TYPE,
-
-       .bmCapabilities =       0x00,
-       .bDataInterface =       0x01,
-};
-
-static struct usb_cdc_acm_descriptor rndis_acm_descriptor = {
-       .bLength =              sizeof rndis_acm_descriptor,
-       .bDescriptorType =      USB_DT_CS_INTERFACE,
-       .bDescriptorSubType =   USB_CDC_ACM_TYPE,
-
-       .bmCapabilities =       0x00,
-};
-
-static struct usb_cdc_union_desc rndis_union_desc = {
-       .bLength =              sizeof(rndis_union_desc),
-       .bDescriptorType =      USB_DT_CS_INTERFACE,
-       .bDescriptorSubType =   USB_CDC_UNION_TYPE,
-       /* .bMasterInterface0 = DYNAMIC */
-       /* .bSlaveInterface0 =  DYNAMIC */
-};
-
-/* the data interface has two bulk endpoints */
-
-static struct usb_interface_descriptor rndis_data_intf = {
-       .bLength =              sizeof rndis_data_intf,
-       .bDescriptorType =      USB_DT_INTERFACE,
-
-       /* .bInterfaceNumber = DYNAMIC */
-       .bNumEndpoints =        2,
-       .bInterfaceClass =      USB_CLASS_CDC_DATA,
-       .bInterfaceSubClass =   0,
-       .bInterfaceProtocol =   0,
-       /* .iInterface = DYNAMIC */
-};
-
-
-static struct usb_interface_assoc_descriptor
-rndis_iad_descriptor = {
-       .bLength =              sizeof rndis_iad_descriptor,
-       .bDescriptorType =      USB_DT_INTERFACE_ASSOCIATION,
-
-       .bFirstInterface =      0, /* XXX, hardcoded */
-       .bInterfaceCount =      2,      // control + data
-       .bFunctionClass =       USB_CLASS_COMM,
-       .bFunctionSubClass =    USB_CDC_SUBCLASS_ETHERNET,
-       .bFunctionProtocol =    USB_CDC_PROTO_NONE,
-       /* .iFunction = DYNAMIC */
-};
-
-/* full speed support: */
-
-static struct usb_endpoint_descriptor fs_notify_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_INT,
-       .wMaxPacketSize =       cpu_to_le16(STATUS_BYTECOUNT),
-       .bInterval =            RNDIS_STATUS_INTERVAL_MS,
-};
-
-static struct usb_endpoint_descriptor fs_in_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-};
-
-static struct usb_endpoint_descriptor fs_out_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_OUT,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-};
-
-static struct usb_descriptor_header *eth_fs_function[] = {
-       (struct usb_descriptor_header *) &rndis_iad_descriptor,
-
-       /* control interface matches ACM, not Ethernet */
-       (struct usb_descriptor_header *) &rndis_control_intf,
-       (struct usb_descriptor_header *) &header_desc,
-       (struct usb_descriptor_header *) &call_mgmt_descriptor,
-       (struct usb_descriptor_header *) &rndis_acm_descriptor,
-       (struct usb_descriptor_header *) &rndis_union_desc,
-       (struct usb_descriptor_header *) &fs_notify_desc,
-
-       /* data interface has no altsetting */
-       (struct usb_descriptor_header *) &rndis_data_intf,
-       (struct usb_descriptor_header *) &fs_in_desc,
-       (struct usb_descriptor_header *) &fs_out_desc,
-       NULL,
-};
-
-/* high speed support: */
-
-static struct usb_endpoint_descriptor hs_notify_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_INT,
-       .wMaxPacketSize =       cpu_to_le16(STATUS_BYTECOUNT),
-       .bInterval =            USB_MS_TO_HS_INTERVAL(RNDIS_STATUS_INTERVAL_MS)
-};
-
-static struct usb_endpoint_descriptor hs_in_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(512),
-};
-
-static struct usb_endpoint_descriptor hs_out_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_OUT,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(512),
-};
-
-static struct usb_descriptor_header *eth_hs_function[] = {
-       (struct usb_descriptor_header *) &rndis_iad_descriptor,
-
-       /* control interface matches ACM, not Ethernet */
-       (struct usb_descriptor_header *) &rndis_control_intf,
-       (struct usb_descriptor_header *) &header_desc,
-       (struct usb_descriptor_header *) &call_mgmt_descriptor,
-       (struct usb_descriptor_header *) &rndis_acm_descriptor,
-       (struct usb_descriptor_header *) &rndis_union_desc,
-       (struct usb_descriptor_header *) &hs_notify_desc,
-
-       /* data interface has no altsetting */
-       (struct usb_descriptor_header *) &rndis_data_intf,
-       (struct usb_descriptor_header *) &hs_in_desc,
-       (struct usb_descriptor_header *) &hs_out_desc,
-       NULL,
-};
-
-/* super speed support: */
-
-static struct usb_endpoint_descriptor ss_notify_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_INT,
-       .wMaxPacketSize =       cpu_to_le16(STATUS_BYTECOUNT),
-       .bInterval =            USB_MS_TO_HS_INTERVAL(RNDIS_STATUS_INTERVAL_MS)
-};
-
-static struct usb_ss_ep_comp_descriptor ss_intr_comp_desc = {
-       .bLength =              sizeof ss_intr_comp_desc,
-       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
-
-       /* the following 3 values can be tweaked if necessary */
-       /* .bMaxBurst =         0, */
-       /* .bmAttributes =      0, */
-       .wBytesPerInterval =    cpu_to_le16(STATUS_BYTECOUNT),
-};
-
-static struct usb_endpoint_descriptor ss_in_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(1024),
-};
-
-static struct usb_endpoint_descriptor ss_out_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_OUT,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(1024),
-};
-
-static struct usb_ss_ep_comp_descriptor ss_bulk_comp_desc = {
-       .bLength =              sizeof ss_bulk_comp_desc,
-       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
-
-       /* the following 2 values can be tweaked if necessary */
-       /* .bMaxBurst =         0, */
-       /* .bmAttributes =      0, */
-};
-
-static struct usb_descriptor_header *eth_ss_function[] = {
-       (struct usb_descriptor_header *) &rndis_iad_descriptor,
-
-       /* control interface matches ACM, not Ethernet */
-       (struct usb_descriptor_header *) &rndis_control_intf,
-       (struct usb_descriptor_header *) &header_desc,
-       (struct usb_descriptor_header *) &call_mgmt_descriptor,
-       (struct usb_descriptor_header *) &rndis_acm_descriptor,
-       (struct usb_descriptor_header *) &rndis_union_desc,
-       (struct usb_descriptor_header *) &ss_notify_desc,
-       (struct usb_descriptor_header *) &ss_intr_comp_desc,
-
-       /* data interface has no altsetting */
-       (struct usb_descriptor_header *) &rndis_data_intf,
-       (struct usb_descriptor_header *) &ss_in_desc,
-       (struct usb_descriptor_header *) &ss_bulk_comp_desc,
-       (struct usb_descriptor_header *) &ss_out_desc,
-       (struct usb_descriptor_header *) &ss_bulk_comp_desc,
-       NULL,
-};
-
-/* string descriptors: */
-
-static struct usb_string rndis_string_defs[] = {
-       [0].s = "RNDIS Communications Control",
-       [1].s = "RNDIS Ethernet Data",
-       [2].s = "RNDIS",
-       {  } /* end of list */
-};
-
-static struct usb_gadget_strings rndis_string_table = {
-       .language =             0x0409, /* en-us */
-       .strings =              rndis_string_defs,
-};
-
-static struct usb_gadget_strings *rndis_strings[] = {
-       &rndis_string_table,
-       NULL,
-};
-
-/*-------------------------------------------------------------------------*/
-
-static struct sk_buff *rndis_add_header(struct gether *port,
-                                       struct sk_buff *skb)
-{
-       struct sk_buff *skb2;
-
-       skb2 = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type));
-       if (skb2)
-               rndis_add_hdr(skb2);
-
-       dev_kfree_skb(skb);
-       return skb2;
-}
-
-static void rndis_response_available(void *_rndis)
-{
-       struct f_rndis                  *rndis = _rndis;
-       struct usb_request              *req = rndis->notify_req;
-       struct usb_composite_dev        *cdev = rndis->port.func.config->cdev;
-       __le32                          *data = req->buf;
-       int                             status;
-
-       if (atomic_inc_return(&rndis->notify_count) != 1)
-               return;
-
-       /* Send RNDIS RESPONSE_AVAILABLE notification; a
-        * USB_CDC_NOTIFY_RESPONSE_AVAILABLE "should" work too
-        *
-        * This is the only notification defined by RNDIS.
-        */
-       data[0] = cpu_to_le32(1);
-       data[1] = cpu_to_le32(0);
-
-       status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC);
-       if (status) {
-               atomic_dec(&rndis->notify_count);
-               DBG(cdev, "notify/0 --> %d\n", status);
-       }
-}
-
-static void rndis_response_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       struct f_rndis                  *rndis = req->context;
-       struct usb_composite_dev        *cdev = rndis->port.func.config->cdev;
-       int                             status = req->status;
-
-       /* after TX:
-        *  - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control)
-        *  - RNDIS_RESPONSE_AVAILABLE (status/irq)
-        */
-       switch (status) {
-       case -ECONNRESET:
-       case -ESHUTDOWN:
-               /* connection gone */
-               atomic_set(&rndis->notify_count, 0);
-               break;
-       default:
-               DBG(cdev, "RNDIS %s response error %d, %d/%d\n",
-                       ep->name, status,
-                       req->actual, req->length);
-               /* FALLTHROUGH */
-       case 0:
-               if (ep != rndis->notify)
-                       break;
-
-               /* handle multiple pending RNDIS_RESPONSE_AVAILABLE
-                * notifications by resending until we're done
-                */
-               if (atomic_dec_and_test(&rndis->notify_count))
-                       break;
-               status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC);
-               if (status) {
-                       atomic_dec(&rndis->notify_count);
-                       DBG(cdev, "notify/1 --> %d\n", status);
-               }
-               break;
-       }
-}
-
-static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       struct f_rndis                  *rndis = req->context;
-       int                             status;
-
-       /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
-//     spin_lock(&dev->lock);
-       status = rndis_msg_parser(rndis->config, (u8 *) req->buf);
-       if (status < 0)
-               pr_err("RNDIS command error %d, %d/%d\n",
-                       status, req->actual, req->length);
-//     spin_unlock(&dev->lock);
-}
-
-static int
-rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
-{
-       struct f_rndis          *rndis = func_to_rndis(f);
-       struct usb_composite_dev *cdev = f->config->cdev;
-       struct usb_request      *req = cdev->req;
-       int                     value = -EOPNOTSUPP;
-       u16                     w_index = le16_to_cpu(ctrl->wIndex);
-       u16                     w_value = le16_to_cpu(ctrl->wValue);
-       u16                     w_length = le16_to_cpu(ctrl->wLength);
-
-       /* composite driver infrastructure handles everything except
-        * CDC class messages; interface activation uses set_alt().
-        */
-       switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
-
-       /* RNDIS uses the CDC command encapsulation mechanism to implement
-        * an RPC scheme, with much getting/setting of attributes by OID.
-        */
-       case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
-                       | USB_CDC_SEND_ENCAPSULATED_COMMAND:
-               if (w_value || w_index != rndis->ctrl_id)
-                       goto invalid;
-               /* read the request; process it later */
-               value = w_length;
-               req->complete = rndis_command_complete;
-               req->context = rndis;
-               /* later, rndis_response_available() sends a notification */
-               break;
-
-       case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
-                       | USB_CDC_GET_ENCAPSULATED_RESPONSE:
-               if (w_value || w_index != rndis->ctrl_id)
-                       goto invalid;
-               else {
-                       u8 *buf;
-                       u32 n;
-
-                       /* return the result */
-                       buf = rndis_get_next_response(rndis->config, &n);
-                       if (buf) {
-                               memcpy(req->buf, buf, n);
-                               req->complete = rndis_response_complete;
-                               req->context = rndis;
-                               rndis_free_response(rndis->config, buf);
-                               value = n;
-                       }
-                       /* else stalls ... spec says to avoid that */
-               }
-               break;
-
-       default:
-invalid:
-               VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
-                       ctrl->bRequestType, ctrl->bRequest,
-                       w_value, w_index, w_length);
-       }
-
-       /* respond with data transfer or status phase? */
-       if (value >= 0) {
-               DBG(cdev, "rndis req%02x.%02x v%04x i%04x l%d\n",
-                       ctrl->bRequestType, ctrl->bRequest,
-                       w_value, w_index, w_length);
-               req->zero = (value < w_length);
-               req->length = value;
-               value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
-               if (value < 0)
-                       ERROR(cdev, "rndis response on err %d\n", value);
-       }
-
-       /* device either stalls (value < 0) or reports success */
-       return value;
-}
-
-
-static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
-{
-       struct f_rndis          *rndis = func_to_rndis(f);
-       struct usb_composite_dev *cdev = f->config->cdev;
-
-       /* we know alt == 0 */
-
-       if (intf == rndis->ctrl_id) {
-               if (rndis->notify->driver_data) {
-                       VDBG(cdev, "reset rndis control %d\n", intf);
-                       usb_ep_disable(rndis->notify);
-               }
-               if (!rndis->notify->desc) {
-                       VDBG(cdev, "init rndis ctrl %d\n", intf);
-                       if (config_ep_by_speed(cdev->gadget, f, rndis->notify))
-                               goto fail;
-               }
-               usb_ep_enable(rndis->notify);
-               rndis->notify->driver_data = rndis;
-
-       } else if (intf == rndis->data_id) {
-               struct net_device       *net;
-
-               if (rndis->port.in_ep->driver_data) {
-                       DBG(cdev, "reset rndis\n");
-                       gether_disconnect(&rndis->port);
-               }
-
-               if (!rndis->port.in_ep->desc || !rndis->port.out_ep->desc) {
-                       DBG(cdev, "init rndis\n");
-                       if (config_ep_by_speed(cdev->gadget, f,
-                                              rndis->port.in_ep) ||
-                           config_ep_by_speed(cdev->gadget, f,
-                                              rndis->port.out_ep)) {
-                               rndis->port.in_ep->desc = NULL;
-                               rndis->port.out_ep->desc = NULL;
-                               goto fail;
-                       }
-               }
-
-               /* Avoid ZLPs; they can be troublesome. */
-               rndis->port.is_zlp_ok = false;
-
-               /* RNDIS should be in the "RNDIS uninitialized" state,
-                * either never activated or after rndis_uninit().
-                *
-                * We don't want data to flow here until a nonzero packet
-                * filter is set, at which point it enters "RNDIS data
-                * initialized" state ... but we do want the endpoints
-                * to be activated.  It's a strange little state.
-                *
-                * REVISIT the RNDIS gadget code has done this wrong for a
-                * very long time.  We need another call to the link layer
-                * code -- gether_updown(...bool) maybe -- to do it right.
-                */
-               rndis->port.cdc_filter = 0;
-
-               DBG(cdev, "RNDIS RX/TX early activation ... \n");
-               net = gether_connect(&rndis->port);
-               if (IS_ERR(net))
-                       return PTR_ERR(net);
-
-               rndis_set_param_dev(rndis->config, net,
-                               &rndis->port.cdc_filter);
-       } else
-               goto fail;
-
-       return 0;
-fail:
-       return -EINVAL;
-}
-
-static void rndis_disable(struct usb_function *f)
-{
-       struct f_rndis          *rndis = func_to_rndis(f);
-       struct usb_composite_dev *cdev = f->config->cdev;
-
-       if (!rndis->notify->driver_data)
-               return;
-
-       DBG(cdev, "rndis deactivated\n");
-
-       rndis_uninit(rndis->config);
-       gether_disconnect(&rndis->port);
-
-       usb_ep_disable(rndis->notify);
-       rndis->notify->driver_data = NULL;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * This isn't quite the same mechanism as CDC Ethernet, since the
- * notification scheme passes less data, but the same set of link
- * states must be tested.  A key difference is that altsettings are
- * not used to tell whether the link should send packets or not.
- */
-
-static void rndis_open(struct gether *geth)
-{
-       struct f_rndis          *rndis = func_to_rndis(&geth->func);
-       struct usb_composite_dev *cdev = geth->func.config->cdev;
-
-       DBG(cdev, "%s\n", __func__);
-
-       rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3,
-                               bitrate(cdev->gadget) / 100);
-       rndis_signal_connect(rndis->config);
-}
-
-static void rndis_close(struct gether *geth)
-{
-       struct f_rndis          *rndis = func_to_rndis(&geth->func);
-
-       DBG(geth->func.config->cdev, "%s\n", __func__);
-
-       rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0);
-       rndis_signal_disconnect(rndis->config);
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* Some controllers can't support RNDIS ... */
-static inline bool can_support_rndis(struct usb_configuration *c)
-{
-       /* everything else is *presumably* fine */
-       return true;
-}
-
-/* ethernet function driver setup/binding */
-
-static int
-rndis_bind(struct usb_configuration *c, struct usb_function *f)
-{
-       struct usb_composite_dev *cdev = c->cdev;
-       struct f_rndis          *rndis = func_to_rndis(f);
-       struct usb_string       *us;
-       int                     status;
-       struct usb_ep           *ep;
-
-       struct f_rndis_opts *rndis_opts;
-
-       if (!can_support_rndis(c))
-               return -EINVAL;
-
-       rndis_opts = container_of(f->fi, struct f_rndis_opts, func_inst);
-
-       if (cdev->use_os_string) {
-               f->os_desc_table = kzalloc(sizeof(*f->os_desc_table),
-                                          GFP_KERNEL);
-               if (!f->os_desc_table)
-                       return PTR_ERR(f->os_desc_table);
-               f->os_desc_n = 1;
-               f->os_desc_table[0].os_desc = &rndis_opts->rndis_os_desc;
-       }
-
-       /*
-        * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
-        * configurations are bound in sequence with list_for_each_entry,
-        * in each configuration its functions are bound in sequence
-        * with list_for_each_entry, so we assume no race condition
-        * with regard to rndis_opts->bound access
-        */
-       if (!rndis_opts->bound) {
-               gether_set_gadget(rndis_opts->net, cdev->gadget);
-               status = gether_register_netdev(rndis_opts->net);
-               if (status)
-                       goto fail;
-               rndis_opts->bound = true;
-       }
-
-       us = usb_gstrings_attach(cdev, rndis_strings,
-                                ARRAY_SIZE(rndis_string_defs));
-       if (IS_ERR(us)) {
-               status = PTR_ERR(us);
-               goto fail;
-       }
-       rndis_control_intf.iInterface = us[0].id;
-       rndis_data_intf.iInterface = us[1].id;
-       rndis_iad_descriptor.iFunction = us[2].id;
-
-       /* allocate instance-specific interface IDs */
-       status = usb_interface_id(c, f);
-       if (status < 0)
-               goto fail;
-       rndis->ctrl_id = status;
-       rndis_iad_descriptor.bFirstInterface = status;
-
-       rndis_control_intf.bInterfaceNumber = status;
-       rndis_union_desc.bMasterInterface0 = status;
-
-       status = usb_interface_id(c, f);
-       if (status < 0)
-               goto fail;
-       rndis->data_id = status;
-
-       rndis_data_intf.bInterfaceNumber = status;
-       rndis_union_desc.bSlaveInterface0 = status;
-
-       status = -ENODEV;
-
-       /* allocate instance-specific endpoints */
-       ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc);
-       if (!ep)
-               goto fail;
-       rndis->port.in_ep = ep;
-       ep->driver_data = cdev; /* claim */
-
-       ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc);
-       if (!ep)
-               goto fail;
-       rndis->port.out_ep = ep;
-       ep->driver_data = cdev; /* claim */
-
-       /* NOTE:  a status/notification endpoint is, strictly speaking,
-        * optional.  We don't treat it that way though!  It's simpler,
-        * and some newer profiles don't treat it as optional.
-        */
-       ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc);
-       if (!ep)
-               goto fail;
-       rndis->notify = ep;
-       ep->driver_data = cdev; /* claim */
-
-       status = -ENOMEM;
-
-       /* allocate notification request and buffer */
-       rndis->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
-       if (!rndis->notify_req)
-               goto fail;
-       rndis->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL);
-       if (!rndis->notify_req->buf)
-               goto fail;
-       rndis->notify_req->length = STATUS_BYTECOUNT;
-       rndis->notify_req->context = rndis;
-       rndis->notify_req->complete = rndis_response_complete;
-
-       /* support all relevant hardware speeds... we expect that when
-        * hardware is dual speed, all bulk-capable endpoints work at
-        * both speeds
-        */
-       hs_in_desc.bEndpointAddress = fs_in_desc.bEndpointAddress;
-       hs_out_desc.bEndpointAddress = fs_out_desc.bEndpointAddress;
-       hs_notify_desc.bEndpointAddress = fs_notify_desc.bEndpointAddress;
-
-       ss_in_desc.bEndpointAddress = fs_in_desc.bEndpointAddress;
-       ss_out_desc.bEndpointAddress = fs_out_desc.bEndpointAddress;
-       ss_notify_desc.bEndpointAddress = fs_notify_desc.bEndpointAddress;
-
-       status = usb_assign_descriptors(f, eth_fs_function, eth_hs_function,
-                       eth_ss_function);
-       if (status)
-               goto fail;
-
-       rndis->port.open = rndis_open;
-       rndis->port.close = rndis_close;
-
-       rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0);
-       rndis_set_host_mac(rndis->config, rndis->ethaddr);
-
-       if (rndis->manufacturer && rndis->vendorID &&
-                       rndis_set_param_vendor(rndis->config, rndis->vendorID,
-                                              rndis->manufacturer))
-               goto fail;
-
-       /* NOTE:  all that is done without knowing or caring about
-        * the network link ... which is unavailable to this code
-        * until we're activated via set_alt().
-        */
-
-       DBG(cdev, "RNDIS: %s speed IN/%s OUT/%s NOTIFY/%s\n",
-                       gadget_is_superspeed(c->cdev->gadget) ? "super" :
-                       gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
-                       rndis->port.in_ep->name, rndis->port.out_ep->name,
-                       rndis->notify->name);
-       return 0;
-
-fail:
-       kfree(f->os_desc_table);
-       f->os_desc_n = 0;
-       usb_free_all_descriptors(f);
-
-       if (rndis->notify_req) {
-               kfree(rndis->notify_req->buf);
-               usb_ep_free_request(rndis->notify, rndis->notify_req);
-       }
-
-       /* we might as well release our claims on endpoints */
-       if (rndis->notify)
-               rndis->notify->driver_data = NULL;
-       if (rndis->port.out_ep)
-               rndis->port.out_ep->driver_data = NULL;
-       if (rndis->port.in_ep)
-               rndis->port.in_ep->driver_data = NULL;
-
-       ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
-
-       return status;
-}
-
-void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net)
-{
-       struct f_rndis_opts *opts;
-
-       opts = container_of(f, struct f_rndis_opts, func_inst);
-       if (opts->bound)
-               gether_cleanup(netdev_priv(opts->net));
-       else
-               free_netdev(opts->net);
-       opts->borrowed_net = opts->bound = true;
-       opts->net = net;
-}
-EXPORT_SYMBOL_GPL(rndis_borrow_net);
-
-static inline struct f_rndis_opts *to_f_rndis_opts(struct config_item *item)
-{
-       return container_of(to_config_group(item), struct f_rndis_opts,
-                           func_inst.group);
-}
-
-/* f_rndis_item_ops */
-USB_ETHERNET_CONFIGFS_ITEM(rndis);
-
-/* f_rndis_opts_dev_addr */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(rndis);
-
-/* f_rndis_opts_host_addr */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(rndis);
-
-/* f_rndis_opts_qmult */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(rndis);
-
-/* f_rndis_opts_ifname */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(rndis);
-
-static struct configfs_attribute *rndis_attrs[] = {
-       &f_rndis_opts_dev_addr.attr,
-       &f_rndis_opts_host_addr.attr,
-       &f_rndis_opts_qmult.attr,
-       &f_rndis_opts_ifname.attr,
-       NULL,
-};
-
-static struct config_item_type rndis_func_type = {
-       .ct_item_ops    = &rndis_item_ops,
-       .ct_attrs       = rndis_attrs,
-       .ct_owner       = THIS_MODULE,
-};
-
-static void rndis_free_inst(struct usb_function_instance *f)
-{
-       struct f_rndis_opts *opts;
-
-       opts = container_of(f, struct f_rndis_opts, func_inst);
-       if (!opts->borrowed_net) {
-               if (opts->bound)
-                       gether_cleanup(netdev_priv(opts->net));
-               else
-                       free_netdev(opts->net);
-       }
-
-       kfree(opts->rndis_os_desc.group.default_groups); /* single VLA chunk */
-       kfree(opts);
-}
-
-static struct usb_function_instance *rndis_alloc_inst(void)
-{
-       struct f_rndis_opts *opts;
-       struct usb_os_desc *descs[1];
-
-       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
-       if (!opts)
-               return ERR_PTR(-ENOMEM);
-       opts->rndis_os_desc.ext_compat_id = opts->rndis_ext_compat_id;
-
-       mutex_init(&opts->lock);
-       opts->func_inst.free_func_inst = rndis_free_inst;
-       opts->net = gether_setup_default();
-       if (IS_ERR(opts->net)) {
-               struct net_device *net = opts->net;
-               kfree(opts);
-               return ERR_CAST(net);
-       }
-       INIT_LIST_HEAD(&opts->rndis_os_desc.ext_prop);
-
-       descs[0] = &opts->rndis_os_desc;
-       usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs,
-                                      THIS_MODULE);
-       config_group_init_type_name(&opts->func_inst.group, "",
-                                   &rndis_func_type);
-
-       return &opts->func_inst;
-}
-
-static void rndis_free(struct usb_function *f)
-{
-       struct f_rndis *rndis;
-       struct f_rndis_opts *opts;
-
-       rndis = func_to_rndis(f);
-       rndis_deregister(rndis->config);
-       opts = container_of(f->fi, struct f_rndis_opts, func_inst);
-       kfree(rndis);
-       mutex_lock(&opts->lock);
-       opts->refcnt--;
-       mutex_unlock(&opts->lock);
-}
-
-static void rndis_unbind(struct usb_configuration *c, struct usb_function *f)
-{
-       struct f_rndis          *rndis = func_to_rndis(f);
-
-       kfree(f->os_desc_table);
-       f->os_desc_n = 0;
-       usb_free_all_descriptors(f);
-
-       kfree(rndis->notify_req->buf);
-       usb_ep_free_request(rndis->notify, rndis->notify_req);
-}
-
-static struct usb_function *rndis_alloc(struct usb_function_instance *fi)
-{
-       struct f_rndis  *rndis;
-       struct f_rndis_opts *opts;
-       int status;
-
-       /* allocate and initialize one new instance */
-       rndis = kzalloc(sizeof(*rndis), GFP_KERNEL);
-       if (!rndis)
-               return ERR_PTR(-ENOMEM);
-
-       opts = container_of(fi, struct f_rndis_opts, func_inst);
-       mutex_lock(&opts->lock);
-       opts->refcnt++;
-
-       gether_get_host_addr_u8(opts->net, rndis->ethaddr);
-       rndis->vendorID = opts->vendor_id;
-       rndis->manufacturer = opts->manufacturer;
-
-       rndis->port.ioport = netdev_priv(opts->net);
-       mutex_unlock(&opts->lock);
-       /* RNDIS activates when the host changes this filter */
-       rndis->port.cdc_filter = 0;
-
-       /* RNDIS has special (and complex) framing */
-       rndis->port.header_len = sizeof(struct rndis_packet_msg_type);
-       rndis->port.wrap = rndis_add_header;
-       rndis->port.unwrap = rndis_rm_hdr;
-
-       rndis->port.func.name = "rndis";
-       /* descriptors are per-instance copies */
-       rndis->port.func.bind = rndis_bind;
-       rndis->port.func.unbind = rndis_unbind;
-       rndis->port.func.set_alt = rndis_set_alt;
-       rndis->port.func.setup = rndis_setup;
-       rndis->port.func.disable = rndis_disable;
-       rndis->port.func.free_func = rndis_free;
-
-       status = rndis_register(rndis_response_available, rndis);
-       if (status < 0) {
-               kfree(rndis);
-               return ERR_PTR(status);
-       }
-       rndis->config = status;
-
-       return &rndis->port.func;
-}
-
-DECLARE_USB_FUNCTION(rndis, rndis_alloc_inst, rndis_alloc);
-
-static int __init rndis_mod_init(void)
-{
-       int ret;
-
-       ret = rndis_init();
-       if (ret)
-               return ret;
-
-       return usb_function_register(&rndisusb_func);
-}
-module_init(rndis_mod_init);
-
-static void __exit rndis_mod_exit(void)
-{
-       usb_function_unregister(&rndisusb_func);
-       rndis_exit();
-}
-module_exit(rndis_mod_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c
deleted file mode 100644 (file)
index 9ecbcbf..0000000
+++ /dev/null
@@ -1,385 +0,0 @@
-/*
- * f_serial.c - generic USB serial function driver
- *
- * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
- * Copyright (C) 2008 by David Brownell
- * Copyright (C) 2008 by Nokia Corporation
- *
- * This software is distributed under the terms of the GNU General
- * Public License ("GPL") as published by the Free Software Foundation,
- * either version 2 of that License or (at your option) any later version.
- */
-
-#include <linux/slab.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/device.h>
-
-#include "u_serial.h"
-#include "gadget_chips.h"
-
-
-/*
- * This function packages a simple "generic serial" port with no real
- * control mechanisms, just raw data transfer over two bulk endpoints.
- *
- * Because it's not standardized, this isn't as interoperable as the
- * CDC ACM driver.  However, for many purposes it's just as functional
- * if you can arrange appropriate host side drivers.
- */
-
-struct f_gser {
-       struct gserial                  port;
-       u8                              data_id;
-       u8                              port_num;
-};
-
-static inline struct f_gser *func_to_gser(struct usb_function *f)
-{
-       return container_of(f, struct f_gser, port.func);
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* interface descriptor: */
-
-static struct usb_interface_descriptor gser_interface_desc = {
-       .bLength =              USB_DT_INTERFACE_SIZE,
-       .bDescriptorType =      USB_DT_INTERFACE,
-       /* .bInterfaceNumber = DYNAMIC */
-       .bNumEndpoints =        2,
-       .bInterfaceClass =      USB_CLASS_VENDOR_SPEC,
-       .bInterfaceSubClass =   0,
-       .bInterfaceProtocol =   0,
-       /* .iInterface = DYNAMIC */
-};
-
-/* full speed support: */
-
-static struct usb_endpoint_descriptor gser_fs_in_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-};
-
-static struct usb_endpoint_descriptor gser_fs_out_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-       .bEndpointAddress =     USB_DIR_OUT,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-};
-
-static struct usb_descriptor_header *gser_fs_function[] = {
-       (struct usb_descriptor_header *) &gser_interface_desc,
-       (struct usb_descriptor_header *) &gser_fs_in_desc,
-       (struct usb_descriptor_header *) &gser_fs_out_desc,
-       NULL,
-};
-
-/* high speed support: */
-
-static struct usb_endpoint_descriptor gser_hs_in_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(512),
-};
-
-static struct usb_endpoint_descriptor gser_hs_out_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(512),
-};
-
-static struct usb_descriptor_header *gser_hs_function[] = {
-       (struct usb_descriptor_header *) &gser_interface_desc,
-       (struct usb_descriptor_header *) &gser_hs_in_desc,
-       (struct usb_descriptor_header *) &gser_hs_out_desc,
-       NULL,
-};
-
-static struct usb_endpoint_descriptor gser_ss_in_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(1024),
-};
-
-static struct usb_endpoint_descriptor gser_ss_out_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(1024),
-};
-
-static struct usb_ss_ep_comp_descriptor gser_ss_bulk_comp_desc = {
-       .bLength =              sizeof gser_ss_bulk_comp_desc,
-       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
-};
-
-static struct usb_descriptor_header *gser_ss_function[] = {
-       (struct usb_descriptor_header *) &gser_interface_desc,
-       (struct usb_descriptor_header *) &gser_ss_in_desc,
-       (struct usb_descriptor_header *) &gser_ss_bulk_comp_desc,
-       (struct usb_descriptor_header *) &gser_ss_out_desc,
-       (struct usb_descriptor_header *) &gser_ss_bulk_comp_desc,
-       NULL,
-};
-
-/* string descriptors: */
-
-static struct usb_string gser_string_defs[] = {
-       [0].s = "Generic Serial",
-       {  } /* end of list */
-};
-
-static struct usb_gadget_strings gser_string_table = {
-       .language =             0x0409, /* en-us */
-       .strings =              gser_string_defs,
-};
-
-static struct usb_gadget_strings *gser_strings[] = {
-       &gser_string_table,
-       NULL,
-};
-
-/*-------------------------------------------------------------------------*/
-
-static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
-{
-       struct f_gser           *gser = func_to_gser(f);
-       struct usb_composite_dev *cdev = f->config->cdev;
-
-       /* we know alt == 0, so this is an activation or a reset */
-
-       if (gser->port.in->driver_data) {
-               DBG(cdev, "reset generic ttyGS%d\n", gser->port_num);
-               gserial_disconnect(&gser->port);
-       }
-       if (!gser->port.in->desc || !gser->port.out->desc) {
-               DBG(cdev, "activate generic ttyGS%d\n", gser->port_num);
-               if (config_ep_by_speed(cdev->gadget, f, gser->port.in) ||
-                   config_ep_by_speed(cdev->gadget, f, gser->port.out)) {
-                       gser->port.in->desc = NULL;
-                       gser->port.out->desc = NULL;
-                       return -EINVAL;
-               }
-       }
-       gserial_connect(&gser->port, gser->port_num);
-       return 0;
-}
-
-static void gser_disable(struct usb_function *f)
-{
-       struct f_gser   *gser = func_to_gser(f);
-       struct usb_composite_dev *cdev = f->config->cdev;
-
-       DBG(cdev, "generic ttyGS%d deactivated\n", gser->port_num);
-       gserial_disconnect(&gser->port);
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* serial function driver setup/binding */
-
-static int gser_bind(struct usb_configuration *c, struct usb_function *f)
-{
-       struct usb_composite_dev *cdev = c->cdev;
-       struct f_gser           *gser = func_to_gser(f);
-       int                     status;
-       struct usb_ep           *ep;
-
-       /* REVISIT might want instance-specific strings to help
-        * distinguish instances ...
-        */
-
-       /* maybe allocate device-global string ID */
-       if (gser_string_defs[0].id == 0) {
-               status = usb_string_id(c->cdev);
-               if (status < 0)
-                       return status;
-               gser_string_defs[0].id = status;
-       }
-
-       /* allocate instance-specific interface IDs */
-       status = usb_interface_id(c, f);
-       if (status < 0)
-               goto fail;
-       gser->data_id = status;
-       gser_interface_desc.bInterfaceNumber = status;
-
-       status = -ENODEV;
-
-       /* allocate instance-specific endpoints */
-       ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_in_desc);
-       if (!ep)
-               goto fail;
-       gser->port.in = ep;
-       ep->driver_data = cdev; /* claim */
-
-       ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_out_desc);
-       if (!ep)
-               goto fail;
-       gser->port.out = ep;
-       ep->driver_data = cdev; /* claim */
-
-       /* support all relevant hardware speeds... we expect that when
-        * hardware is dual speed, all bulk-capable endpoints work at
-        * both speeds
-        */
-       gser_hs_in_desc.bEndpointAddress = gser_fs_in_desc.bEndpointAddress;
-       gser_hs_out_desc.bEndpointAddress = gser_fs_out_desc.bEndpointAddress;
-
-       gser_ss_in_desc.bEndpointAddress = gser_fs_in_desc.bEndpointAddress;
-       gser_ss_out_desc.bEndpointAddress = gser_fs_out_desc.bEndpointAddress;
-
-       status = usb_assign_descriptors(f, gser_fs_function, gser_hs_function,
-                       gser_ss_function);
-       if (status)
-               goto fail;
-       DBG(cdev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n",
-                       gser->port_num,
-                       gadget_is_superspeed(c->cdev->gadget) ? "super" :
-                       gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
-                       gser->port.in->name, gser->port.out->name);
-       return 0;
-
-fail:
-       /* we might as well release our claims on endpoints */
-       if (gser->port.out)
-               gser->port.out->driver_data = NULL;
-       if (gser->port.in)
-               gser->port.in->driver_data = NULL;
-
-       ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
-
-       return status;
-}
-
-static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item)
-{
-       return container_of(to_config_group(item), struct f_serial_opts,
-                           func_inst.group);
-}
-
-CONFIGFS_ATTR_STRUCT(f_serial_opts);
-static ssize_t f_serial_attr_show(struct config_item *item,
-                                 struct configfs_attribute *attr,
-                                 char *page)
-{
-       struct f_serial_opts *opts = to_f_serial_opts(item);
-       struct f_serial_opts_attribute *f_serial_opts_attr =
-               container_of(attr, struct f_serial_opts_attribute, attr);
-       ssize_t ret = 0;
-
-       if (f_serial_opts_attr->show)
-               ret = f_serial_opts_attr->show(opts, page);
-
-       return ret;
-}
-
-static void serial_attr_release(struct config_item *item)
-{
-       struct f_serial_opts *opts = to_f_serial_opts(item);
-
-       usb_put_function_instance(&opts->func_inst);
-}
-
-static struct configfs_item_operations serial_item_ops = {
-       .release        = serial_attr_release,
-       .show_attribute = f_serial_attr_show,
-};
-
-static ssize_t f_serial_port_num_show(struct f_serial_opts *opts, char *page)
-{
-       return sprintf(page, "%u\n", opts->port_num);
-}
-
-static struct f_serial_opts_attribute f_serial_port_num =
-       __CONFIGFS_ATTR_RO(port_num, f_serial_port_num_show);
-
-static struct configfs_attribute *acm_attrs[] = {
-       &f_serial_port_num.attr,
-       NULL,
-};
-
-static struct config_item_type serial_func_type = {
-       .ct_item_ops    = &serial_item_ops,
-       .ct_attrs       = acm_attrs,
-       .ct_owner       = THIS_MODULE,
-};
-
-static void gser_free_inst(struct usb_function_instance *f)
-{
-       struct f_serial_opts *opts;
-
-       opts = container_of(f, struct f_serial_opts, func_inst);
-       gserial_free_line(opts->port_num);
-       kfree(opts);
-}
-
-static struct usb_function_instance *gser_alloc_inst(void)
-{
-       struct f_serial_opts *opts;
-       int ret;
-
-       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
-       if (!opts)
-               return ERR_PTR(-ENOMEM);
-
-       opts->func_inst.free_func_inst = gser_free_inst;
-       ret = gserial_alloc_line(&opts->port_num);
-       if (ret) {
-               kfree(opts);
-               return ERR_PTR(ret);
-       }
-       config_group_init_type_name(&opts->func_inst.group, "",
-                                   &serial_func_type);
-
-       return &opts->func_inst;
-}
-
-static void gser_free(struct usb_function *f)
-{
-       struct f_gser *serial;
-
-       serial = func_to_gser(f);
-       kfree(serial);
-}
-
-static void gser_unbind(struct usb_configuration *c, struct usb_function *f)
-{
-       usb_free_all_descriptors(f);
-}
-
-static struct usb_function *gser_alloc(struct usb_function_instance *fi)
-{
-       struct f_gser   *gser;
-       struct f_serial_opts *opts;
-
-       /* allocate and initialize one new instance */
-       gser = kzalloc(sizeof(*gser), GFP_KERNEL);
-       if (!gser)
-               return ERR_PTR(-ENOMEM);
-
-       opts = container_of(fi, struct f_serial_opts, func_inst);
-
-       gser->port_num = opts->port_num;
-
-       gser->port.func.name = "gser";
-       gser->port.func.strings = gser_strings;
-       gser->port.func.bind = gser_bind;
-       gser->port.func.unbind = gser_unbind;
-       gser->port.func.set_alt = gser_set_alt;
-       gser->port.func.disable = gser_disable;
-       gser->port.func.free_func = gser_free;
-
-       return &gser->port.func;
-}
-
-DECLARE_USB_FUNCTION_INIT(gser, gser_alloc_inst, gser_alloc);
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Al Borchers");
-MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c
deleted file mode 100644 (file)
index d3cd52d..0000000
+++ /dev/null
@@ -1,1247 +0,0 @@
-/*
- * f_sourcesink.c - USB peripheral source/sink configuration driver
- *
- * Copyright (C) 2003-2008 David Brownell
- * Copyright (C) 2008 by Nokia Corporation
- *
- * 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.
- */
-
-/* #define VERBOSE_DEBUG */
-
-#include <linux/slab.h>
-#include <linux/kernel.h>
-#include <linux/device.h>
-#include <linux/module.h>
-#include <linux/usb/composite.h>
-#include <linux/err.h>
-
-#include "g_zero.h"
-#include "gadget_chips.h"
-#include "u_f.h"
-
-/*
- * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral
- * controller drivers.
- *
- * This just sinks bulk packets OUT to the peripheral and sources them IN
- * to the host, optionally with specific data patterns for integrity tests.
- * As such it supports basic functionality and load tests.
- *
- * In terms of control messaging, this supports all the standard requests
- * plus two that support control-OUT tests.  If the optional "autoresume"
- * mode is enabled, it provides good functional coverage for the "USBCV"
- * test harness from USB-IF.
- *
- * Note that because this doesn't queue more than one request at a time,
- * some other function must be used to test queueing logic.  The network
- * link (g_ether) is the best overall option for that, since its TX and RX
- * queues are relatively independent, will receive a range of packet sizes,
- * and can often be made to run out completely.  Those issues are important
- * when stress testing peripheral controller drivers.
- *
- *
- * This is currently packaged as a configuration driver, which can't be
- * combined with other functions to make composite devices.  However, it
- * can be combined with other independent configurations.
- */
-struct f_sourcesink {
-       struct usb_function     function;
-
-       struct usb_ep           *in_ep;
-       struct usb_ep           *out_ep;
-       struct usb_ep           *iso_in_ep;
-       struct usb_ep           *iso_out_ep;
-       int                     cur_alt;
-};
-
-static inline struct f_sourcesink *func_to_ss(struct usb_function *f)
-{
-       return container_of(f, struct f_sourcesink, function);
-}
-
-static unsigned pattern;
-static unsigned isoc_interval;
-static unsigned isoc_maxpacket;
-static unsigned isoc_mult;
-static unsigned isoc_maxburst;
-static unsigned buflen;
-
-/*-------------------------------------------------------------------------*/
-
-static struct usb_interface_descriptor source_sink_intf_alt0 = {
-       .bLength =              USB_DT_INTERFACE_SIZE,
-       .bDescriptorType =      USB_DT_INTERFACE,
-
-       .bAlternateSetting =    0,
-       .bNumEndpoints =        2,
-       .bInterfaceClass =      USB_CLASS_VENDOR_SPEC,
-       /* .iInterface          = DYNAMIC */
-};
-
-static struct usb_interface_descriptor source_sink_intf_alt1 = {
-       .bLength =              USB_DT_INTERFACE_SIZE,
-       .bDescriptorType =      USB_DT_INTERFACE,
-
-       .bAlternateSetting =    1,
-       .bNumEndpoints =        4,
-       .bInterfaceClass =      USB_CLASS_VENDOR_SPEC,
-       /* .iInterface          = DYNAMIC */
-};
-
-/* full speed support: */
-
-static struct usb_endpoint_descriptor fs_source_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-};
-
-static struct usb_endpoint_descriptor fs_sink_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_OUT,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-};
-
-static struct usb_endpoint_descriptor fs_iso_source_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_ISOC,
-       .wMaxPacketSize =       cpu_to_le16(1023),
-       .bInterval =            4,
-};
-
-static struct usb_endpoint_descriptor fs_iso_sink_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_OUT,
-       .bmAttributes =         USB_ENDPOINT_XFER_ISOC,
-       .wMaxPacketSize =       cpu_to_le16(1023),
-       .bInterval =            4,
-};
-
-static struct usb_descriptor_header *fs_source_sink_descs[] = {
-       (struct usb_descriptor_header *) &source_sink_intf_alt0,
-       (struct usb_descriptor_header *) &fs_sink_desc,
-       (struct usb_descriptor_header *) &fs_source_desc,
-       (struct usb_descriptor_header *) &source_sink_intf_alt1,
-#define FS_ALT_IFC_1_OFFSET    3
-       (struct usb_descriptor_header *) &fs_sink_desc,
-       (struct usb_descriptor_header *) &fs_source_desc,
-       (struct usb_descriptor_header *) &fs_iso_sink_desc,
-       (struct usb_descriptor_header *) &fs_iso_source_desc,
-       NULL,
-};
-
-/* high speed support: */
-
-static struct usb_endpoint_descriptor hs_source_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(512),
-};
-
-static struct usb_endpoint_descriptor hs_sink_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(512),
-};
-
-static struct usb_endpoint_descriptor hs_iso_source_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bmAttributes =         USB_ENDPOINT_XFER_ISOC,
-       .wMaxPacketSize =       cpu_to_le16(1024),
-       .bInterval =            4,
-};
-
-static struct usb_endpoint_descriptor hs_iso_sink_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bmAttributes =         USB_ENDPOINT_XFER_ISOC,
-       .wMaxPacketSize =       cpu_to_le16(1024),
-       .bInterval =            4,
-};
-
-static struct usb_descriptor_header *hs_source_sink_descs[] = {
-       (struct usb_descriptor_header *) &source_sink_intf_alt0,
-       (struct usb_descriptor_header *) &hs_source_desc,
-       (struct usb_descriptor_header *) &hs_sink_desc,
-       (struct usb_descriptor_header *) &source_sink_intf_alt1,
-#define HS_ALT_IFC_1_OFFSET    3
-       (struct usb_descriptor_header *) &hs_source_desc,
-       (struct usb_descriptor_header *) &hs_sink_desc,
-       (struct usb_descriptor_header *) &hs_iso_source_desc,
-       (struct usb_descriptor_header *) &hs_iso_sink_desc,
-       NULL,
-};
-
-/* super speed support: */
-
-static struct usb_endpoint_descriptor ss_source_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(1024),
-};
-
-static struct usb_ss_ep_comp_descriptor ss_source_comp_desc = {
-       .bLength =              USB_DT_SS_EP_COMP_SIZE,
-       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
-
-       .bMaxBurst =            0,
-       .bmAttributes =         0,
-       .wBytesPerInterval =    0,
-};
-
-static struct usb_endpoint_descriptor ss_sink_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(1024),
-};
-
-static struct usb_ss_ep_comp_descriptor ss_sink_comp_desc = {
-       .bLength =              USB_DT_SS_EP_COMP_SIZE,
-       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
-
-       .bMaxBurst =            0,
-       .bmAttributes =         0,
-       .wBytesPerInterval =    0,
-};
-
-static struct usb_endpoint_descriptor ss_iso_source_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bmAttributes =         USB_ENDPOINT_XFER_ISOC,
-       .wMaxPacketSize =       cpu_to_le16(1024),
-       .bInterval =            4,
-};
-
-static struct usb_ss_ep_comp_descriptor ss_iso_source_comp_desc = {
-       .bLength =              USB_DT_SS_EP_COMP_SIZE,
-       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
-
-       .bMaxBurst =            0,
-       .bmAttributes =         0,
-       .wBytesPerInterval =    cpu_to_le16(1024),
-};
-
-static struct usb_endpoint_descriptor ss_iso_sink_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bmAttributes =         USB_ENDPOINT_XFER_ISOC,
-       .wMaxPacketSize =       cpu_to_le16(1024),
-       .bInterval =            4,
-};
-
-static struct usb_ss_ep_comp_descriptor ss_iso_sink_comp_desc = {
-       .bLength =              USB_DT_SS_EP_COMP_SIZE,
-       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
-
-       .bMaxBurst =            0,
-       .bmAttributes =         0,
-       .wBytesPerInterval =    cpu_to_le16(1024),
-};
-
-static struct usb_descriptor_header *ss_source_sink_descs[] = {
-       (struct usb_descriptor_header *) &source_sink_intf_alt0,
-       (struct usb_descriptor_header *) &ss_source_desc,
-       (struct usb_descriptor_header *) &ss_source_comp_desc,
-       (struct usb_descriptor_header *) &ss_sink_desc,
-       (struct usb_descriptor_header *) &ss_sink_comp_desc,
-       (struct usb_descriptor_header *) &source_sink_intf_alt1,
-#define SS_ALT_IFC_1_OFFSET    5
-       (struct usb_descriptor_header *) &ss_source_desc,
-       (struct usb_descriptor_header *) &ss_source_comp_desc,
-       (struct usb_descriptor_header *) &ss_sink_desc,
-       (struct usb_descriptor_header *) &ss_sink_comp_desc,
-       (struct usb_descriptor_header *) &ss_iso_source_desc,
-       (struct usb_descriptor_header *) &ss_iso_source_comp_desc,
-       (struct usb_descriptor_header *) &ss_iso_sink_desc,
-       (struct usb_descriptor_header *) &ss_iso_sink_comp_desc,
-       NULL,
-};
-
-/* function-specific strings: */
-
-static struct usb_string strings_sourcesink[] = {
-       [0].s = "source and sink data",
-       {  }                    /* end of list */
-};
-
-static struct usb_gadget_strings stringtab_sourcesink = {
-       .language       = 0x0409,       /* en-us */
-       .strings        = strings_sourcesink,
-};
-
-static struct usb_gadget_strings *sourcesink_strings[] = {
-       &stringtab_sourcesink,
-       NULL,
-};
-
-/*-------------------------------------------------------------------------*/
-
-static inline struct usb_request *ss_alloc_ep_req(struct usb_ep *ep, int len)
-{
-       return alloc_ep_req(ep, len, buflen);
-}
-
-void free_ep_req(struct usb_ep *ep, struct usb_request *req)
-{
-       kfree(req->buf);
-       usb_ep_free_request(ep, req);
-}
-
-static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep)
-{
-       int                     value;
-
-       if (ep->driver_data) {
-               value = usb_ep_disable(ep);
-               if (value < 0)
-                       DBG(cdev, "disable %s --> %d\n",
-                                       ep->name, value);
-               ep->driver_data = NULL;
-       }
-}
-
-void disable_endpoints(struct usb_composite_dev *cdev,
-               struct usb_ep *in, struct usb_ep *out,
-               struct usb_ep *iso_in, struct usb_ep *iso_out)
-{
-       disable_ep(cdev, in);
-       disable_ep(cdev, out);
-       if (iso_in)
-               disable_ep(cdev, iso_in);
-       if (iso_out)
-               disable_ep(cdev, iso_out);
-}
-
-static int
-sourcesink_bind(struct usb_configuration *c, struct usb_function *f)
-{
-       struct usb_composite_dev *cdev = c->cdev;
-       struct f_sourcesink     *ss = func_to_ss(f);
-       int     id;
-       int ret;
-
-       /* allocate interface ID(s) */
-       id = usb_interface_id(c, f);
-       if (id < 0)
-               return id;
-       source_sink_intf_alt0.bInterfaceNumber = id;
-       source_sink_intf_alt1.bInterfaceNumber = id;
-
-       /* allocate bulk endpoints */
-       ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
-       if (!ss->in_ep) {
-autoconf_fail:
-               ERROR(cdev, "%s: can't autoconfigure on %s\n",
-                       f->name, cdev->gadget->name);
-               return -ENODEV;
-       }
-       ss->in_ep->driver_data = cdev;  /* claim */
-
-       ss->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_sink_desc);
-       if (!ss->out_ep)
-               goto autoconf_fail;
-       ss->out_ep->driver_data = cdev; /* claim */
-
-       /* sanity check the isoc module parameters */
-       if (isoc_interval < 1)
-               isoc_interval = 1;
-       if (isoc_interval > 16)
-               isoc_interval = 16;
-       if (isoc_mult > 2)
-               isoc_mult = 2;
-       if (isoc_maxburst > 15)
-               isoc_maxburst = 15;
-
-       /* fill in the FS isoc descriptors from the module parameters */
-       fs_iso_source_desc.wMaxPacketSize = isoc_maxpacket > 1023 ?
-                                               1023 : isoc_maxpacket;
-       fs_iso_source_desc.bInterval = isoc_interval;
-       fs_iso_sink_desc.wMaxPacketSize = isoc_maxpacket > 1023 ?
-                                               1023 : isoc_maxpacket;
-       fs_iso_sink_desc.bInterval = isoc_interval;
-
-       /* allocate iso endpoints */
-       ss->iso_in_ep = usb_ep_autoconfig(cdev->gadget, &fs_iso_source_desc);
-       if (!ss->iso_in_ep)
-               goto no_iso;
-       ss->iso_in_ep->driver_data = cdev;      /* claim */
-
-       ss->iso_out_ep = usb_ep_autoconfig(cdev->gadget, &fs_iso_sink_desc);
-       if (ss->iso_out_ep) {
-               ss->iso_out_ep->driver_data = cdev;     /* claim */
-       } else {
-               ss->iso_in_ep->driver_data = NULL;
-               ss->iso_in_ep = NULL;
-no_iso:
-               /*
-                * We still want to work even if the UDC doesn't have isoc
-                * endpoints, so null out the alt interface that contains
-                * them and continue.
-                */
-               fs_source_sink_descs[FS_ALT_IFC_1_OFFSET] = NULL;
-               hs_source_sink_descs[HS_ALT_IFC_1_OFFSET] = NULL;
-               ss_source_sink_descs[SS_ALT_IFC_1_OFFSET] = NULL;
-       }
-
-       if (isoc_maxpacket > 1024)
-               isoc_maxpacket = 1024;
-
-       /* support high speed hardware */
-       hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress;
-       hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress;
-
-       /*
-        * Fill in the HS isoc descriptors from the module parameters.
-        * We assume that the user knows what they are doing and won't
-        * give parameters that their UDC doesn't support.
-        */
-       hs_iso_source_desc.wMaxPacketSize = isoc_maxpacket;
-       hs_iso_source_desc.wMaxPacketSize |= isoc_mult << 11;
-       hs_iso_source_desc.bInterval = isoc_interval;
-       hs_iso_source_desc.bEndpointAddress =
-               fs_iso_source_desc.bEndpointAddress;
-
-       hs_iso_sink_desc.wMaxPacketSize = isoc_maxpacket;
-       hs_iso_sink_desc.wMaxPacketSize |= isoc_mult << 11;
-       hs_iso_sink_desc.bInterval = isoc_interval;
-       hs_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress;
-
-       /* support super speed hardware */
-       ss_source_desc.bEndpointAddress =
-               fs_source_desc.bEndpointAddress;
-       ss_sink_desc.bEndpointAddress =
-               fs_sink_desc.bEndpointAddress;
-
-       /*
-        * Fill in the SS isoc descriptors from the module parameters.
-        * We assume that the user knows what they are doing and won't
-        * give parameters that their UDC doesn't support.
-        */
-       ss_iso_source_desc.wMaxPacketSize = isoc_maxpacket;
-       ss_iso_source_desc.bInterval = isoc_interval;
-       ss_iso_source_comp_desc.bmAttributes = isoc_mult;
-       ss_iso_source_comp_desc.bMaxBurst = isoc_maxburst;
-       ss_iso_source_comp_desc.wBytesPerInterval =
-               isoc_maxpacket * (isoc_mult + 1) * (isoc_maxburst + 1);
-       ss_iso_source_desc.bEndpointAddress =
-               fs_iso_source_desc.bEndpointAddress;
-
-       ss_iso_sink_desc.wMaxPacketSize = isoc_maxpacket;
-       ss_iso_sink_desc.bInterval = isoc_interval;
-       ss_iso_sink_comp_desc.bmAttributes = isoc_mult;
-       ss_iso_sink_comp_desc.bMaxBurst = isoc_maxburst;
-       ss_iso_sink_comp_desc.wBytesPerInterval =
-               isoc_maxpacket * (isoc_mult + 1) * (isoc_maxburst + 1);
-       ss_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress;
-
-       ret = usb_assign_descriptors(f, fs_source_sink_descs,
-                       hs_source_sink_descs, ss_source_sink_descs);
-       if (ret)
-               return ret;
-
-       DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s\n",
-           (gadget_is_superspeed(c->cdev->gadget) ? "super" :
-            (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")),
-                       f->name, ss->in_ep->name, ss->out_ep->name,
-                       ss->iso_in_ep ? ss->iso_in_ep->name : "<none>",
-                       ss->iso_out_ep ? ss->iso_out_ep->name : "<none>");
-       return 0;
-}
-
-static void
-sourcesink_free_func(struct usb_function *f)
-{
-       struct f_ss_opts *opts;
-
-       opts = container_of(f->fi, struct f_ss_opts, func_inst);
-
-       mutex_lock(&opts->lock);
-       opts->refcnt--;
-       mutex_unlock(&opts->lock);
-
-       usb_free_all_descriptors(f);
-       kfree(func_to_ss(f));
-}
-
-/* optionally require specific source/sink data patterns  */
-static int check_read_data(struct f_sourcesink *ss, struct usb_request *req)
-{
-       unsigned                i;
-       u8                      *buf = req->buf;
-       struct usb_composite_dev *cdev = ss->function.config->cdev;
-
-       if (pattern == 2)
-               return 0;
-
-       for (i = 0; i < req->actual; i++, buf++) {
-               switch (pattern) {
-
-               /* all-zeroes has no synchronization issues */
-               case 0:
-                       if (*buf == 0)
-                               continue;
-                       break;
-
-               /* "mod63" stays in sync with short-terminated transfers,
-                * OR otherwise when host and gadget agree on how large
-                * each usb transfer request should be.  Resync is done
-                * with set_interface or set_config.  (We *WANT* it to
-                * get quickly out of sync if controllers or their drivers
-                * stutter for any reason, including buffer duplication...)
-                */
-               case 1:
-                       if (*buf == (u8)(i % 63))
-                               continue;
-                       break;
-               }
-               ERROR(cdev, "bad OUT byte, buf[%d] = %d\n", i, *buf);
-               usb_ep_set_halt(ss->out_ep);
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static void reinit_write_data(struct usb_ep *ep, struct usb_request *req)
-{
-       unsigned        i;
-       u8              *buf = req->buf;
-
-       switch (pattern) {
-       case 0:
-               memset(req->buf, 0, req->length);
-               break;
-       case 1:
-               for  (i = 0; i < req->length; i++)
-                       *buf++ = (u8) (i % 63);
-               break;
-       case 2:
-               break;
-       }
-}
-
-static void source_sink_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       struct usb_composite_dev        *cdev;
-       struct f_sourcesink             *ss = ep->driver_data;
-       int                             status = req->status;
-
-       /* driver_data will be null if ep has been disabled */
-       if (!ss)
-               return;
-
-       cdev = ss->function.config->cdev;
-
-       switch (status) {
-
-       case 0:                         /* normal completion? */
-               if (ep == ss->out_ep) {
-                       check_read_data(ss, req);
-                       if (pattern != 2)
-                               memset(req->buf, 0x55, req->length);
-               }
-               break;
-
-       /* this endpoint is normally active while we're configured */
-       case -ECONNABORTED:             /* hardware forced ep reset */
-       case -ECONNRESET:               /* request dequeued */
-       case -ESHUTDOWN:                /* disconnect from host */
-               VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status,
-                               req->actual, req->length);
-               if (ep == ss->out_ep)
-                       check_read_data(ss, req);
-               free_ep_req(ep, req);
-               return;
-
-       case -EOVERFLOW:                /* buffer overrun on read means that
-                                        * we didn't provide a big enough
-                                        * buffer.
-                                        */
-       default:
-#if 1
-               DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name,
-                               status, req->actual, req->length);
-#endif
-       case -EREMOTEIO:                /* short read */
-               break;
-       }
-
-       status = usb_ep_queue(ep, req, GFP_ATOMIC);
-       if (status) {
-               ERROR(cdev, "kill %s:  resubmit %d bytes --> %d\n",
-                               ep->name, req->length, status);
-               usb_ep_set_halt(ep);
-               /* FIXME recover later ... somehow */
-       }
-}
-
-static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
-               bool is_iso, int speed)
-{
-       struct usb_ep           *ep;
-       struct usb_request      *req;
-       int                     i, size, status;
-
-       for (i = 0; i < 8; i++) {
-               if (is_iso) {
-                       switch (speed) {
-                       case USB_SPEED_SUPER:
-                               size = isoc_maxpacket * (isoc_mult + 1) *
-                                               (isoc_maxburst + 1);
-                               break;
-                       case USB_SPEED_HIGH:
-                               size = isoc_maxpacket * (isoc_mult + 1);
-                               break;
-                       default:
-                               size = isoc_maxpacket > 1023 ?
-                                               1023 : isoc_maxpacket;
-                               break;
-                       }
-                       ep = is_in ? ss->iso_in_ep : ss->iso_out_ep;
-                       req = ss_alloc_ep_req(ep, size);
-               } else {
-                       ep = is_in ? ss->in_ep : ss->out_ep;
-                       req = ss_alloc_ep_req(ep, 0);
-               }
-
-               if (!req)
-                       return -ENOMEM;
-
-               req->complete = source_sink_complete;
-               if (is_in)
-                       reinit_write_data(ep, req);
-               else if (pattern != 2)
-                       memset(req->buf, 0x55, req->length);
-
-               status = usb_ep_queue(ep, req, GFP_ATOMIC);
-               if (status) {
-                       struct usb_composite_dev        *cdev;
-
-                       cdev = ss->function.config->cdev;
-                       ERROR(cdev, "start %s%s %s --> %d\n",
-                             is_iso ? "ISO-" : "", is_in ? "IN" : "OUT",
-                             ep->name, status);
-                       free_ep_req(ep, req);
-               }
-
-               if (!is_iso)
-                       break;
-       }
-
-       return status;
-}
-
-static void disable_source_sink(struct f_sourcesink *ss)
-{
-       struct usb_composite_dev        *cdev;
-
-       cdev = ss->function.config->cdev;
-       disable_endpoints(cdev, ss->in_ep, ss->out_ep, ss->iso_in_ep,
-                       ss->iso_out_ep);
-       VDBG(cdev, "%s disabled\n", ss->function.name);
-}
-
-static int
-enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss,
-               int alt)
-{
-       int                                     result = 0;
-       int                                     speed = cdev->gadget->speed;
-       struct usb_ep                           *ep;
-
-       /* one bulk endpoint writes (sources) zeroes IN (to the host) */
-       ep = ss->in_ep;
-       result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
-       if (result)
-               return result;
-       result = usb_ep_enable(ep);
-       if (result < 0)
-               return result;
-       ep->driver_data = ss;
-
-       result = source_sink_start_ep(ss, true, false, speed);
-       if (result < 0) {
-fail:
-               ep = ss->in_ep;
-               usb_ep_disable(ep);
-               ep->driver_data = NULL;
-               return result;
-       }
-
-       /* one bulk endpoint reads (sinks) anything OUT (from the host) */
-       ep = ss->out_ep;
-       result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
-       if (result)
-               goto fail;
-       result = usb_ep_enable(ep);
-       if (result < 0)
-               goto fail;
-       ep->driver_data = ss;
-
-       result = source_sink_start_ep(ss, false, false, speed);
-       if (result < 0) {
-fail2:
-               ep = ss->out_ep;
-               usb_ep_disable(ep);
-               ep->driver_data = NULL;
-               goto fail;
-       }
-
-       if (alt == 0)
-               goto out;
-
-       /* one iso endpoint writes (sources) zeroes IN (to the host) */
-       ep = ss->iso_in_ep;
-       if (ep) {
-               result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
-               if (result)
-                       goto fail2;
-               result = usb_ep_enable(ep);
-               if (result < 0)
-                       goto fail2;
-               ep->driver_data = ss;
-
-               result = source_sink_start_ep(ss, true, true, speed);
-               if (result < 0) {
-fail3:
-                       ep = ss->iso_in_ep;
-                       if (ep) {
-                               usb_ep_disable(ep);
-                               ep->driver_data = NULL;
-                       }
-                       goto fail2;
-               }
-       }
-
-       /* one iso endpoint reads (sinks) anything OUT (from the host) */
-       ep = ss->iso_out_ep;
-       if (ep) {
-               result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
-               if (result)
-                       goto fail3;
-               result = usb_ep_enable(ep);
-               if (result < 0)
-                       goto fail3;
-               ep->driver_data = ss;
-
-               result = source_sink_start_ep(ss, false, true, speed);
-               if (result < 0) {
-                       usb_ep_disable(ep);
-                       ep->driver_data = NULL;
-                       goto fail3;
-               }
-       }
-out:
-       ss->cur_alt = alt;
-
-       DBG(cdev, "%s enabled, alt intf %d\n", ss->function.name, alt);
-       return result;
-}
-
-static int sourcesink_set_alt(struct usb_function *f,
-               unsigned intf, unsigned alt)
-{
-       struct f_sourcesink             *ss = func_to_ss(f);
-       struct usb_composite_dev        *cdev = f->config->cdev;
-
-       if (ss->in_ep->driver_data)
-               disable_source_sink(ss);
-       return enable_source_sink(cdev, ss, alt);
-}
-
-static int sourcesink_get_alt(struct usb_function *f, unsigned intf)
-{
-       struct f_sourcesink             *ss = func_to_ss(f);
-
-       return ss->cur_alt;
-}
-
-static void sourcesink_disable(struct usb_function *f)
-{
-       struct f_sourcesink     *ss = func_to_ss(f);
-
-       disable_source_sink(ss);
-}
-
-/*-------------------------------------------------------------------------*/
-
-static int sourcesink_setup(struct usb_function *f,
-               const struct usb_ctrlrequest *ctrl)
-{
-       struct usb_configuration        *c = f->config;
-       struct usb_request      *req = c->cdev->req;
-       int                     value = -EOPNOTSUPP;
-       u16                     w_index = le16_to_cpu(ctrl->wIndex);
-       u16                     w_value = le16_to_cpu(ctrl->wValue);
-       u16                     w_length = le16_to_cpu(ctrl->wLength);
-
-       req->length = USB_COMP_EP0_BUFSIZ;
-
-       /* composite driver infrastructure handles everything except
-        * the two control test requests.
-        */
-       switch (ctrl->bRequest) {
-
-       /*
-        * These are the same vendor-specific requests supported by
-        * Intel's USB 2.0 compliance test devices.  We exceed that
-        * device spec by allowing multiple-packet requests.
-        *
-        * NOTE:  the Control-OUT data stays in req->buf ... better
-        * would be copying it into a scratch buffer, so that other
-        * requests may safely intervene.
-        */
-       case 0x5b:      /* control WRITE test -- fill the buffer */
-               if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR))
-                       goto unknown;
-               if (w_value || w_index)
-                       break;
-               /* just read that many bytes into the buffer */
-               if (w_length > req->length)
-                       break;
-               value = w_length;
-               break;
-       case 0x5c:      /* control READ test -- return the buffer */
-               if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR))
-                       goto unknown;
-               if (w_value || w_index)
-                       break;
-               /* expect those bytes are still in the buffer; send back */
-               if (w_length > req->length)
-                       break;
-               value = w_length;
-               break;
-
-       default:
-unknown:
-               VDBG(c->cdev,
-                       "unknown control req%02x.%02x v%04x i%04x l%d\n",
-                       ctrl->bRequestType, ctrl->bRequest,
-                       w_value, w_index, w_length);
-       }
-
-       /* respond with data transfer or status phase? */
-       if (value >= 0) {
-               VDBG(c->cdev, "source/sink req%02x.%02x v%04x i%04x l%d\n",
-                       ctrl->bRequestType, ctrl->bRequest,
-                       w_value, w_index, w_length);
-               req->zero = 0;
-               req->length = value;
-               value = usb_ep_queue(c->cdev->gadget->ep0, req, GFP_ATOMIC);
-               if (value < 0)
-                       ERROR(c->cdev, "source/sink response, err %d\n",
-                                       value);
-       }
-
-       /* device either stalls (value < 0) or reports success */
-       return value;
-}
-
-static struct usb_function *source_sink_alloc_func(
-               struct usb_function_instance *fi)
-{
-       struct f_sourcesink     *ss;
-       struct f_ss_opts        *ss_opts;
-
-       ss = kzalloc(sizeof(*ss), GFP_KERNEL);
-       if (!ss)
-               return NULL;
-
-       ss_opts =  container_of(fi, struct f_ss_opts, func_inst);
-
-       mutex_lock(&ss_opts->lock);
-       ss_opts->refcnt++;
-       mutex_unlock(&ss_opts->lock);
-
-       pattern = ss_opts->pattern;
-       isoc_interval = ss_opts->isoc_interval;
-       isoc_maxpacket = ss_opts->isoc_maxpacket;
-       isoc_mult = ss_opts->isoc_mult;
-       isoc_maxburst = ss_opts->isoc_maxburst;
-       buflen = ss_opts->bulk_buflen;
-
-       ss->function.name = "source/sink";
-       ss->function.bind = sourcesink_bind;
-       ss->function.set_alt = sourcesink_set_alt;
-       ss->function.get_alt = sourcesink_get_alt;
-       ss->function.disable = sourcesink_disable;
-       ss->function.setup = sourcesink_setup;
-       ss->function.strings = sourcesink_strings;
-
-       ss->function.free_func = sourcesink_free_func;
-
-       return &ss->function;
-}
-
-static inline struct f_ss_opts *to_f_ss_opts(struct config_item *item)
-{
-       return container_of(to_config_group(item), struct f_ss_opts,
-                           func_inst.group);
-}
-
-CONFIGFS_ATTR_STRUCT(f_ss_opts);
-CONFIGFS_ATTR_OPS(f_ss_opts);
-
-static void ss_attr_release(struct config_item *item)
-{
-       struct f_ss_opts *ss_opts = to_f_ss_opts(item);
-
-       usb_put_function_instance(&ss_opts->func_inst);
-}
-
-static struct configfs_item_operations ss_item_ops = {
-       .release                = ss_attr_release,
-       .show_attribute         = f_ss_opts_attr_show,
-       .store_attribute        = f_ss_opts_attr_store,
-};
-
-static ssize_t f_ss_opts_pattern_show(struct f_ss_opts *opts, char *page)
-{
-       int result;
-
-       mutex_lock(&opts->lock);
-       result = sprintf(page, "%d", opts->pattern);
-       mutex_unlock(&opts->lock);
-
-       return result;
-}
-
-static ssize_t f_ss_opts_pattern_store(struct f_ss_opts *opts,
-                                      const char *page, size_t len)
-{
-       int ret;
-       u8 num;
-
-       mutex_lock(&opts->lock);
-       if (opts->refcnt) {
-               ret = -EBUSY;
-               goto end;
-       }
-
-       ret = kstrtou8(page, 0, &num);
-       if (ret)
-               goto end;
-
-       if (num != 0 && num != 1 && num != 2) {
-               ret = -EINVAL;
-               goto end;
-       }
-
-       opts->pattern = num;
-       ret = len;
-end:
-       mutex_unlock(&opts->lock);
-       return ret;
-}
-
-static struct f_ss_opts_attribute f_ss_opts_pattern =
-       __CONFIGFS_ATTR(pattern, S_IRUGO | S_IWUSR,
-                       f_ss_opts_pattern_show,
-                       f_ss_opts_pattern_store);
-
-static ssize_t f_ss_opts_isoc_interval_show(struct f_ss_opts *opts, char *page)
-{
-       int result;
-
-       mutex_lock(&opts->lock);
-       result = sprintf(page, "%d", opts->isoc_interval);
-       mutex_unlock(&opts->lock);
-
-       return result;
-}
-
-static ssize_t f_ss_opts_isoc_interval_store(struct f_ss_opts *opts,
-                                      const char *page, size_t len)
-{
-       int ret;
-       u8 num;
-
-       mutex_lock(&opts->lock);
-       if (opts->refcnt) {
-               ret = -EBUSY;
-               goto end;
-       }
-
-       ret = kstrtou8(page, 0, &num);
-       if (ret)
-               goto end;
-
-       if (num > 16) {
-               ret = -EINVAL;
-               goto end;
-       }
-
-       opts->isoc_interval = num;
-       ret = len;
-end:
-       mutex_unlock(&opts->lock);
-       return ret;
-}
-
-static struct f_ss_opts_attribute f_ss_opts_isoc_interval =
-       __CONFIGFS_ATTR(isoc_interval, S_IRUGO | S_IWUSR,
-                       f_ss_opts_isoc_interval_show,
-                       f_ss_opts_isoc_interval_store);
-
-static ssize_t f_ss_opts_isoc_maxpacket_show(struct f_ss_opts *opts, char *page)
-{
-       int result;
-
-       mutex_lock(&opts->lock);
-       result = sprintf(page, "%d", opts->isoc_maxpacket);
-       mutex_unlock(&opts->lock);
-
-       return result;
-}
-
-static ssize_t f_ss_opts_isoc_maxpacket_store(struct f_ss_opts *opts,
-                                      const char *page, size_t len)
-{
-       int ret;
-       u16 num;
-
-       mutex_lock(&opts->lock);
-       if (opts->refcnt) {
-               ret = -EBUSY;
-               goto end;
-       }
-
-       ret = kstrtou16(page, 0, &num);
-       if (ret)
-               goto end;
-
-       if (num > 1024) {
-               ret = -EINVAL;
-               goto end;
-       }
-
-       opts->isoc_maxpacket = num;
-       ret = len;
-end:
-       mutex_unlock(&opts->lock);
-       return ret;
-}
-
-static struct f_ss_opts_attribute f_ss_opts_isoc_maxpacket =
-       __CONFIGFS_ATTR(isoc_maxpacket, S_IRUGO | S_IWUSR,
-                       f_ss_opts_isoc_maxpacket_show,
-                       f_ss_opts_isoc_maxpacket_store);
-
-static ssize_t f_ss_opts_isoc_mult_show(struct f_ss_opts *opts, char *page)
-{
-       int result;
-
-       mutex_lock(&opts->lock);
-       result = sprintf(page, "%d", opts->isoc_mult);
-       mutex_unlock(&opts->lock);
-
-       return result;
-}
-
-static ssize_t f_ss_opts_isoc_mult_store(struct f_ss_opts *opts,
-                                      const char *page, size_t len)
-{
-       int ret;
-       u8 num;
-
-       mutex_lock(&opts->lock);
-       if (opts->refcnt) {
-               ret = -EBUSY;
-               goto end;
-       }
-
-       ret = kstrtou8(page, 0, &num);
-       if (ret)
-               goto end;
-
-       if (num > 2) {
-               ret = -EINVAL;
-               goto end;
-       }
-
-       opts->isoc_mult = num;
-       ret = len;
-end:
-       mutex_unlock(&opts->lock);
-       return ret;
-}
-
-static struct f_ss_opts_attribute f_ss_opts_isoc_mult =
-       __CONFIGFS_ATTR(isoc_mult, S_IRUGO | S_IWUSR,
-                       f_ss_opts_isoc_mult_show,
-                       f_ss_opts_isoc_mult_store);
-
-static ssize_t f_ss_opts_isoc_maxburst_show(struct f_ss_opts *opts, char *page)
-{
-       int result;
-
-       mutex_lock(&opts->lock);
-       result = sprintf(page, "%d", opts->isoc_maxburst);
-       mutex_unlock(&opts->lock);
-
-       return result;
-}
-
-static ssize_t f_ss_opts_isoc_maxburst_store(struct f_ss_opts *opts,
-                                      const char *page, size_t len)
-{
-       int ret;
-       u8 num;
-
-       mutex_lock(&opts->lock);
-       if (opts->refcnt) {
-               ret = -EBUSY;
-               goto end;
-       }
-
-       ret = kstrtou8(page, 0, &num);
-       if (ret)
-               goto end;
-
-       if (num > 15) {
-               ret = -EINVAL;
-               goto end;
-       }
-
-       opts->isoc_maxburst = num;
-       ret = len;
-end:
-       mutex_unlock(&opts->lock);
-       return ret;
-}
-
-static struct f_ss_opts_attribute f_ss_opts_isoc_maxburst =
-       __CONFIGFS_ATTR(isoc_maxburst, S_IRUGO | S_IWUSR,
-                       f_ss_opts_isoc_maxburst_show,
-                       f_ss_opts_isoc_maxburst_store);
-
-static ssize_t f_ss_opts_bulk_buflen_show(struct f_ss_opts *opts, char *page)
-{
-       int result;
-
-       mutex_lock(&opts->lock);
-       result = sprintf(page, "%d", opts->bulk_buflen);
-       mutex_unlock(&opts->lock);
-
-       return result;
-}
-
-static ssize_t f_ss_opts_bulk_buflen_store(struct f_ss_opts *opts,
-                                          const char *page, size_t len)
-{
-       int ret;
-       u32 num;
-
-       mutex_lock(&opts->lock);
-       if (opts->refcnt) {
-               ret = -EBUSY;
-               goto end;
-       }
-
-       ret = kstrtou32(page, 0, &num);
-       if (ret)
-               goto end;
-
-       opts->bulk_buflen = num;
-       ret = len;
-end:
-       mutex_unlock(&opts->lock);
-       return ret;
-}
-
-static struct f_ss_opts_attribute f_ss_opts_bulk_buflen =
-       __CONFIGFS_ATTR(buflen, S_IRUGO | S_IWUSR,
-                       f_ss_opts_bulk_buflen_show,
-                       f_ss_opts_bulk_buflen_store);
-
-static struct configfs_attribute *ss_attrs[] = {
-       &f_ss_opts_pattern.attr,
-       &f_ss_opts_isoc_interval.attr,
-       &f_ss_opts_isoc_maxpacket.attr,
-       &f_ss_opts_isoc_mult.attr,
-       &f_ss_opts_isoc_maxburst.attr,
-       &f_ss_opts_bulk_buflen.attr,
-       NULL,
-};
-
-static struct config_item_type ss_func_type = {
-       .ct_item_ops    = &ss_item_ops,
-       .ct_attrs       = ss_attrs,
-       .ct_owner       = THIS_MODULE,
-};
-
-static void source_sink_free_instance(struct usb_function_instance *fi)
-{
-       struct f_ss_opts *ss_opts;
-
-       ss_opts = container_of(fi, struct f_ss_opts, func_inst);
-       kfree(ss_opts);
-}
-
-static struct usb_function_instance *source_sink_alloc_inst(void)
-{
-       struct f_ss_opts *ss_opts;
-
-       ss_opts = kzalloc(sizeof(*ss_opts), GFP_KERNEL);
-       if (!ss_opts)
-               return ERR_PTR(-ENOMEM);
-       mutex_init(&ss_opts->lock);
-       ss_opts->func_inst.free_func_inst = source_sink_free_instance;
-       ss_opts->isoc_interval = GZERO_ISOC_INTERVAL;
-       ss_opts->isoc_maxpacket = GZERO_ISOC_MAXPACKET;
-       ss_opts->bulk_buflen = GZERO_BULK_BUFLEN;
-
-       config_group_init_type_name(&ss_opts->func_inst.group, "",
-                                   &ss_func_type);
-
-       return &ss_opts->func_inst;
-}
-DECLARE_USB_FUNCTION(SourceSink, source_sink_alloc_inst,
-               source_sink_alloc_func);
-
-static int __init sslb_modinit(void)
-{
-       int ret;
-
-       ret = usb_function_register(&SourceSinkusb_func);
-       if (ret)
-               return ret;
-       ret = lb_modinit();
-       if (ret)
-               usb_function_unregister(&SourceSinkusb_func);
-       return ret;
-}
-static void __exit sslb_modexit(void)
-{
-       usb_function_unregister(&SourceSinkusb_func);
-       lb_modexit();
-}
-module_init(sslb_modinit);
-module_exit(sslb_modexit);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/f_subset.c
deleted file mode 100644 (file)
index 1ea8baf..0000000
+++ /dev/null
@@ -1,519 +0,0 @@
-/*
- * f_subset.c -- "CDC Subset" Ethernet link function driver
- *
- * Copyright (C) 2003-2005,2008 David Brownell
- * Copyright (C) 2008 Nokia Corporation
- *
- * 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.
- */
-
-#include <linux/slab.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/etherdevice.h>
-
-#include "u_ether.h"
-#include "u_ether_configfs.h"
-#include "u_gether.h"
-
-/*
- * This function packages a simple "CDC Subset" Ethernet port with no real
- * control mechanisms; just raw data transfer over two bulk endpoints.
- * The data transfer model is exactly that of CDC Ethernet, which is
- * why we call it the "CDC Subset".
- *
- * Because it's not standardized, this has some interoperability issues.
- * They mostly relate to driver binding, since the data transfer model is
- * so simple (CDC Ethernet).  The original versions of this protocol used
- * specific product/vendor IDs:  byteswapped IDs for Digital Equipment's
- * SA-1100 "Itsy" board, which could run Linux 2.4 kernels and supported
- * daughtercards with USB peripheral connectors.  (It was used more often
- * with other boards, using the Itsy identifiers.)  Linux hosts recognized
- * this with CONFIG_USB_ARMLINUX; these devices have only one configuration
- * and one interface.
- *
- * At some point, MCCI defined a (nonconformant) CDC MDLM variant called
- * "SAFE", which happens to have a mode which is identical to the "CDC
- * Subset" in terms of data transfer and lack of control model.  This was
- * adopted by later Sharp Zaurus models, and by some other software which
- * Linux hosts recognize with CONFIG_USB_NET_ZAURUS.
- *
- * Because Microsoft's RNDIS drivers are far from robust, we added a few
- * descriptors to the CDC Subset code, making this code look like a SAFE
- * implementation.  This lets you use MCCI's host side MS-Windows drivers
- * if you get fed up with RNDIS.  It also makes it easier for composite
- * drivers to work, since they can use class based binding instead of
- * caring about specific product and vendor IDs.
- */
-
-struct f_gether {
-       struct gether                   port;
-
-       char                            ethaddr[14];
-};
-
-static inline struct f_gether *func_to_geth(struct usb_function *f)
-{
-       return container_of(f, struct f_gether, port.func);
-}
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * "Simple" CDC-subset option is a simple vendor-neutral model that most
- * full speed controllers can handle:  one interface, two bulk endpoints.
- * To assist host side drivers, we fancy it up a bit, and add descriptors so
- * some host side drivers will understand it as a "SAFE" variant.
- *
- * "SAFE" loosely follows CDC WMC MDLM, violating the spec in various ways.
- * Data endpoints live in the control interface, there's no data interface.
- * And it's not used to talk to a cell phone radio.
- */
-
-/* interface descriptor: */
-
-static struct usb_interface_descriptor subset_data_intf = {
-       .bLength =              sizeof subset_data_intf,
-       .bDescriptorType =      USB_DT_INTERFACE,
-
-       /* .bInterfaceNumber = DYNAMIC */
-       .bAlternateSetting =    0,
-       .bNumEndpoints =        2,
-       .bInterfaceClass =      USB_CLASS_COMM,
-       .bInterfaceSubClass =   USB_CDC_SUBCLASS_MDLM,
-       .bInterfaceProtocol =   0,
-       /* .iInterface = DYNAMIC */
-};
-
-static struct usb_cdc_header_desc mdlm_header_desc = {
-       .bLength =              sizeof mdlm_header_desc,
-       .bDescriptorType =      USB_DT_CS_INTERFACE,
-       .bDescriptorSubType =   USB_CDC_HEADER_TYPE,
-
-       .bcdCDC =               cpu_to_le16(0x0110),
-};
-
-static struct usb_cdc_mdlm_desc mdlm_desc = {
-       .bLength =              sizeof mdlm_desc,
-       .bDescriptorType =      USB_DT_CS_INTERFACE,
-       .bDescriptorSubType =   USB_CDC_MDLM_TYPE,
-
-       .bcdVersion =           cpu_to_le16(0x0100),
-       .bGUID = {
-               0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6,
-               0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f,
-       },
-};
-
-/* since "usb_cdc_mdlm_detail_desc" is a variable length structure, we
- * can't really use its struct.  All we do here is say that we're using
- * the submode of "SAFE" which directly matches the CDC Subset.
- */
-static u8 mdlm_detail_desc[] = {
-       6,
-       USB_DT_CS_INTERFACE,
-       USB_CDC_MDLM_DETAIL_TYPE,
-
-       0,      /* "SAFE" */
-       0,      /* network control capabilities (none) */
-       0,      /* network data capabilities ("raw" encapsulation) */
-};
-
-static struct usb_cdc_ether_desc ether_desc = {
-       .bLength =              sizeof ether_desc,
-       .bDescriptorType =      USB_DT_CS_INTERFACE,
-       .bDescriptorSubType =   USB_CDC_ETHERNET_TYPE,
-
-       /* this descriptor actually adds value, surprise! */
-       /* .iMACAddress = DYNAMIC */
-       .bmEthernetStatistics = cpu_to_le32(0), /* no statistics */
-       .wMaxSegmentSize =      cpu_to_le16(ETH_FRAME_LEN),
-       .wNumberMCFilters =     cpu_to_le16(0),
-       .bNumberPowerFilters =  0,
-};
-
-/* full speed support: */
-
-static struct usb_endpoint_descriptor fs_subset_in_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-};
-
-static struct usb_endpoint_descriptor fs_subset_out_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_OUT,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-};
-
-static struct usb_descriptor_header *fs_eth_function[] = {
-       (struct usb_descriptor_header *) &subset_data_intf,
-       (struct usb_descriptor_header *) &mdlm_header_desc,
-       (struct usb_descriptor_header *) &mdlm_desc,
-       (struct usb_descriptor_header *) &mdlm_detail_desc,
-       (struct usb_descriptor_header *) &ether_desc,
-       (struct usb_descriptor_header *) &fs_subset_in_desc,
-       (struct usb_descriptor_header *) &fs_subset_out_desc,
-       NULL,
-};
-
-/* high speed support: */
-
-static struct usb_endpoint_descriptor hs_subset_in_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(512),
-};
-
-static struct usb_endpoint_descriptor hs_subset_out_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(512),
-};
-
-static struct usb_descriptor_header *hs_eth_function[] = {
-       (struct usb_descriptor_header *) &subset_data_intf,
-       (struct usb_descriptor_header *) &mdlm_header_desc,
-       (struct usb_descriptor_header *) &mdlm_desc,
-       (struct usb_descriptor_header *) &mdlm_detail_desc,
-       (struct usb_descriptor_header *) &ether_desc,
-       (struct usb_descriptor_header *) &hs_subset_in_desc,
-       (struct usb_descriptor_header *) &hs_subset_out_desc,
-       NULL,
-};
-
-/* super speed support: */
-
-static struct usb_endpoint_descriptor ss_subset_in_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(1024),
-};
-
-static struct usb_endpoint_descriptor ss_subset_out_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(1024),
-};
-
-static struct usb_ss_ep_comp_descriptor ss_subset_bulk_comp_desc = {
-       .bLength =              sizeof ss_subset_bulk_comp_desc,
-       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
-
-       /* the following 2 values can be tweaked if necessary */
-       /* .bMaxBurst =         0, */
-       /* .bmAttributes =      0, */
-};
-
-static struct usb_descriptor_header *ss_eth_function[] = {
-       (struct usb_descriptor_header *) &subset_data_intf,
-       (struct usb_descriptor_header *) &mdlm_header_desc,
-       (struct usb_descriptor_header *) &mdlm_desc,
-       (struct usb_descriptor_header *) &mdlm_detail_desc,
-       (struct usb_descriptor_header *) &ether_desc,
-       (struct usb_descriptor_header *) &ss_subset_in_desc,
-       (struct usb_descriptor_header *) &ss_subset_bulk_comp_desc,
-       (struct usb_descriptor_header *) &ss_subset_out_desc,
-       (struct usb_descriptor_header *) &ss_subset_bulk_comp_desc,
-       NULL,
-};
-
-/* string descriptors: */
-
-static struct usb_string geth_string_defs[] = {
-       [0].s = "CDC Ethernet Subset/SAFE",
-       [1].s = "",
-       {  } /* end of list */
-};
-
-static struct usb_gadget_strings geth_string_table = {
-       .language =             0x0409, /* en-us */
-       .strings =              geth_string_defs,
-};
-
-static struct usb_gadget_strings *geth_strings[] = {
-       &geth_string_table,
-       NULL,
-};
-
-/*-------------------------------------------------------------------------*/
-
-static int geth_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
-{
-       struct f_gether         *geth = func_to_geth(f);
-       struct usb_composite_dev *cdev = f->config->cdev;
-       struct net_device       *net;
-
-       /* we know alt == 0, so this is an activation or a reset */
-
-       if (geth->port.in_ep->driver_data) {
-               DBG(cdev, "reset cdc subset\n");
-               gether_disconnect(&geth->port);
-       }
-
-       DBG(cdev, "init + activate cdc subset\n");
-       if (config_ep_by_speed(cdev->gadget, f, geth->port.in_ep) ||
-           config_ep_by_speed(cdev->gadget, f, geth->port.out_ep)) {
-               geth->port.in_ep->desc = NULL;
-               geth->port.out_ep->desc = NULL;
-               return -EINVAL;
-       }
-
-       net = gether_connect(&geth->port);
-       return PTR_ERR_OR_ZERO(net);
-}
-
-static void geth_disable(struct usb_function *f)
-{
-       struct f_gether *geth = func_to_geth(f);
-       struct usb_composite_dev *cdev = f->config->cdev;
-
-       DBG(cdev, "net deactivated\n");
-       gether_disconnect(&geth->port);
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* serial function driver setup/binding */
-
-static int
-geth_bind(struct usb_configuration *c, struct usb_function *f)
-{
-       struct usb_composite_dev *cdev = c->cdev;
-       struct f_gether         *geth = func_to_geth(f);
-       struct usb_string       *us;
-       int                     status;
-       struct usb_ep           *ep;
-
-       struct f_gether_opts    *gether_opts;
-
-       gether_opts = container_of(f->fi, struct f_gether_opts, func_inst);
-
-       /*
-        * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
-        * configurations are bound in sequence with list_for_each_entry,
-        * in each configuration its functions are bound in sequence
-        * with list_for_each_entry, so we assume no race condition
-        * with regard to gether_opts->bound access
-        */
-       if (!gether_opts->bound) {
-               mutex_lock(&gether_opts->lock);
-               gether_set_gadget(gether_opts->net, cdev->gadget);
-               status = gether_register_netdev(gether_opts->net);
-               mutex_unlock(&gether_opts->lock);
-               if (status)
-                       return status;
-               gether_opts->bound = true;
-       }
-
-       us = usb_gstrings_attach(cdev, geth_strings,
-                                ARRAY_SIZE(geth_string_defs));
-       if (IS_ERR(us))
-               return PTR_ERR(us);
-
-       subset_data_intf.iInterface = us[0].id;
-       ether_desc.iMACAddress = us[1].id;
-
-       /* allocate instance-specific interface IDs */
-       status = usb_interface_id(c, f);
-       if (status < 0)
-               goto fail;
-       subset_data_intf.bInterfaceNumber = status;
-
-       status = -ENODEV;
-
-       /* allocate instance-specific endpoints */
-       ep = usb_ep_autoconfig(cdev->gadget, &fs_subset_in_desc);
-       if (!ep)
-               goto fail;
-       geth->port.in_ep = ep;
-       ep->driver_data = cdev; /* claim */
-
-       ep = usb_ep_autoconfig(cdev->gadget, &fs_subset_out_desc);
-       if (!ep)
-               goto fail;
-       geth->port.out_ep = ep;
-       ep->driver_data = cdev; /* claim */
-
-       /* support all relevant hardware speeds... we expect that when
-        * hardware is dual speed, all bulk-capable endpoints work at
-        * both speeds
-        */
-       hs_subset_in_desc.bEndpointAddress = fs_subset_in_desc.bEndpointAddress;
-       hs_subset_out_desc.bEndpointAddress =
-               fs_subset_out_desc.bEndpointAddress;
-
-       ss_subset_in_desc.bEndpointAddress = fs_subset_in_desc.bEndpointAddress;
-       ss_subset_out_desc.bEndpointAddress =
-               fs_subset_out_desc.bEndpointAddress;
-
-       status = usb_assign_descriptors(f, fs_eth_function, hs_eth_function,
-                       ss_eth_function);
-       if (status)
-               goto fail;
-
-       /* NOTE:  all that is done without knowing or caring about
-        * the network link ... which is unavailable to this code
-        * until we're activated via set_alt().
-        */
-
-       DBG(cdev, "CDC Subset: %s speed IN/%s OUT/%s\n",
-                       gadget_is_superspeed(c->cdev->gadget) ? "super" :
-                       gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
-                       geth->port.in_ep->name, geth->port.out_ep->name);
-       return 0;
-
-fail:
-       usb_free_all_descriptors(f);
-       /* we might as well release our claims on endpoints */
-       if (geth->port.out_ep)
-               geth->port.out_ep->driver_data = NULL;
-       if (geth->port.in_ep)
-               geth->port.in_ep->driver_data = NULL;
-
-       ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
-
-       return status;
-}
-
-static inline struct f_gether_opts *to_f_gether_opts(struct config_item *item)
-{
-       return container_of(to_config_group(item), struct f_gether_opts,
-                           func_inst.group);
-}
-
-/* f_gether_item_ops */
-USB_ETHERNET_CONFIGFS_ITEM(gether);
-
-/* f_gether_opts_dev_addr */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(gether);
-
-/* f_gether_opts_host_addr */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(gether);
-
-/* f_gether_opts_qmult */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(gether);
-
-/* f_gether_opts_ifname */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(gether);
-
-static struct configfs_attribute *gether_attrs[] = {
-       &f_gether_opts_dev_addr.attr,
-       &f_gether_opts_host_addr.attr,
-       &f_gether_opts_qmult.attr,
-       &f_gether_opts_ifname.attr,
-       NULL,
-};
-
-static struct config_item_type gether_func_type = {
-       .ct_item_ops    = &gether_item_ops,
-       .ct_attrs       = gether_attrs,
-       .ct_owner       = THIS_MODULE,
-};
-
-static void geth_free_inst(struct usb_function_instance *f)
-{
-       struct f_gether_opts *opts;
-
-       opts = container_of(f, struct f_gether_opts, func_inst);
-       if (opts->bound)
-               gether_cleanup(netdev_priv(opts->net));
-       else
-               free_netdev(opts->net);
-       kfree(opts);
-}
-
-static struct usb_function_instance *geth_alloc_inst(void)
-{
-       struct f_gether_opts *opts;
-
-       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
-       if (!opts)
-               return ERR_PTR(-ENOMEM);
-       mutex_init(&opts->lock);
-       opts->func_inst.free_func_inst = geth_free_inst;
-       opts->net = gether_setup_default();
-       if (IS_ERR(opts->net)) {
-               struct net_device *net = opts->net;
-               kfree(opts);
-               return ERR_CAST(net);
-       }
-
-       config_group_init_type_name(&opts->func_inst.group, "",
-                                   &gether_func_type);
-
-       return &opts->func_inst;
-}
-
-static void geth_free(struct usb_function *f)
-{
-       struct f_gether *eth;
-
-       eth = func_to_geth(f);
-       kfree(eth);
-}
-
-static void geth_unbind(struct usb_configuration *c, struct usb_function *f)
-{
-       geth_string_defs[0].id = 0;
-       usb_free_all_descriptors(f);
-}
-
-static struct usb_function *geth_alloc(struct usb_function_instance *fi)
-{
-       struct f_gether *geth;
-       struct f_gether_opts *opts;
-       int status;
-
-       /* allocate and initialize one new instance */
-       geth = kzalloc(sizeof(*geth), GFP_KERNEL);
-       if (!geth)
-               return ERR_PTR(-ENOMEM);
-
-       opts = container_of(fi, struct f_gether_opts, func_inst);
-
-       mutex_lock(&opts->lock);
-       opts->refcnt++;
-       /* export host's Ethernet address in CDC format */
-       status = gether_get_host_addr_cdc(opts->net, geth->ethaddr,
-                                         sizeof(geth->ethaddr));
-       if (status < 12) {
-               kfree(geth);
-               mutex_unlock(&opts->lock);
-               return ERR_PTR(-EINVAL);
-       }
-       geth_string_defs[1].s = geth->ethaddr;
-
-       geth->port.ioport = netdev_priv(opts->net);
-       mutex_unlock(&opts->lock);
-       geth->port.cdc_filter = DEFAULT_FILTER;
-
-       geth->port.func.name = "cdc_subset";
-       geth->port.func.bind = geth_bind;
-       geth->port.func.unbind = geth_unbind;
-       geth->port.func.set_alt = geth_set_alt;
-       geth->port.func.disable = geth_disable;
-       geth->port.func.free_func = geth_free;
-
-       return &geth->port.func;
-}
-
-DECLARE_USB_FUNCTION_INIT(geth, geth_alloc_inst, geth_alloc);
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/f_uac1.c b/drivers/usb/gadget/f_uac1.c
deleted file mode 100644 (file)
index 2b4c82d..0000000
+++ /dev/null
@@ -1,768 +0,0 @@
-/*
- * f_audio.c -- USB Audio class function driver
-  *
- * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
- * Copyright (C) 2008 Analog Devices, Inc
- *
- * Enter bugs at http://blackfin.uclinux.org/
- *
- * Licensed under the GPL-2 or later.
- */
-
-#include <linux/slab.h>
-#include <linux/kernel.h>
-#include <linux/device.h>
-#include <linux/atomic.h>
-
-#include "u_uac1.h"
-
-#define OUT_EP_MAX_PACKET_SIZE 200
-static int req_buf_size = OUT_EP_MAX_PACKET_SIZE;
-module_param(req_buf_size, int, S_IRUGO);
-MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size");
-
-static int req_count = 256;
-module_param(req_count, int, S_IRUGO);
-MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count");
-
-static int audio_buf_size = 48000;
-module_param(audio_buf_size, int, S_IRUGO);
-MODULE_PARM_DESC(audio_buf_size, "Audio buffer size");
-
-static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value);
-static int generic_get_cmd(struct usb_audio_control *con, u8 cmd);
-
-/*
- * DESCRIPTORS ... most are static, but strings and full
- * configuration descriptors are built on demand.
- */
-
-/*
- * We have two interfaces- AudioControl and AudioStreaming
- * TODO: only supcard playback currently
- */
-#define F_AUDIO_AC_INTERFACE   0
-#define F_AUDIO_AS_INTERFACE   1
-#define F_AUDIO_NUM_INTERFACES 2
-
-/* B.3.1  Standard AC Interface Descriptor */
-static struct usb_interface_descriptor ac_interface_desc __initdata = {
-       .bLength =              USB_DT_INTERFACE_SIZE,
-       .bDescriptorType =      USB_DT_INTERFACE,
-       .bNumEndpoints =        0,
-       .bInterfaceClass =      USB_CLASS_AUDIO,
-       .bInterfaceSubClass =   USB_SUBCLASS_AUDIOCONTROL,
-};
-
-DECLARE_UAC_AC_HEADER_DESCRIPTOR(2);
-
-#define UAC_DT_AC_HEADER_LENGTH        UAC_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES)
-/* 1 input terminal, 1 output terminal and 1 feature unit */
-#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH + UAC_DT_INPUT_TERMINAL_SIZE \
-       + UAC_DT_OUTPUT_TERMINAL_SIZE + UAC_DT_FEATURE_UNIT_SIZE(0))
-/* B.3.2  Class-Specific AC Interface Descriptor */
-static struct uac1_ac_header_descriptor_2 ac_header_desc = {
-       .bLength =              UAC_DT_AC_HEADER_LENGTH,
-       .bDescriptorType =      USB_DT_CS_INTERFACE,
-       .bDescriptorSubtype =   UAC_HEADER,
-       .bcdADC =               __constant_cpu_to_le16(0x0100),
-       .wTotalLength =         __constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH),
-       .bInCollection =        F_AUDIO_NUM_INTERFACES,
-       .baInterfaceNr = {
-               [0] =           F_AUDIO_AC_INTERFACE,
-               [1] =           F_AUDIO_AS_INTERFACE,
-       }
-};
-
-#define INPUT_TERMINAL_ID      1
-static struct uac_input_terminal_descriptor input_terminal_desc = {
-       .bLength =              UAC_DT_INPUT_TERMINAL_SIZE,
-       .bDescriptorType =      USB_DT_CS_INTERFACE,
-       .bDescriptorSubtype =   UAC_INPUT_TERMINAL,
-       .bTerminalID =          INPUT_TERMINAL_ID,
-       .wTerminalType =        UAC_TERMINAL_STREAMING,
-       .bAssocTerminal =       0,
-       .wChannelConfig =       0x3,
-};
-
-DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0);
-
-#define FEATURE_UNIT_ID                2
-static struct uac_feature_unit_descriptor_0 feature_unit_desc = {
-       .bLength                = UAC_DT_FEATURE_UNIT_SIZE(0),
-       .bDescriptorType        = USB_DT_CS_INTERFACE,
-       .bDescriptorSubtype     = UAC_FEATURE_UNIT,
-       .bUnitID                = FEATURE_UNIT_ID,
-       .bSourceID              = INPUT_TERMINAL_ID,
-       .bControlSize           = 2,
-       .bmaControls[0]         = (UAC_FU_MUTE | UAC_FU_VOLUME),
-};
-
-static struct usb_audio_control mute_control = {
-       .list = LIST_HEAD_INIT(mute_control.list),
-       .name = "Mute Control",
-       .type = UAC_FU_MUTE,
-       /* Todo: add real Mute control code */
-       .set = generic_set_cmd,
-       .get = generic_get_cmd,
-};
-
-static struct usb_audio_control volume_control = {
-       .list = LIST_HEAD_INIT(volume_control.list),
-       .name = "Volume Control",
-       .type = UAC_FU_VOLUME,
-       /* Todo: add real Volume control code */
-       .set = generic_set_cmd,
-       .get = generic_get_cmd,
-};
-
-static struct usb_audio_control_selector feature_unit = {
-       .list = LIST_HEAD_INIT(feature_unit.list),
-       .id = FEATURE_UNIT_ID,
-       .name = "Mute & Volume Control",
-       .type = UAC_FEATURE_UNIT,
-       .desc = (struct usb_descriptor_header *)&feature_unit_desc,
-};
-
-#define OUTPUT_TERMINAL_ID     3
-static struct uac1_output_terminal_descriptor output_terminal_desc = {
-       .bLength                = UAC_DT_OUTPUT_TERMINAL_SIZE,
-       .bDescriptorType        = USB_DT_CS_INTERFACE,
-       .bDescriptorSubtype     = UAC_OUTPUT_TERMINAL,
-       .bTerminalID            = OUTPUT_TERMINAL_ID,
-       .wTerminalType          = UAC_OUTPUT_TERMINAL_SPEAKER,
-       .bAssocTerminal         = FEATURE_UNIT_ID,
-       .bSourceID              = FEATURE_UNIT_ID,
-};
-
-/* B.4.1  Standard AS Interface Descriptor */
-static struct usb_interface_descriptor as_interface_alt_0_desc = {
-       .bLength =              USB_DT_INTERFACE_SIZE,
-       .bDescriptorType =      USB_DT_INTERFACE,
-       .bAlternateSetting =    0,
-       .bNumEndpoints =        0,
-       .bInterfaceClass =      USB_CLASS_AUDIO,
-       .bInterfaceSubClass =   USB_SUBCLASS_AUDIOSTREAMING,
-};
-
-static struct usb_interface_descriptor as_interface_alt_1_desc = {
-       .bLength =              USB_DT_INTERFACE_SIZE,
-       .bDescriptorType =      USB_DT_INTERFACE,
-       .bAlternateSetting =    1,
-       .bNumEndpoints =        1,
-       .bInterfaceClass =      USB_CLASS_AUDIO,
-       .bInterfaceSubClass =   USB_SUBCLASS_AUDIOSTREAMING,
-};
-
-/* B.4.2  Class-Specific AS Interface Descriptor */
-static struct uac1_as_header_descriptor as_header_desc = {
-       .bLength =              UAC_DT_AS_HEADER_SIZE,
-       .bDescriptorType =      USB_DT_CS_INTERFACE,
-       .bDescriptorSubtype =   UAC_AS_GENERAL,
-       .bTerminalLink =        INPUT_TERMINAL_ID,
-       .bDelay =               1,
-       .wFormatTag =           UAC_FORMAT_TYPE_I_PCM,
-};
-
-DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1);
-
-static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = {
-       .bLength =              UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
-       .bDescriptorType =      USB_DT_CS_INTERFACE,
-       .bDescriptorSubtype =   UAC_FORMAT_TYPE,
-       .bFormatType =          UAC_FORMAT_TYPE_I,
-       .bSubframeSize =        2,
-       .bBitResolution =       16,
-       .bSamFreqType =         1,
-};
-
-/* Standard ISO OUT Endpoint Descriptor */
-static struct usb_endpoint_descriptor as_out_ep_desc  = {
-       .bLength =              USB_DT_ENDPOINT_AUDIO_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-       .bEndpointAddress =     USB_DIR_OUT,
-       .bmAttributes =         USB_ENDPOINT_SYNC_ADAPTIVE
-                               | USB_ENDPOINT_XFER_ISOC,
-       .wMaxPacketSize =       __constant_cpu_to_le16(OUT_EP_MAX_PACKET_SIZE),
-       .bInterval =            4,
-};
-
-/* Class-specific AS ISO OUT Endpoint Descriptor */
-static struct uac_iso_endpoint_descriptor as_iso_out_desc __initdata = {
-       .bLength =              UAC_ISO_ENDPOINT_DESC_SIZE,
-       .bDescriptorType =      USB_DT_CS_ENDPOINT,
-       .bDescriptorSubtype =   UAC_EP_GENERAL,
-       .bmAttributes =         1,
-       .bLockDelayUnits =      1,
-       .wLockDelay =           __constant_cpu_to_le16(1),
-};
-
-static struct usb_descriptor_header *f_audio_desc[] __initdata = {
-       (struct usb_descriptor_header *)&ac_interface_desc,
-       (struct usb_descriptor_header *)&ac_header_desc,
-
-       (struct usb_descriptor_header *)&input_terminal_desc,
-       (struct usb_descriptor_header *)&output_terminal_desc,
-       (struct usb_descriptor_header *)&feature_unit_desc,
-
-       (struct usb_descriptor_header *)&as_interface_alt_0_desc,
-       (struct usb_descriptor_header *)&as_interface_alt_1_desc,
-       (struct usb_descriptor_header *)&as_header_desc,
-
-       (struct usb_descriptor_header *)&as_type_i_desc,
-
-       (struct usb_descriptor_header *)&as_out_ep_desc,
-       (struct usb_descriptor_header *)&as_iso_out_desc,
-       NULL,
-};
-
-/*
- * This function is an ALSA sound card following USB Audio Class Spec 1.0.
- */
-
-/*-------------------------------------------------------------------------*/
-struct f_audio_buf {
-       u8 *buf;
-       int actual;
-       struct list_head list;
-};
-
-static struct f_audio_buf *f_audio_buffer_alloc(int buf_size)
-{
-       struct f_audio_buf *copy_buf;
-
-       copy_buf = kzalloc(sizeof *copy_buf, GFP_ATOMIC);
-       if (!copy_buf)
-               return ERR_PTR(-ENOMEM);
-
-       copy_buf->buf = kzalloc(buf_size, GFP_ATOMIC);
-       if (!copy_buf->buf) {
-               kfree(copy_buf);
-               return ERR_PTR(-ENOMEM);
-       }
-
-       return copy_buf;
-}
-
-static void f_audio_buffer_free(struct f_audio_buf *audio_buf)
-{
-       kfree(audio_buf->buf);
-       kfree(audio_buf);
-}
-/*-------------------------------------------------------------------------*/
-
-struct f_audio {
-       struct gaudio                   card;
-
-       /* endpoints handle full and/or high speeds */
-       struct usb_ep                   *out_ep;
-
-       spinlock_t                      lock;
-       struct f_audio_buf *copy_buf;
-       struct work_struct playback_work;
-       struct list_head play_queue;
-
-       /* Control Set command */
-       struct list_head cs;
-       u8 set_cmd;
-       struct usb_audio_control *set_con;
-};
-
-static inline struct f_audio *func_to_audio(struct usb_function *f)
-{
-       return container_of(f, struct f_audio, card.func);
-}
-
-/*-------------------------------------------------------------------------*/
-
-static void f_audio_playback_work(struct work_struct *data)
-{
-       struct f_audio *audio = container_of(data, struct f_audio,
-                                       playback_work);
-       struct f_audio_buf *play_buf;
-
-       spin_lock_irq(&audio->lock);
-       if (list_empty(&audio->play_queue)) {
-               spin_unlock_irq(&audio->lock);
-               return;
-       }
-       play_buf = list_first_entry(&audio->play_queue,
-                       struct f_audio_buf, list);
-       list_del(&play_buf->list);
-       spin_unlock_irq(&audio->lock);
-
-       u_audio_playback(&audio->card, play_buf->buf, play_buf->actual);
-       f_audio_buffer_free(play_buf);
-}
-
-static int f_audio_out_ep_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       struct f_audio *audio = req->context;
-       struct usb_composite_dev *cdev = audio->card.func.config->cdev;
-       struct f_audio_buf *copy_buf = audio->copy_buf;
-       int err;
-
-       if (!copy_buf)
-               return -EINVAL;
-
-       /* Copy buffer is full, add it to the play_queue */
-       if (audio_buf_size - copy_buf->actual < req->actual) {
-               list_add_tail(&copy_buf->list, &audio->play_queue);
-               schedule_work(&audio->playback_work);
-               copy_buf = f_audio_buffer_alloc(audio_buf_size);
-               if (IS_ERR(copy_buf))
-                       return -ENOMEM;
-       }
-
-       memcpy(copy_buf->buf + copy_buf->actual, req->buf, req->actual);
-       copy_buf->actual += req->actual;
-       audio->copy_buf = copy_buf;
-
-       err = usb_ep_queue(ep, req, GFP_ATOMIC);
-       if (err)
-               ERROR(cdev, "%s queue req: %d\n", ep->name, err);
-
-       return 0;
-
-}
-
-static void f_audio_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       struct f_audio *audio = req->context;
-       int status = req->status;
-       u32 data = 0;
-       struct usb_ep *out_ep = audio->out_ep;
-
-       switch (status) {
-
-       case 0:                         /* normal completion? */
-               if (ep == out_ep)
-                       f_audio_out_ep_complete(ep, req);
-               else if (audio->set_con) {
-                       memcpy(&data, req->buf, req->length);
-                       audio->set_con->set(audio->set_con, audio->set_cmd,
-                                       le16_to_cpu(data));
-                       audio->set_con = NULL;
-               }
-               break;
-       default:
-               break;
-       }
-}
-
-static int audio_set_intf_req(struct usb_function *f,
-               const struct usb_ctrlrequest *ctrl)
-{
-       struct f_audio          *audio = func_to_audio(f);
-       struct usb_composite_dev *cdev = f->config->cdev;
-       struct usb_request      *req = cdev->req;
-       u8                      id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
-       u16                     len = le16_to_cpu(ctrl->wLength);
-       u16                     w_value = le16_to_cpu(ctrl->wValue);
-       u8                      con_sel = (w_value >> 8) & 0xFF;
-       u8                      cmd = (ctrl->bRequest & 0x0F);
-       struct usb_audio_control_selector *cs;
-       struct usb_audio_control *con;
-
-       DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n",
-                       ctrl->bRequest, w_value, len, id);
-
-       list_for_each_entry(cs, &audio->cs, list) {
-               if (cs->id == id) {
-                       list_for_each_entry(con, &cs->control, list) {
-                               if (con->type == con_sel) {
-                                       audio->set_con = con;
-                                       break;
-                               }
-                       }
-                       break;
-               }
-       }
-
-       audio->set_cmd = cmd;
-       req->context = audio;
-       req->complete = f_audio_complete;
-
-       return len;
-}
-
-static int audio_get_intf_req(struct usb_function *f,
-               const struct usb_ctrlrequest *ctrl)
-{
-       struct f_audio          *audio = func_to_audio(f);
-       struct usb_composite_dev *cdev = f->config->cdev;
-       struct usb_request      *req = cdev->req;
-       int                     value = -EOPNOTSUPP;
-       u8                      id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
-       u16                     len = le16_to_cpu(ctrl->wLength);
-       u16                     w_value = le16_to_cpu(ctrl->wValue);
-       u8                      con_sel = (w_value >> 8) & 0xFF;
-       u8                      cmd = (ctrl->bRequest & 0x0F);
-       struct usb_audio_control_selector *cs;
-       struct usb_audio_control *con;
-
-       DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n",
-                       ctrl->bRequest, w_value, len, id);
-
-       list_for_each_entry(cs, &audio->cs, list) {
-               if (cs->id == id) {
-                       list_for_each_entry(con, &cs->control, list) {
-                               if (con->type == con_sel && con->get) {
-                                       value = con->get(con, cmd);
-                                       break;
-                               }
-                       }
-                       break;
-               }
-       }
-
-       req->context = audio;
-       req->complete = f_audio_complete;
-       len = min_t(size_t, sizeof(value), len);
-       memcpy(req->buf, &value, len);
-
-       return len;
-}
-
-static int audio_set_endpoint_req(struct usb_function *f,
-               const struct usb_ctrlrequest *ctrl)
-{
-       struct usb_composite_dev *cdev = f->config->cdev;
-       int                     value = -EOPNOTSUPP;
-       u16                     ep = le16_to_cpu(ctrl->wIndex);
-       u16                     len = le16_to_cpu(ctrl->wLength);
-       u16                     w_value = le16_to_cpu(ctrl->wValue);
-
-       DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
-                       ctrl->bRequest, w_value, len, ep);
-
-       switch (ctrl->bRequest) {
-       case UAC_SET_CUR:
-               value = len;
-               break;
-
-       case UAC_SET_MIN:
-               break;
-
-       case UAC_SET_MAX:
-               break;
-
-       case UAC_SET_RES:
-               break;
-
-       case UAC_SET_MEM:
-               break;
-
-       default:
-               break;
-       }
-
-       return value;
-}
-
-static int audio_get_endpoint_req(struct usb_function *f,
-               const struct usb_ctrlrequest *ctrl)
-{
-       struct usb_composite_dev *cdev = f->config->cdev;
-       int value = -EOPNOTSUPP;
-       u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
-       u16 len = le16_to_cpu(ctrl->wLength);
-       u16 w_value = le16_to_cpu(ctrl->wValue);
-
-       DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
-                       ctrl->bRequest, w_value, len, ep);
-
-       switch (ctrl->bRequest) {
-       case UAC_GET_CUR:
-       case UAC_GET_MIN:
-       case UAC_GET_MAX:
-       case UAC_GET_RES:
-               value = len;
-               break;
-       case UAC_GET_MEM:
-               break;
-       default:
-               break;
-       }
-
-       return value;
-}
-
-static int
-f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
-{
-       struct usb_composite_dev *cdev = f->config->cdev;
-       struct usb_request      *req = cdev->req;
-       int                     value = -EOPNOTSUPP;
-       u16                     w_index = le16_to_cpu(ctrl->wIndex);
-       u16                     w_value = le16_to_cpu(ctrl->wValue);
-       u16                     w_length = le16_to_cpu(ctrl->wLength);
-
-       /* composite driver infrastructure handles everything; interface
-        * activation uses set_alt().
-        */
-       switch (ctrl->bRequestType) {
-       case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE:
-               value = audio_set_intf_req(f, ctrl);
-               break;
-
-       case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE:
-               value = audio_get_intf_req(f, ctrl);
-               break;
-
-       case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
-               value = audio_set_endpoint_req(f, ctrl);
-               break;
-
-       case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
-               value = audio_get_endpoint_req(f, ctrl);
-               break;
-
-       default:
-               ERROR(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
-                       ctrl->bRequestType, ctrl->bRequest,
-                       w_value, w_index, w_length);
-       }
-
-       /* respond with data transfer or status phase? */
-       if (value >= 0) {
-               DBG(cdev, "audio req%02x.%02x v%04x i%04x l%d\n",
-                       ctrl->bRequestType, ctrl->bRequest,
-                       w_value, w_index, w_length);
-               req->zero = 0;
-               req->length = value;
-               value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
-               if (value < 0)
-                       ERROR(cdev, "audio response on err %d\n", value);
-       }
-
-       /* device either stalls (value < 0) or reports success */
-       return value;
-}
-
-static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
-{
-       struct f_audio          *audio = func_to_audio(f);
-       struct usb_composite_dev *cdev = f->config->cdev;
-       struct usb_ep *out_ep = audio->out_ep;
-       struct usb_request *req;
-       int i = 0, err = 0;
-
-       DBG(cdev, "intf %d, alt %d\n", intf, alt);
-
-       if (intf == 1) {
-               if (alt == 1) {
-                       usb_ep_enable(out_ep);
-                       out_ep->driver_data = audio;
-                       audio->copy_buf = f_audio_buffer_alloc(audio_buf_size);
-                       if (IS_ERR(audio->copy_buf))
-                               return -ENOMEM;
-
-                       /*
-                        * allocate a bunch of read buffers
-                        * and queue them all at once.
-                        */
-                       for (i = 0; i < req_count && err == 0; i++) {
-                               req = usb_ep_alloc_request(out_ep, GFP_ATOMIC);
-                               if (req) {
-                                       req->buf = kzalloc(req_buf_size,
-                                                       GFP_ATOMIC);
-                                       if (req->buf) {
-                                               req->length = req_buf_size;
-                                               req->context = audio;
-                                               req->complete =
-                                                       f_audio_complete;
-                                               err = usb_ep_queue(out_ep,
-                                                       req, GFP_ATOMIC);
-                                               if (err)
-                                                       ERROR(cdev,
-                                                       "%s queue req: %d\n",
-                                                       out_ep->name, err);
-                                       } else
-                                               err = -ENOMEM;
-                               } else
-                                       err = -ENOMEM;
-                       }
-
-               } else {
-                       struct f_audio_buf *copy_buf = audio->copy_buf;
-                       if (copy_buf) {
-                               list_add_tail(&copy_buf->list,
-                                               &audio->play_queue);
-                               schedule_work(&audio->playback_work);
-                       }
-               }
-       }
-
-       return err;
-}
-
-static void f_audio_disable(struct usb_function *f)
-{
-       return;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static void f_audio_build_desc(struct f_audio *audio)
-{
-       struct gaudio *card = &audio->card;
-       u8 *sam_freq;
-       int rate;
-
-       /* Set channel numbers */
-       input_terminal_desc.bNrChannels = u_audio_get_playback_channels(card);
-       as_type_i_desc.bNrChannels = u_audio_get_playback_channels(card);
-
-       /* Set sample rates */
-       rate = u_audio_get_playback_rate(card);
-       sam_freq = as_type_i_desc.tSamFreq[0];
-       memcpy(sam_freq, &rate, 3);
-
-       /* Todo: Set Sample bits and other parameters */
-
-       return;
-}
-
-/* audio function driver setup/binding */
-static int __init
-f_audio_bind(struct usb_configuration *c, struct usb_function *f)
-{
-       struct usb_composite_dev *cdev = c->cdev;
-       struct f_audio          *audio = func_to_audio(f);
-       int                     status;
-       struct usb_ep           *ep = NULL;
-
-       f_audio_build_desc(audio);
-
-       /* allocate instance-specific interface IDs, and patch descriptors */
-       status = usb_interface_id(c, f);
-       if (status < 0)
-               goto fail;
-       ac_interface_desc.bInterfaceNumber = status;
-
-       status = usb_interface_id(c, f);
-       if (status < 0)
-               goto fail;
-       as_interface_alt_0_desc.bInterfaceNumber = status;
-       as_interface_alt_1_desc.bInterfaceNumber = status;
-
-       status = -ENODEV;
-
-       /* allocate instance-specific endpoints */
-       ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc);
-       if (!ep)
-               goto fail;
-       audio->out_ep = ep;
-       audio->out_ep->desc = &as_out_ep_desc;
-       ep->driver_data = cdev; /* claim */
-
-       status = -ENOMEM;
-
-       /* copy descriptors, and track endpoint copies */
-       status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL);
-       if (status)
-               goto fail;
-       return 0;
-
-fail:
-       if (ep)
-               ep->driver_data = NULL;
-       return status;
-}
-
-static void
-f_audio_unbind(struct usb_configuration *c, struct usb_function *f)
-{
-       struct f_audio          *audio = func_to_audio(f);
-
-       usb_free_all_descriptors(f);
-       kfree(audio);
-}
-
-/*-------------------------------------------------------------------------*/
-
-static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value)
-{
-       con->data[cmd] = value;
-
-       return 0;
-}
-
-static int generic_get_cmd(struct usb_audio_control *con, u8 cmd)
-{
-       return con->data[cmd];
-}
-
-/* Todo: add more control selecotor dynamically */
-static int __init control_selector_init(struct f_audio *audio)
-{
-       INIT_LIST_HEAD(&audio->cs);
-       list_add(&feature_unit.list, &audio->cs);
-
-       INIT_LIST_HEAD(&feature_unit.control);
-       list_add(&mute_control.list, &feature_unit.control);
-       list_add(&volume_control.list, &feature_unit.control);
-
-       volume_control.data[UAC__CUR] = 0xffc0;
-       volume_control.data[UAC__MIN] = 0xe3a0;
-       volume_control.data[UAC__MAX] = 0xfff0;
-       volume_control.data[UAC__RES] = 0x0030;
-
-       return 0;
-}
-
-/**
- * audio_bind_config - add USB audio function to a configuration
- * @c: the configuration to supcard the USB audio function
- * Context: single threaded during gadget setup
- *
- * Returns zero on success, else negative errno.
- */
-static int __init audio_bind_config(struct usb_configuration *c)
-{
-       struct f_audio *audio;
-       int status;
-
-       /* allocate and initialize one new instance */
-       audio = kzalloc(sizeof *audio, GFP_KERNEL);
-       if (!audio)
-               return -ENOMEM;
-
-       audio->card.func.name = "g_audio";
-       audio->card.gadget = c->cdev->gadget;
-
-       INIT_LIST_HEAD(&audio->play_queue);
-       spin_lock_init(&audio->lock);
-
-       /* set up ASLA audio devices */
-       status = gaudio_setup(&audio->card);
-       if (status < 0)
-               goto setup_fail;
-
-       audio->card.func.strings = audio_strings;
-       audio->card.func.bind = f_audio_bind;
-       audio->card.func.unbind = f_audio_unbind;
-       audio->card.func.set_alt = f_audio_set_alt;
-       audio->card.func.setup = f_audio_setup;
-       audio->card.func.disable = f_audio_disable;
-
-       control_selector_init(audio);
-
-       INIT_WORK(&audio->playback_work, f_audio_playback_work);
-
-       status = usb_add_function(c, &audio->card.func);
-       if (status)
-               goto add_fail;
-
-       INFO(c->cdev, "audio_buf_size %d, req_buf_size %d, req_count %d\n",
-               audio_buf_size, req_buf_size, req_count);
-
-       return status;
-
-add_fail:
-       gaudio_cleanup();
-setup_fail:
-       kfree(audio);
-       return status;
-}
diff --git a/drivers/usb/gadget/f_uac2.c b/drivers/usb/gadget/f_uac2.c
deleted file mode 100644 (file)
index 6261db4..0000000
+++ /dev/null
@@ -1,1354 +0,0 @@
-/*
- * f_uac2.c -- USB Audio Class 2.0 Function
- *
- * Copyright (C) 2011
- *    Yadwinder Singh (yadi.brar01@gmail.com)
- *    Jaswinder Singh (jaswinder.singh@linaro.org)
- *
- * 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.
- */
-
-#include <linux/usb/audio.h>
-#include <linux/usb/audio-v2.h>
-#include <linux/platform_device.h>
-#include <linux/module.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-
-/* Playback(USB-IN) Default Stereo - Fl/Fr */
-static int p_chmask = 0x3;
-module_param(p_chmask, uint, S_IRUGO);
-MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
-
-/* Playback Default 48 KHz */
-static int p_srate = 48000;
-module_param(p_srate, uint, S_IRUGO);
-MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
-
-/* Playback Default 16bits/sample */
-static int p_ssize = 2;
-module_param(p_ssize, uint, S_IRUGO);
-MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)");
-
-/* Capture(USB-OUT) Default Stereo - Fl/Fr */
-static int c_chmask = 0x3;
-module_param(c_chmask, uint, S_IRUGO);
-MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
-
-/* Capture Default 64 KHz */
-static int c_srate = 64000;
-module_param(c_srate, uint, S_IRUGO);
-MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
-
-/* Capture Default 16bits/sample */
-static int c_ssize = 2;
-module_param(c_ssize, uint, S_IRUGO);
-MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
-
-/* Keep everyone on toes */
-#define USB_XFERS      2
-
-/*
- * The driver implements a simple UAC_2 topology.
- * USB-OUT -> IT_1 -> OT_3 -> ALSA_Capture
- * ALSA_Playback -> IT_2 -> OT_4 -> USB-IN
- * Capture and Playback sampling rates are independently
- *  controlled by two clock sources :
- *    CLK_5 := c_srate, and CLK_6 := p_srate
- */
-#define USB_OUT_IT_ID  1
-#define IO_IN_IT_ID    2
-#define IO_OUT_OT_ID   3
-#define USB_IN_OT_ID   4
-#define USB_OUT_CLK_ID 5
-#define USB_IN_CLK_ID  6
-
-#define CONTROL_ABSENT 0
-#define CONTROL_RDONLY 1
-#define CONTROL_RDWR   3
-
-#define CLK_FREQ_CTRL  0
-#define CLK_VLD_CTRL   2
-
-#define COPY_CTRL      0
-#define CONN_CTRL      2
-#define OVRLD_CTRL     4
-#define CLSTR_CTRL     6
-#define UNFLW_CTRL     8
-#define OVFLW_CTRL     10
-
-const char *uac2_name = "snd_uac2";
-
-struct uac2_req {
-       struct uac2_rtd_params *pp; /* parent param */
-       struct usb_request *req;
-};
-
-struct uac2_rtd_params {
-       struct snd_uac2_chip *uac2; /* parent chip */
-       bool ep_enabled; /* if the ep is enabled */
-       /* Size of the ring buffer */
-       size_t dma_bytes;
-       unsigned char *dma_area;
-
-       struct snd_pcm_substream *ss;
-
-       /* Ring buffer */
-       ssize_t hw_ptr;
-
-       void *rbuf;
-
-       size_t period_size;
-
-       unsigned max_psize;
-       struct uac2_req ureq[USB_XFERS];
-
-       spinlock_t lock;
-};
-
-struct snd_uac2_chip {
-       struct platform_device pdev;
-       struct platform_driver pdrv;
-
-       struct uac2_rtd_params p_prm;
-       struct uac2_rtd_params c_prm;
-
-       struct snd_card *card;
-       struct snd_pcm *pcm;
-};
-
-#define BUFF_SIZE_MAX  (PAGE_SIZE * 16)
-#define PRD_SIZE_MAX   PAGE_SIZE
-#define MIN_PERIODS    4
-
-static struct snd_pcm_hardware uac2_pcm_hardware = {
-       .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER
-                | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID
-                | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
-       .rates = SNDRV_PCM_RATE_CONTINUOUS,
-       .periods_max = BUFF_SIZE_MAX / PRD_SIZE_MAX,
-       .buffer_bytes_max = BUFF_SIZE_MAX,
-       .period_bytes_max = PRD_SIZE_MAX,
-       .periods_min = MIN_PERIODS,
-};
-
-struct audio_dev {
-       u8 ac_intf, ac_alt;
-       u8 as_out_intf, as_out_alt;
-       u8 as_in_intf, as_in_alt;
-
-       struct usb_ep *in_ep, *out_ep;
-       struct usb_function func;
-
-       /* The ALSA Sound Card it represents on the USB-Client side */
-       struct snd_uac2_chip uac2;
-};
-
-static struct audio_dev *agdev_g;
-
-static inline
-struct audio_dev *func_to_agdev(struct usb_function *f)
-{
-       return container_of(f, struct audio_dev, func);
-}
-
-static inline
-struct audio_dev *uac2_to_agdev(struct snd_uac2_chip *u)
-{
-       return container_of(u, struct audio_dev, uac2);
-}
-
-static inline
-struct snd_uac2_chip *pdev_to_uac2(struct platform_device *p)
-{
-       return container_of(p, struct snd_uac2_chip, pdev);
-}
-
-static inline
-uint num_channels(uint chanmask)
-{
-       uint num = 0;
-
-       while (chanmask) {
-               num += (chanmask & 1);
-               chanmask >>= 1;
-       }
-
-       return num;
-}
-
-static void
-agdev_iso_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       unsigned pending;
-       unsigned long flags;
-       bool update_alsa = false;
-       unsigned char *src, *dst;
-       int status = req->status;
-       struct uac2_req *ur = req->context;
-       struct snd_pcm_substream *substream;
-       struct uac2_rtd_params *prm = ur->pp;
-       struct snd_uac2_chip *uac2 = prm->uac2;
-
-       /* i/f shutting down */
-       if (!prm->ep_enabled || req->status == -ESHUTDOWN)
-               return;
-
-       /*
-        * We can't really do much about bad xfers.
-        * Afterall, the ISOCH xfers could fail legitimately.
-        */
-       if (status)
-               pr_debug("%s: iso_complete status(%d) %d/%d\n",
-                       __func__, status, req->actual, req->length);
-
-       substream = prm->ss;
-
-       /* Do nothing if ALSA isn't active */
-       if (!substream)
-               goto exit;
-
-       spin_lock_irqsave(&prm->lock, flags);
-
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               src = prm->dma_area + prm->hw_ptr;
-               req->actual = req->length;
-               dst = req->buf;
-       } else {
-               dst = prm->dma_area + prm->hw_ptr;
-               src = req->buf;
-       }
-
-       pending = prm->hw_ptr % prm->period_size;
-       pending += req->actual;
-       if (pending >= prm->period_size)
-               update_alsa = true;
-
-       prm->hw_ptr = (prm->hw_ptr + req->actual) % prm->dma_bytes;
-
-       spin_unlock_irqrestore(&prm->lock, flags);
-
-       /* Pack USB load in ALSA ring buffer */
-       memcpy(dst, src, req->actual);
-exit:
-       if (usb_ep_queue(ep, req, GFP_ATOMIC))
-               dev_err(&uac2->pdev.dev, "%d Error!\n", __LINE__);
-
-       if (update_alsa)
-               snd_pcm_period_elapsed(substream);
-
-       return;
-}
-
-static int
-uac2_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-       struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream);
-       struct uac2_rtd_params *prm;
-       unsigned long flags;
-       int err = 0;
-
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               prm = &uac2->p_prm;
-       else
-               prm = &uac2->c_prm;
-
-       spin_lock_irqsave(&prm->lock, flags);
-
-       /* Reset */
-       prm->hw_ptr = 0;
-
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_START:
-       case SNDRV_PCM_TRIGGER_RESUME:
-               prm->ss = substream;
-               break;
-       case SNDRV_PCM_TRIGGER_STOP:
-       case SNDRV_PCM_TRIGGER_SUSPEND:
-               prm->ss = NULL;
-               break;
-       default:
-               err = -EINVAL;
-       }
-
-       spin_unlock_irqrestore(&prm->lock, flags);
-
-       /* Clear buffer after Play stops */
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !prm->ss)
-               memset(prm->rbuf, 0, prm->max_psize * USB_XFERS);
-
-       return err;
-}
-
-static snd_pcm_uframes_t uac2_pcm_pointer(struct snd_pcm_substream *substream)
-{
-       struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream);
-       struct uac2_rtd_params *prm;
-
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               prm = &uac2->p_prm;
-       else
-               prm = &uac2->c_prm;
-
-       return bytes_to_frames(substream->runtime, prm->hw_ptr);
-}
-
-static int uac2_pcm_hw_params(struct snd_pcm_substream *substream,
-                              struct snd_pcm_hw_params *hw_params)
-{
-       struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream);
-       struct uac2_rtd_params *prm;
-       int err;
-
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               prm = &uac2->p_prm;
-       else
-               prm = &uac2->c_prm;
-
-       err = snd_pcm_lib_malloc_pages(substream,
-                                       params_buffer_bytes(hw_params));
-       if (err >= 0) {
-               prm->dma_bytes = substream->runtime->dma_bytes;
-               prm->dma_area = substream->runtime->dma_area;
-               prm->period_size = params_period_bytes(hw_params);
-       }
-
-       return err;
-}
-
-static int uac2_pcm_hw_free(struct snd_pcm_substream *substream)
-{
-       struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream);
-       struct uac2_rtd_params *prm;
-
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               prm = &uac2->p_prm;
-       else
-               prm = &uac2->c_prm;
-
-       prm->dma_area = NULL;
-       prm->dma_bytes = 0;
-       prm->period_size = 0;
-
-       return snd_pcm_lib_free_pages(substream);
-}
-
-static int uac2_pcm_open(struct snd_pcm_substream *substream)
-{
-       struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream);
-       struct snd_pcm_runtime *runtime = substream->runtime;
-
-       runtime->hw = uac2_pcm_hardware;
-
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               spin_lock_init(&uac2->p_prm.lock);
-               runtime->hw.rate_min = p_srate;
-               runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; /* ! p_ssize ! */
-               runtime->hw.channels_min = num_channels(p_chmask);
-               runtime->hw.period_bytes_min = 2 * uac2->p_prm.max_psize
-                                               / runtime->hw.periods_min;
-       } else {
-               spin_lock_init(&uac2->c_prm.lock);
-               runtime->hw.rate_min = c_srate;
-               runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; /* ! c_ssize ! */
-               runtime->hw.channels_min = num_channels(c_chmask);
-               runtime->hw.period_bytes_min = 2 * uac2->c_prm.max_psize
-                                               / runtime->hw.periods_min;
-       }
-
-       runtime->hw.rate_max = runtime->hw.rate_min;
-       runtime->hw.channels_max = runtime->hw.channels_min;
-
-       snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
-
-       return 0;
-}
-
-/* ALSA cries without these function pointers */
-static int uac2_pcm_null(struct snd_pcm_substream *substream)
-{
-       return 0;
-}
-
-static struct snd_pcm_ops uac2_pcm_ops = {
-       .open = uac2_pcm_open,
-       .close = uac2_pcm_null,
-       .ioctl = snd_pcm_lib_ioctl,
-       .hw_params = uac2_pcm_hw_params,
-       .hw_free = uac2_pcm_hw_free,
-       .trigger = uac2_pcm_trigger,
-       .pointer = uac2_pcm_pointer,
-       .prepare = uac2_pcm_null,
-};
-
-static int snd_uac2_probe(struct platform_device *pdev)
-{
-       struct snd_uac2_chip *uac2 = pdev_to_uac2(pdev);
-       struct snd_card *card;
-       struct snd_pcm *pcm;
-       int err;
-
-       /* Choose any slot, with no id */
-       err = snd_card_new(&pdev->dev, -1, NULL, THIS_MODULE, 0, &card);
-       if (err < 0)
-               return err;
-
-       uac2->card = card;
-
-       /*
-        * Create first PCM device
-        * Create a substream only for non-zero channel streams
-        */
-       err = snd_pcm_new(uac2->card, "UAC2 PCM", 0,
-                              p_chmask ? 1 : 0, c_chmask ? 1 : 0, &pcm);
-       if (err < 0)
-               goto snd_fail;
-
-       strcpy(pcm->name, "UAC2 PCM");
-       pcm->private_data = uac2;
-
-       uac2->pcm = pcm;
-
-       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &uac2_pcm_ops);
-       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &uac2_pcm_ops);
-
-       strcpy(card->driver, "UAC2_Gadget");
-       strcpy(card->shortname, "UAC2_Gadget");
-       sprintf(card->longname, "UAC2_Gadget %i", pdev->id);
-
-       snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
-               snd_dma_continuous_data(GFP_KERNEL), 0, BUFF_SIZE_MAX);
-
-       err = snd_card_register(card);
-       if (!err) {
-               platform_set_drvdata(pdev, card);
-               return 0;
-       }
-
-snd_fail:
-       snd_card_free(card);
-
-       uac2->pcm = NULL;
-       uac2->card = NULL;
-
-       return err;
-}
-
-static int snd_uac2_remove(struct platform_device *pdev)
-{
-       struct snd_card *card = platform_get_drvdata(pdev);
-
-       if (card)
-               return snd_card_free(card);
-
-       return 0;
-}
-
-static int alsa_uac2_init(struct audio_dev *agdev)
-{
-       struct snd_uac2_chip *uac2 = &agdev->uac2;
-       int err;
-
-       uac2->pdrv.probe = snd_uac2_probe;
-       uac2->pdrv.remove = snd_uac2_remove;
-       uac2->pdrv.driver.name = uac2_name;
-
-       uac2->pdev.id = 0;
-       uac2->pdev.name = uac2_name;
-
-       /* Register snd_uac2 driver */
-       err = platform_driver_register(&uac2->pdrv);
-       if (err)
-               return err;
-
-       /* Register snd_uac2 device */
-       err = platform_device_register(&uac2->pdev);
-       if (err)
-               platform_driver_unregister(&uac2->pdrv);
-
-       return err;
-}
-
-static void alsa_uac2_exit(struct audio_dev *agdev)
-{
-       struct snd_uac2_chip *uac2 = &agdev->uac2;
-
-       platform_driver_unregister(&uac2->pdrv);
-       platform_device_unregister(&uac2->pdev);
-}
-
-
-/* --------- USB Function Interface ------------- */
-
-enum {
-       STR_ASSOC,
-       STR_IF_CTRL,
-       STR_CLKSRC_IN,
-       STR_CLKSRC_OUT,
-       STR_USB_IT,
-       STR_IO_IT,
-       STR_USB_OT,
-       STR_IO_OT,
-       STR_AS_OUT_ALT0,
-       STR_AS_OUT_ALT1,
-       STR_AS_IN_ALT0,
-       STR_AS_IN_ALT1,
-};
-
-static char clksrc_in[8];
-static char clksrc_out[8];
-
-static struct usb_string strings_fn[] = {
-       [STR_ASSOC].s = "Source/Sink",
-       [STR_IF_CTRL].s = "Topology Control",
-       [STR_CLKSRC_IN].s = clksrc_in,
-       [STR_CLKSRC_OUT].s = clksrc_out,
-       [STR_USB_IT].s = "USBH Out",
-       [STR_IO_IT].s = "USBD Out",
-       [STR_USB_OT].s = "USBH In",
-       [STR_IO_OT].s = "USBD In",
-       [STR_AS_OUT_ALT0].s = "Playback Inactive",
-       [STR_AS_OUT_ALT1].s = "Playback Active",
-       [STR_AS_IN_ALT0].s = "Capture Inactive",
-       [STR_AS_IN_ALT1].s = "Capture Active",
-       { },
-};
-
-static struct usb_gadget_strings str_fn = {
-       .language = 0x0409,     /* en-us */
-       .strings = strings_fn,
-};
-
-static struct usb_gadget_strings *fn_strings[] = {
-       &str_fn,
-       NULL,
-};
-
-static struct usb_qualifier_descriptor devqual_desc = {
-       .bLength = sizeof devqual_desc,
-       .bDescriptorType = USB_DT_DEVICE_QUALIFIER,
-
-       .bcdUSB = cpu_to_le16(0x200),
-       .bDeviceClass = USB_CLASS_MISC,
-       .bDeviceSubClass = 0x02,
-       .bDeviceProtocol = 0x01,
-       .bNumConfigurations = 1,
-       .bRESERVED = 0,
-};
-
-static struct usb_interface_assoc_descriptor iad_desc = {
-       .bLength = sizeof iad_desc,
-       .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
-
-       .bFirstInterface = 0,
-       .bInterfaceCount = 3,
-       .bFunctionClass = USB_CLASS_AUDIO,
-       .bFunctionSubClass = UAC2_FUNCTION_SUBCLASS_UNDEFINED,
-       .bFunctionProtocol = UAC_VERSION_2,
-};
-
-/* Audio Control Interface */
-static struct usb_interface_descriptor std_ac_if_desc = {
-       .bLength = sizeof std_ac_if_desc,
-       .bDescriptorType = USB_DT_INTERFACE,
-
-       .bAlternateSetting = 0,
-       .bNumEndpoints = 0,
-       .bInterfaceClass = USB_CLASS_AUDIO,
-       .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
-       .bInterfaceProtocol = UAC_VERSION_2,
-};
-
-/* Clock source for IN traffic */
-struct uac_clock_source_descriptor in_clk_src_desc = {
-       .bLength = sizeof in_clk_src_desc,
-       .bDescriptorType = USB_DT_CS_INTERFACE,
-
-       .bDescriptorSubtype = UAC2_CLOCK_SOURCE,
-       .bClockID = USB_IN_CLK_ID,
-       .bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED,
-       .bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL),
-       .bAssocTerminal = 0,
-};
-
-/* Clock source for OUT traffic */
-struct uac_clock_source_descriptor out_clk_src_desc = {
-       .bLength = sizeof out_clk_src_desc,
-       .bDescriptorType = USB_DT_CS_INTERFACE,
-
-       .bDescriptorSubtype = UAC2_CLOCK_SOURCE,
-       .bClockID = USB_OUT_CLK_ID,
-       .bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED,
-       .bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL),
-       .bAssocTerminal = 0,
-};
-
-/* Input Terminal for USB_OUT */
-struct uac2_input_terminal_descriptor usb_out_it_desc = {
-       .bLength = sizeof usb_out_it_desc,
-       .bDescriptorType = USB_DT_CS_INTERFACE,
-
-       .bDescriptorSubtype = UAC_INPUT_TERMINAL,
-       .bTerminalID = USB_OUT_IT_ID,
-       .wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING),
-       .bAssocTerminal = 0,
-       .bCSourceID = USB_OUT_CLK_ID,
-       .iChannelNames = 0,
-       .bmControls = (CONTROL_RDWR << COPY_CTRL),
-};
-
-/* Input Terminal for I/O-In */
-struct uac2_input_terminal_descriptor io_in_it_desc = {
-       .bLength = sizeof io_in_it_desc,
-       .bDescriptorType = USB_DT_CS_INTERFACE,
-
-       .bDescriptorSubtype = UAC_INPUT_TERMINAL,
-       .bTerminalID = IO_IN_IT_ID,
-       .wTerminalType = cpu_to_le16(UAC_INPUT_TERMINAL_UNDEFINED),
-       .bAssocTerminal = 0,
-       .bCSourceID = USB_IN_CLK_ID,
-       .iChannelNames = 0,
-       .bmControls = (CONTROL_RDWR << COPY_CTRL),
-};
-
-/* Ouput Terminal for USB_IN */
-struct uac2_output_terminal_descriptor usb_in_ot_desc = {
-       .bLength = sizeof usb_in_ot_desc,
-       .bDescriptorType = USB_DT_CS_INTERFACE,
-
-       .bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
-       .bTerminalID = USB_IN_OT_ID,
-       .wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING),
-       .bAssocTerminal = 0,
-       .bSourceID = IO_IN_IT_ID,
-       .bCSourceID = USB_IN_CLK_ID,
-       .bmControls = (CONTROL_RDWR << COPY_CTRL),
-};
-
-/* Ouput Terminal for I/O-Out */
-struct uac2_output_terminal_descriptor io_out_ot_desc = {
-       .bLength = sizeof io_out_ot_desc,
-       .bDescriptorType = USB_DT_CS_INTERFACE,
-
-       .bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
-       .bTerminalID = IO_OUT_OT_ID,
-       .wTerminalType = cpu_to_le16(UAC_OUTPUT_TERMINAL_UNDEFINED),
-       .bAssocTerminal = 0,
-       .bSourceID = USB_OUT_IT_ID,
-       .bCSourceID = USB_OUT_CLK_ID,
-       .bmControls = (CONTROL_RDWR << COPY_CTRL),
-};
-
-struct uac2_ac_header_descriptor ac_hdr_desc = {
-       .bLength = sizeof ac_hdr_desc,
-       .bDescriptorType = USB_DT_CS_INTERFACE,
-
-       .bDescriptorSubtype = UAC_MS_HEADER,
-       .bcdADC = cpu_to_le16(0x200),
-       .bCategory = UAC2_FUNCTION_IO_BOX,
-       .wTotalLength = sizeof in_clk_src_desc + sizeof out_clk_src_desc
-                        + sizeof usb_out_it_desc + sizeof io_in_it_desc
-                       + sizeof usb_in_ot_desc + sizeof io_out_ot_desc,
-       .bmControls = 0,
-};
-
-/* Audio Streaming OUT Interface - Alt0 */
-static struct usb_interface_descriptor std_as_out_if0_desc = {
-       .bLength = sizeof std_as_out_if0_desc,
-       .bDescriptorType = USB_DT_INTERFACE,
-
-       .bAlternateSetting = 0,
-       .bNumEndpoints = 0,
-       .bInterfaceClass = USB_CLASS_AUDIO,
-       .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
-       .bInterfaceProtocol = UAC_VERSION_2,
-};
-
-/* Audio Streaming OUT Interface - Alt1 */
-static struct usb_interface_descriptor std_as_out_if1_desc = {
-       .bLength = sizeof std_as_out_if1_desc,
-       .bDescriptorType = USB_DT_INTERFACE,
-
-       .bAlternateSetting = 1,
-       .bNumEndpoints = 1,
-       .bInterfaceClass = USB_CLASS_AUDIO,
-       .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
-       .bInterfaceProtocol = UAC_VERSION_2,
-};
-
-/* Audio Stream OUT Intface Desc */
-struct uac2_as_header_descriptor as_out_hdr_desc = {
-       .bLength = sizeof as_out_hdr_desc,
-       .bDescriptorType = USB_DT_CS_INTERFACE,
-
-       .bDescriptorSubtype = UAC_AS_GENERAL,
-       .bTerminalLink = USB_OUT_IT_ID,
-       .bmControls = 0,
-       .bFormatType = UAC_FORMAT_TYPE_I,
-       .bmFormats = cpu_to_le32(UAC_FORMAT_TYPE_I_PCM),
-       .iChannelNames = 0,
-};
-
-/* Audio USB_OUT Format */
-struct uac2_format_type_i_descriptor as_out_fmt1_desc = {
-       .bLength = sizeof as_out_fmt1_desc,
-       .bDescriptorType = USB_DT_CS_INTERFACE,
-       .bDescriptorSubtype = UAC_FORMAT_TYPE,
-       .bFormatType = UAC_FORMAT_TYPE_I,
-};
-
-/* STD AS ISO OUT Endpoint */
-struct usb_endpoint_descriptor fs_epout_desc = {
-       .bLength = USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType = USB_DT_ENDPOINT,
-
-       .bEndpointAddress = USB_DIR_OUT,
-       .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
-       .bInterval = 1,
-};
-
-struct usb_endpoint_descriptor hs_epout_desc = {
-       .bLength = USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType = USB_DT_ENDPOINT,
-
-       .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
-       .bInterval = 4,
-};
-
-/* CS AS ISO OUT Endpoint */
-static struct uac2_iso_endpoint_descriptor as_iso_out_desc = {
-       .bLength = sizeof as_iso_out_desc,
-       .bDescriptorType = USB_DT_CS_ENDPOINT,
-
-       .bDescriptorSubtype = UAC_EP_GENERAL,
-       .bmAttributes = 0,
-       .bmControls = 0,
-       .bLockDelayUnits = 0,
-       .wLockDelay = 0,
-};
-
-/* Audio Streaming IN Interface - Alt0 */
-static struct usb_interface_descriptor std_as_in_if0_desc = {
-       .bLength = sizeof std_as_in_if0_desc,
-       .bDescriptorType = USB_DT_INTERFACE,
-
-       .bAlternateSetting = 0,
-       .bNumEndpoints = 0,
-       .bInterfaceClass = USB_CLASS_AUDIO,
-       .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
-       .bInterfaceProtocol = UAC_VERSION_2,
-};
-
-/* Audio Streaming IN Interface - Alt1 */
-static struct usb_interface_descriptor std_as_in_if1_desc = {
-       .bLength = sizeof std_as_in_if1_desc,
-       .bDescriptorType = USB_DT_INTERFACE,
-
-       .bAlternateSetting = 1,
-       .bNumEndpoints = 1,
-       .bInterfaceClass = USB_CLASS_AUDIO,
-       .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
-       .bInterfaceProtocol = UAC_VERSION_2,
-};
-
-/* Audio Stream IN Intface Desc */
-struct uac2_as_header_descriptor as_in_hdr_desc = {
-       .bLength = sizeof as_in_hdr_desc,
-       .bDescriptorType = USB_DT_CS_INTERFACE,
-
-       .bDescriptorSubtype = UAC_AS_GENERAL,
-       .bTerminalLink = USB_IN_OT_ID,
-       .bmControls = 0,
-       .bFormatType = UAC_FORMAT_TYPE_I,
-       .bmFormats = cpu_to_le32(UAC_FORMAT_TYPE_I_PCM),
-       .iChannelNames = 0,
-};
-
-/* Audio USB_IN Format */
-struct uac2_format_type_i_descriptor as_in_fmt1_desc = {
-       .bLength = sizeof as_in_fmt1_desc,
-       .bDescriptorType = USB_DT_CS_INTERFACE,
-       .bDescriptorSubtype = UAC_FORMAT_TYPE,
-       .bFormatType = UAC_FORMAT_TYPE_I,
-};
-
-/* STD AS ISO IN Endpoint */
-struct usb_endpoint_descriptor fs_epin_desc = {
-       .bLength = USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType = USB_DT_ENDPOINT,
-
-       .bEndpointAddress = USB_DIR_IN,
-       .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
-       .bInterval = 1,
-};
-
-struct usb_endpoint_descriptor hs_epin_desc = {
-       .bLength = USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType = USB_DT_ENDPOINT,
-
-       .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
-       .bInterval = 4,
-};
-
-/* CS AS ISO IN Endpoint */
-static struct uac2_iso_endpoint_descriptor as_iso_in_desc = {
-       .bLength = sizeof as_iso_in_desc,
-       .bDescriptorType = USB_DT_CS_ENDPOINT,
-
-       .bDescriptorSubtype = UAC_EP_GENERAL,
-       .bmAttributes = 0,
-       .bmControls = 0,
-       .bLockDelayUnits = 0,
-       .wLockDelay = 0,
-};
-
-static struct usb_descriptor_header *fs_audio_desc[] = {
-       (struct usb_descriptor_header *)&iad_desc,
-       (struct usb_descriptor_header *)&std_ac_if_desc,
-
-       (struct usb_descriptor_header *)&ac_hdr_desc,
-       (struct usb_descriptor_header *)&in_clk_src_desc,
-       (struct usb_descriptor_header *)&out_clk_src_desc,
-       (struct usb_descriptor_header *)&usb_out_it_desc,
-       (struct usb_descriptor_header *)&io_in_it_desc,
-       (struct usb_descriptor_header *)&usb_in_ot_desc,
-       (struct usb_descriptor_header *)&io_out_ot_desc,
-
-       (struct usb_descriptor_header *)&std_as_out_if0_desc,
-       (struct usb_descriptor_header *)&std_as_out_if1_desc,
-
-       (struct usb_descriptor_header *)&as_out_hdr_desc,
-       (struct usb_descriptor_header *)&as_out_fmt1_desc,
-       (struct usb_descriptor_header *)&fs_epout_desc,
-       (struct usb_descriptor_header *)&as_iso_out_desc,
-
-       (struct usb_descriptor_header *)&std_as_in_if0_desc,
-       (struct usb_descriptor_header *)&std_as_in_if1_desc,
-
-       (struct usb_descriptor_header *)&as_in_hdr_desc,
-       (struct usb_descriptor_header *)&as_in_fmt1_desc,
-       (struct usb_descriptor_header *)&fs_epin_desc,
-       (struct usb_descriptor_header *)&as_iso_in_desc,
-       NULL,
-};
-
-static struct usb_descriptor_header *hs_audio_desc[] = {
-       (struct usb_descriptor_header *)&iad_desc,
-       (struct usb_descriptor_header *)&std_ac_if_desc,
-
-       (struct usb_descriptor_header *)&ac_hdr_desc,
-       (struct usb_descriptor_header *)&in_clk_src_desc,
-       (struct usb_descriptor_header *)&out_clk_src_desc,
-       (struct usb_descriptor_header *)&usb_out_it_desc,
-       (struct usb_descriptor_header *)&io_in_it_desc,
-       (struct usb_descriptor_header *)&usb_in_ot_desc,
-       (struct usb_descriptor_header *)&io_out_ot_desc,
-
-       (struct usb_descriptor_header *)&std_as_out_if0_desc,
-       (struct usb_descriptor_header *)&std_as_out_if1_desc,
-
-       (struct usb_descriptor_header *)&as_out_hdr_desc,
-       (struct usb_descriptor_header *)&as_out_fmt1_desc,
-       (struct usb_descriptor_header *)&hs_epout_desc,
-       (struct usb_descriptor_header *)&as_iso_out_desc,
-
-       (struct usb_descriptor_header *)&std_as_in_if0_desc,
-       (struct usb_descriptor_header *)&std_as_in_if1_desc,
-
-       (struct usb_descriptor_header *)&as_in_hdr_desc,
-       (struct usb_descriptor_header *)&as_in_fmt1_desc,
-       (struct usb_descriptor_header *)&hs_epin_desc,
-       (struct usb_descriptor_header *)&as_iso_in_desc,
-       NULL,
-};
-
-struct cntrl_cur_lay3 {
-       __u32   dCUR;
-};
-
-struct cntrl_range_lay3 {
-       __u16   wNumSubRanges;
-       __u32   dMIN;
-       __u32   dMAX;
-       __u32   dRES;
-} __packed;
-
-static inline void
-free_ep(struct uac2_rtd_params *prm, struct usb_ep *ep)
-{
-       struct snd_uac2_chip *uac2 = prm->uac2;
-       int i;
-
-       prm->ep_enabled = false;
-
-       for (i = 0; i < USB_XFERS; i++) {
-               if (prm->ureq[i].req) {
-                       usb_ep_dequeue(ep, prm->ureq[i].req);
-                       usb_ep_free_request(ep, prm->ureq[i].req);
-                       prm->ureq[i].req = NULL;
-               }
-       }
-
-       if (usb_ep_disable(ep))
-               dev_err(&uac2->pdev.dev,
-                       "%s:%d Error!\n", __func__, __LINE__);
-}
-
-static int __init
-afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
-{
-       struct audio_dev *agdev = func_to_agdev(fn);
-       struct snd_uac2_chip *uac2 = &agdev->uac2;
-       struct usb_composite_dev *cdev = cfg->cdev;
-       struct usb_gadget *gadget = cdev->gadget;
-       struct uac2_rtd_params *prm;
-       int ret;
-
-       ret = usb_interface_id(cfg, fn);
-       if (ret < 0) {
-               dev_err(&uac2->pdev.dev,
-                       "%s:%d Error!\n", __func__, __LINE__);
-               return ret;
-       }
-       std_ac_if_desc.bInterfaceNumber = ret;
-       agdev->ac_intf = ret;
-       agdev->ac_alt = 0;
-
-       ret = usb_interface_id(cfg, fn);
-       if (ret < 0) {
-               dev_err(&uac2->pdev.dev,
-                       "%s:%d Error!\n", __func__, __LINE__);
-               return ret;
-       }
-       std_as_out_if0_desc.bInterfaceNumber = ret;
-       std_as_out_if1_desc.bInterfaceNumber = ret;
-       agdev->as_out_intf = ret;
-       agdev->as_out_alt = 0;
-
-       ret = usb_interface_id(cfg, fn);
-       if (ret < 0) {
-               dev_err(&uac2->pdev.dev,
-                       "%s:%d Error!\n", __func__, __LINE__);
-               return ret;
-       }
-       std_as_in_if0_desc.bInterfaceNumber = ret;
-       std_as_in_if1_desc.bInterfaceNumber = ret;
-       agdev->as_in_intf = ret;
-       agdev->as_in_alt = 0;
-
-       agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc);
-       if (!agdev->out_ep) {
-               dev_err(&uac2->pdev.dev,
-                       "%s:%d Error!\n", __func__, __LINE__);
-               goto err;
-       }
-       agdev->out_ep->driver_data = agdev;
-
-       agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc);
-       if (!agdev->in_ep) {
-               dev_err(&uac2->pdev.dev,
-                       "%s:%d Error!\n", __func__, __LINE__);
-               goto err;
-       }
-       agdev->in_ep->driver_data = agdev;
-
-       uac2->p_prm.uac2 = uac2;
-       uac2->c_prm.uac2 = uac2;
-
-       hs_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress;
-       hs_epout_desc.wMaxPacketSize = fs_epout_desc.wMaxPacketSize;
-       hs_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress;
-       hs_epin_desc.wMaxPacketSize = fs_epin_desc.wMaxPacketSize;
-
-       ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, NULL);
-       if (ret)
-               goto err;
-
-       prm = &agdev->uac2.c_prm;
-       prm->max_psize = hs_epout_desc.wMaxPacketSize;
-       prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL);
-       if (!prm->rbuf) {
-               prm->max_psize = 0;
-               goto err;
-       }
-
-       prm = &agdev->uac2.p_prm;
-       prm->max_psize = hs_epin_desc.wMaxPacketSize;
-       prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL);
-       if (!prm->rbuf) {
-               prm->max_psize = 0;
-               goto err;
-       }
-
-       ret = alsa_uac2_init(agdev);
-       if (ret)
-               goto err;
-       return 0;
-err:
-       kfree(agdev->uac2.p_prm.rbuf);
-       kfree(agdev->uac2.c_prm.rbuf);
-       usb_free_all_descriptors(fn);
-       if (agdev->in_ep)
-               agdev->in_ep->driver_data = NULL;
-       if (agdev->out_ep)
-               agdev->out_ep->driver_data = NULL;
-       return -EINVAL;
-}
-
-static void
-afunc_unbind(struct usb_configuration *cfg, struct usb_function *fn)
-{
-       struct audio_dev *agdev = func_to_agdev(fn);
-       struct uac2_rtd_params *prm;
-
-       alsa_uac2_exit(agdev);
-
-       prm = &agdev->uac2.p_prm;
-       kfree(prm->rbuf);
-
-       prm = &agdev->uac2.c_prm;
-       kfree(prm->rbuf);
-       usb_free_all_descriptors(fn);
-
-       if (agdev->in_ep)
-               agdev->in_ep->driver_data = NULL;
-       if (agdev->out_ep)
-               agdev->out_ep->driver_data = NULL;
-}
-
-static int
-afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
-{
-       struct usb_composite_dev *cdev = fn->config->cdev;
-       struct audio_dev *agdev = func_to_agdev(fn);
-       struct snd_uac2_chip *uac2 = &agdev->uac2;
-       struct usb_gadget *gadget = cdev->gadget;
-       struct usb_request *req;
-       struct usb_ep *ep;
-       struct uac2_rtd_params *prm;
-       int i;
-
-       /* No i/f has more than 2 alt settings */
-       if (alt > 1) {
-               dev_err(&uac2->pdev.dev,
-                       "%s:%d Error!\n", __func__, __LINE__);
-               return -EINVAL;
-       }
-
-       if (intf == agdev->ac_intf) {
-               /* Control I/f has only 1 AltSetting - 0 */
-               if (alt) {
-                       dev_err(&uac2->pdev.dev,
-                               "%s:%d Error!\n", __func__, __LINE__);
-                       return -EINVAL;
-               }
-               return 0;
-       }
-
-       if (intf == agdev->as_out_intf) {
-               ep = agdev->out_ep;
-               prm = &uac2->c_prm;
-               config_ep_by_speed(gadget, fn, ep);
-               agdev->as_out_alt = alt;
-       } else if (intf == agdev->as_in_intf) {
-               ep = agdev->in_ep;
-               prm = &uac2->p_prm;
-               config_ep_by_speed(gadget, fn, ep);
-               agdev->as_in_alt = alt;
-       } else {
-               dev_err(&uac2->pdev.dev,
-                       "%s:%d Error!\n", __func__, __LINE__);
-               return -EINVAL;
-       }
-
-       if (alt == 0) {
-               free_ep(prm, ep);
-               return 0;
-       }
-
-       prm->ep_enabled = true;
-       usb_ep_enable(ep);
-
-       for (i = 0; i < USB_XFERS; i++) {
-               if (prm->ureq[i].req) {
-                       if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC))
-                               dev_err(&uac2->pdev.dev, "%d Error!\n",
-                                       __LINE__);
-                       continue;
-               }
-
-               req = usb_ep_alloc_request(ep, GFP_ATOMIC);
-               if (req == NULL) {
-                       dev_err(&uac2->pdev.dev,
-                               "%s:%d Error!\n", __func__, __LINE__);
-                       return -EINVAL;
-               }
-
-               prm->ureq[i].req = req;
-               prm->ureq[i].pp = prm;
-
-               req->zero = 0;
-               req->context = &prm->ureq[i];
-               req->length = prm->max_psize;
-               req->complete = agdev_iso_complete;
-               req->buf = prm->rbuf + i * req->length;
-
-               if (usb_ep_queue(ep, req, GFP_ATOMIC))
-                       dev_err(&uac2->pdev.dev, "%d Error!\n", __LINE__);
-       }
-
-       return 0;
-}
-
-static int
-afunc_get_alt(struct usb_function *fn, unsigned intf)
-{
-       struct audio_dev *agdev = func_to_agdev(fn);
-       struct snd_uac2_chip *uac2 = &agdev->uac2;
-
-       if (intf == agdev->ac_intf)
-               return agdev->ac_alt;
-       else if (intf == agdev->as_out_intf)
-               return agdev->as_out_alt;
-       else if (intf == agdev->as_in_intf)
-               return agdev->as_in_alt;
-       else
-               dev_err(&uac2->pdev.dev,
-                       "%s:%d Invalid Interface %d!\n",
-                       __func__, __LINE__, intf);
-
-       return -EINVAL;
-}
-
-static void
-afunc_disable(struct usb_function *fn)
-{
-       struct audio_dev *agdev = func_to_agdev(fn);
-       struct snd_uac2_chip *uac2 = &agdev->uac2;
-
-       free_ep(&uac2->p_prm, agdev->in_ep);
-       agdev->as_in_alt = 0;
-
-       free_ep(&uac2->c_prm, agdev->out_ep);
-       agdev->as_out_alt = 0;
-}
-
-static int
-in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
-{
-       struct usb_request *req = fn->config->cdev->req;
-       struct audio_dev *agdev = func_to_agdev(fn);
-       struct snd_uac2_chip *uac2 = &agdev->uac2;
-       u16 w_length = le16_to_cpu(cr->wLength);
-       u16 w_index = le16_to_cpu(cr->wIndex);
-       u16 w_value = le16_to_cpu(cr->wValue);
-       u8 entity_id = (w_index >> 8) & 0xff;
-       u8 control_selector = w_value >> 8;
-       int value = -EOPNOTSUPP;
-
-       if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
-               struct cntrl_cur_lay3 c;
-
-               if (entity_id == USB_IN_CLK_ID)
-                       c.dCUR = p_srate;
-               else if (entity_id == USB_OUT_CLK_ID)
-                       c.dCUR = c_srate;
-
-               value = min_t(unsigned, w_length, sizeof c);
-               memcpy(req->buf, &c, value);
-       } else if (control_selector == UAC2_CS_CONTROL_CLOCK_VALID) {
-               *(u8 *)req->buf = 1;
-               value = min_t(unsigned, w_length, 1);
-       } else {
-               dev_err(&uac2->pdev.dev,
-                       "%s:%d control_selector=%d TODO!\n",
-                       __func__, __LINE__, control_selector);
-       }
-
-       return value;
-}
-
-static int
-in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
-{
-       struct usb_request *req = fn->config->cdev->req;
-       struct audio_dev *agdev = func_to_agdev(fn);
-       struct snd_uac2_chip *uac2 = &agdev->uac2;
-       u16 w_length = le16_to_cpu(cr->wLength);
-       u16 w_index = le16_to_cpu(cr->wIndex);
-       u16 w_value = le16_to_cpu(cr->wValue);
-       u8 entity_id = (w_index >> 8) & 0xff;
-       u8 control_selector = w_value >> 8;
-       struct cntrl_range_lay3 r;
-       int value = -EOPNOTSUPP;
-
-       if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
-               if (entity_id == USB_IN_CLK_ID)
-                       r.dMIN = p_srate;
-               else if (entity_id == USB_OUT_CLK_ID)
-                       r.dMIN = c_srate;
-               else
-                       return -EOPNOTSUPP;
-
-               r.dMAX = r.dMIN;
-               r.dRES = 0;
-               r.wNumSubRanges = 1;
-
-               value = min_t(unsigned, w_length, sizeof r);
-               memcpy(req->buf, &r, value);
-       } else {
-               dev_err(&uac2->pdev.dev,
-                       "%s:%d control_selector=%d TODO!\n",
-                       __func__, __LINE__, control_selector);
-       }
-
-       return value;
-}
-
-static int
-ac_rq_in(struct usb_function *fn, const struct usb_ctrlrequest *cr)
-{
-       if (cr->bRequest == UAC2_CS_CUR)
-               return in_rq_cur(fn, cr);
-       else if (cr->bRequest == UAC2_CS_RANGE)
-               return in_rq_range(fn, cr);
-       else
-               return -EOPNOTSUPP;
-}
-
-static int
-out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
-{
-       u16 w_length = le16_to_cpu(cr->wLength);
-       u16 w_value = le16_to_cpu(cr->wValue);
-       u8 control_selector = w_value >> 8;
-
-       if (control_selector == UAC2_CS_CONTROL_SAM_FREQ)
-               return w_length;
-
-       return -EOPNOTSUPP;
-}
-
-static int
-setup_rq_inf(struct usb_function *fn, const struct usb_ctrlrequest *cr)
-{
-       struct audio_dev *agdev = func_to_agdev(fn);
-       struct snd_uac2_chip *uac2 = &agdev->uac2;
-       u16 w_index = le16_to_cpu(cr->wIndex);
-       u8 intf = w_index & 0xff;
-
-       if (intf != agdev->ac_intf) {
-               dev_err(&uac2->pdev.dev,
-                       "%s:%d Error!\n", __func__, __LINE__);
-               return -EOPNOTSUPP;
-       }
-
-       if (cr->bRequestType & USB_DIR_IN)
-               return ac_rq_in(fn, cr);
-       else if (cr->bRequest == UAC2_CS_CUR)
-               return out_rq_cur(fn, cr);
-
-       return -EOPNOTSUPP;
-}
-
-static int
-afunc_setup(struct usb_function *fn, const struct usb_ctrlrequest *cr)
-{
-       struct usb_composite_dev *cdev = fn->config->cdev;
-       struct audio_dev *agdev = func_to_agdev(fn);
-       struct snd_uac2_chip *uac2 = &agdev->uac2;
-       struct usb_request *req = cdev->req;
-       u16 w_length = le16_to_cpu(cr->wLength);
-       int value = -EOPNOTSUPP;
-
-       /* Only Class specific requests are supposed to reach here */
-       if ((cr->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS)
-               return -EOPNOTSUPP;
-
-       if ((cr->bRequestType & USB_RECIP_MASK) == USB_RECIP_INTERFACE)
-               value = setup_rq_inf(fn, cr);
-       else
-               dev_err(&uac2->pdev.dev, "%s:%d Error!\n", __func__, __LINE__);
-
-       if (value >= 0) {
-               req->length = value;
-               req->zero = value < w_length;
-               value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
-               if (value < 0) {
-                       dev_err(&uac2->pdev.dev,
-                               "%s:%d Error!\n", __func__, __LINE__);
-                       req->status = 0;
-               }
-       }
-
-       return value;
-}
-
-static int audio_bind_config(struct usb_configuration *cfg)
-{
-       int res;
-
-       agdev_g = kzalloc(sizeof *agdev_g, GFP_KERNEL);
-       if (agdev_g == NULL)
-               return -ENOMEM;
-
-       res = usb_string_ids_tab(cfg->cdev, strings_fn);
-       if (res)
-               return res;
-       iad_desc.iFunction = strings_fn[STR_ASSOC].id;
-       std_ac_if_desc.iInterface = strings_fn[STR_IF_CTRL].id;
-       in_clk_src_desc.iClockSource = strings_fn[STR_CLKSRC_IN].id;
-       out_clk_src_desc.iClockSource = strings_fn[STR_CLKSRC_OUT].id;
-       usb_out_it_desc.iTerminal = strings_fn[STR_USB_IT].id;
-       io_in_it_desc.iTerminal = strings_fn[STR_IO_IT].id;
-       usb_in_ot_desc.iTerminal = strings_fn[STR_USB_OT].id;
-       io_out_ot_desc.iTerminal = strings_fn[STR_IO_OT].id;
-       std_as_out_if0_desc.iInterface = strings_fn[STR_AS_OUT_ALT0].id;
-       std_as_out_if1_desc.iInterface = strings_fn[STR_AS_OUT_ALT1].id;
-       std_as_in_if0_desc.iInterface = strings_fn[STR_AS_IN_ALT0].id;
-       std_as_in_if1_desc.iInterface = strings_fn[STR_AS_IN_ALT1].id;
-
-       agdev_g->func.name = "uac2_func";
-       agdev_g->func.strings = fn_strings;
-       agdev_g->func.bind = afunc_bind;
-       agdev_g->func.unbind = afunc_unbind;
-       agdev_g->func.set_alt = afunc_set_alt;
-       agdev_g->func.get_alt = afunc_get_alt;
-       agdev_g->func.disable = afunc_disable;
-       agdev_g->func.setup = afunc_setup;
-
-       /* Initialize the configurable parameters */
-       usb_out_it_desc.bNrChannels = num_channels(c_chmask);
-       usb_out_it_desc.bmChannelConfig = cpu_to_le32(c_chmask);
-       io_in_it_desc.bNrChannels = num_channels(p_chmask);
-       io_in_it_desc.bmChannelConfig = cpu_to_le32(p_chmask);
-       as_out_hdr_desc.bNrChannels = num_channels(c_chmask);
-       as_out_hdr_desc.bmChannelConfig = cpu_to_le32(c_chmask);
-       as_in_hdr_desc.bNrChannels = num_channels(p_chmask);
-       as_in_hdr_desc.bmChannelConfig = cpu_to_le32(p_chmask);
-       as_out_fmt1_desc.bSubslotSize = c_ssize;
-       as_out_fmt1_desc.bBitResolution = c_ssize * 8;
-       as_in_fmt1_desc.bSubslotSize = p_ssize;
-       as_in_fmt1_desc.bBitResolution = p_ssize * 8;
-
-       snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", p_srate);
-       snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", c_srate);
-
-       res = usb_add_function(cfg, &agdev_g->func);
-       if (res < 0)
-               kfree(agdev_g);
-
-       return res;
-}
-
-static void
-uac2_unbind_config(struct usb_configuration *cfg)
-{
-       kfree(agdev_g);
-       agdev_g = NULL;
-}
diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c
deleted file mode 100644 (file)
index e2a1f50..0000000
+++ /dev/null
@@ -1,836 +0,0 @@
-/*
- *     uvc_gadget.c  --  USB Video Class Gadget driver
- *
- *     Copyright (C) 2009-2010
- *         Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- *
- *     This program is free software; you can redistribute it and/or modify
- *     it under the terms of the GNU General Public License as published by
- *     the Free Software Foundation; either version 2 of the License, or
- *     (at your option) any later version.
- */
-
-#include <linux/kernel.h>
-#include <linux/device.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/list.h>
-#include <linux/mutex.h>
-#include <linux/string.h>
-#include <linux/usb/ch9.h>
-#include <linux/usb/gadget.h>
-#include <linux/usb/video.h>
-#include <linux/vmalloc.h>
-#include <linux/wait.h>
-
-#include <media/v4l2-dev.h>
-#include <media/v4l2-event.h>
-
-#include "uvc.h"
-
-unsigned int uvc_gadget_trace_param;
-
-/*-------------------------------------------------------------------------*/
-
-/* module parameters specific to the Video streaming endpoint */
-static unsigned int streaming_interval = 1;
-module_param(streaming_interval, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(streaming_interval, "1 - 16");
-
-static unsigned int streaming_maxpacket = 1024;
-module_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(streaming_maxpacket, "1 - 1023 (FS), 1 - 3072 (hs/ss)");
-
-static unsigned int streaming_maxburst;
-module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)");
-
-/* --------------------------------------------------------------------------
- * Function descriptors
- */
-
-/* string IDs are assigned dynamically */
-
-#define UVC_STRING_CONTROL_IDX                 0
-#define UVC_STRING_STREAMING_IDX               1
-
-static struct usb_string uvc_en_us_strings[] = {
-       [UVC_STRING_CONTROL_IDX].s = "UVC Camera",
-       [UVC_STRING_STREAMING_IDX].s = "Video Streaming",
-       {  }
-};
-
-static struct usb_gadget_strings uvc_stringtab = {
-       .language = 0x0409,     /* en-us */
-       .strings = uvc_en_us_strings,
-};
-
-static struct usb_gadget_strings *uvc_function_strings[] = {
-       &uvc_stringtab,
-       NULL,
-};
-
-#define UVC_INTF_VIDEO_CONTROL                 0
-#define UVC_INTF_VIDEO_STREAMING               1
-
-#define UVC_STATUS_MAX_PACKET_SIZE             16      /* 16 bytes status */
-
-static struct usb_interface_assoc_descriptor uvc_iad __initdata = {
-       .bLength                = sizeof(uvc_iad),
-       .bDescriptorType        = USB_DT_INTERFACE_ASSOCIATION,
-       .bFirstInterface        = 0,
-       .bInterfaceCount        = 2,
-       .bFunctionClass         = USB_CLASS_VIDEO,
-       .bFunctionSubClass      = UVC_SC_VIDEO_INTERFACE_COLLECTION,
-       .bFunctionProtocol      = 0x00,
-       .iFunction              = 0,
-};
-
-static struct usb_interface_descriptor uvc_control_intf __initdata = {
-       .bLength                = USB_DT_INTERFACE_SIZE,
-       .bDescriptorType        = USB_DT_INTERFACE,
-       .bInterfaceNumber       = UVC_INTF_VIDEO_CONTROL,
-       .bAlternateSetting      = 0,
-       .bNumEndpoints          = 1,
-       .bInterfaceClass        = USB_CLASS_VIDEO,
-       .bInterfaceSubClass     = UVC_SC_VIDEOCONTROL,
-       .bInterfaceProtocol     = 0x00,
-       .iInterface             = 0,
-};
-
-static struct usb_endpoint_descriptor uvc_control_ep __initdata = {
-       .bLength                = USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType        = USB_DT_ENDPOINT,
-       .bEndpointAddress       = USB_DIR_IN,
-       .bmAttributes           = USB_ENDPOINT_XFER_INT,
-       .wMaxPacketSize         = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE),
-       .bInterval              = 8,
-};
-
-static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp __initdata = {
-       .bLength                = sizeof(uvc_ss_control_comp),
-       .bDescriptorType        = USB_DT_SS_ENDPOINT_COMP,
-       /* The following 3 values can be tweaked if necessary. */
-       .bMaxBurst              = 0,
-       .bmAttributes           = 0,
-       .wBytesPerInterval      = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE),
-};
-
-static struct uvc_control_endpoint_descriptor uvc_control_cs_ep __initdata = {
-       .bLength                = UVC_DT_CONTROL_ENDPOINT_SIZE,
-       .bDescriptorType        = USB_DT_CS_ENDPOINT,
-       .bDescriptorSubType     = UVC_EP_INTERRUPT,
-       .wMaxTransferSize       = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE),
-};
-
-static struct usb_interface_descriptor uvc_streaming_intf_alt0 __initdata = {
-       .bLength                = USB_DT_INTERFACE_SIZE,
-       .bDescriptorType        = USB_DT_INTERFACE,
-       .bInterfaceNumber       = UVC_INTF_VIDEO_STREAMING,
-       .bAlternateSetting      = 0,
-       .bNumEndpoints          = 0,
-       .bInterfaceClass        = USB_CLASS_VIDEO,
-       .bInterfaceSubClass     = UVC_SC_VIDEOSTREAMING,
-       .bInterfaceProtocol     = 0x00,
-       .iInterface             = 0,
-};
-
-static struct usb_interface_descriptor uvc_streaming_intf_alt1 __initdata = {
-       .bLength                = USB_DT_INTERFACE_SIZE,
-       .bDescriptorType        = USB_DT_INTERFACE,
-       .bInterfaceNumber       = UVC_INTF_VIDEO_STREAMING,
-       .bAlternateSetting      = 1,
-       .bNumEndpoints          = 1,
-       .bInterfaceClass        = USB_CLASS_VIDEO,
-       .bInterfaceSubClass     = UVC_SC_VIDEOSTREAMING,
-       .bInterfaceProtocol     = 0x00,
-       .iInterface             = 0,
-};
-
-static struct usb_endpoint_descriptor uvc_fs_streaming_ep __initdata = {
-       .bLength                = USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType        = USB_DT_ENDPOINT,
-       .bEndpointAddress       = USB_DIR_IN,
-       .bmAttributes           = USB_ENDPOINT_SYNC_ASYNC
-                               | USB_ENDPOINT_XFER_ISOC,
-       /* The wMaxPacketSize and bInterval values will be initialized from
-        * module parameters.
-        */
-};
-
-static struct usb_endpoint_descriptor uvc_hs_streaming_ep __initdata = {
-       .bLength                = USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType        = USB_DT_ENDPOINT,
-       .bEndpointAddress       = USB_DIR_IN,
-       .bmAttributes           = USB_ENDPOINT_SYNC_ASYNC
-                               | USB_ENDPOINT_XFER_ISOC,
-       /* The wMaxPacketSize and bInterval values will be initialized from
-        * module parameters.
-        */
-};
-
-static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = {
-       .bLength                = USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType        = USB_DT_ENDPOINT,
-
-       .bEndpointAddress       = USB_DIR_IN,
-       .bmAttributes           = USB_ENDPOINT_SYNC_ASYNC
-                               | USB_ENDPOINT_XFER_ISOC,
-       /* The wMaxPacketSize and bInterval values will be initialized from
-        * module parameters.
-        */
-};
-
-static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp __initdata = {
-       .bLength                = sizeof(uvc_ss_streaming_comp),
-       .bDescriptorType        = USB_DT_SS_ENDPOINT_COMP,
-       /* The bMaxBurst, bmAttributes and wBytesPerInterval values will be
-        * initialized from module parameters.
-        */
-};
-
-static const struct usb_descriptor_header * const uvc_fs_streaming[] = {
-       (struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
-       (struct usb_descriptor_header *) &uvc_fs_streaming_ep,
-       NULL,
-};
-
-static const struct usb_descriptor_header * const uvc_hs_streaming[] = {
-       (struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
-       (struct usb_descriptor_header *) &uvc_hs_streaming_ep,
-       NULL,
-};
-
-static const struct usb_descriptor_header * const uvc_ss_streaming[] = {
-       (struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
-       (struct usb_descriptor_header *) &uvc_ss_streaming_ep,
-       (struct usb_descriptor_header *) &uvc_ss_streaming_comp,
-       NULL,
-};
-
-/* --------------------------------------------------------------------------
- * Control requests
- */
-
-static void
-uvc_function_ep0_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       struct uvc_device *uvc = req->context;
-       struct v4l2_event v4l2_event;
-       struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
-
-       if (uvc->event_setup_out) {
-               uvc->event_setup_out = 0;
-
-               memset(&v4l2_event, 0, sizeof(v4l2_event));
-               v4l2_event.type = UVC_EVENT_DATA;
-               uvc_event->data.length = req->actual;
-               memcpy(&uvc_event->data.data, req->buf, req->actual);
-               v4l2_event_queue(uvc->vdev, &v4l2_event);
-       }
-}
-
-static int
-uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
-{
-       struct uvc_device *uvc = to_uvc(f);
-       struct v4l2_event v4l2_event;
-       struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
-
-       /* printk(KERN_INFO "setup request %02x %02x value %04x index %04x %04x\n",
-        *      ctrl->bRequestType, ctrl->bRequest, le16_to_cpu(ctrl->wValue),
-        *      le16_to_cpu(ctrl->wIndex), le16_to_cpu(ctrl->wLength));
-        */
-
-       if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS) {
-               INFO(f->config->cdev, "invalid request type\n");
-               return -EINVAL;
-       }
-
-       /* Stall too big requests. */
-       if (le16_to_cpu(ctrl->wLength) > UVC_MAX_REQUEST_SIZE)
-               return -EINVAL;
-
-       memset(&v4l2_event, 0, sizeof(v4l2_event));
-       v4l2_event.type = UVC_EVENT_SETUP;
-       memcpy(&uvc_event->req, ctrl, sizeof(uvc_event->req));
-       v4l2_event_queue(uvc->vdev, &v4l2_event);
-
-       return 0;
-}
-
-void uvc_function_setup_continue(struct uvc_device *uvc)
-{
-       struct usb_composite_dev *cdev = uvc->func.config->cdev;
-
-       usb_composite_setup_continue(cdev);
-}
-
-static int
-uvc_function_get_alt(struct usb_function *f, unsigned interface)
-{
-       struct uvc_device *uvc = to_uvc(f);
-
-       INFO(f->config->cdev, "uvc_function_get_alt(%u)\n", interface);
-
-       if (interface == uvc->control_intf)
-               return 0;
-       else if (interface != uvc->streaming_intf)
-               return -EINVAL;
-       else
-               return uvc->state == UVC_STATE_STREAMING ? 1 : 0;
-}
-
-static int
-uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
-{
-       struct uvc_device *uvc = to_uvc(f);
-       struct v4l2_event v4l2_event;
-       struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
-       int ret;
-
-       INFO(f->config->cdev, "uvc_function_set_alt(%u, %u)\n", interface, alt);
-
-       if (interface == uvc->control_intf) {
-               if (alt)
-                       return -EINVAL;
-
-               if (uvc->state == UVC_STATE_DISCONNECTED) {
-                       memset(&v4l2_event, 0, sizeof(v4l2_event));
-                       v4l2_event.type = UVC_EVENT_CONNECT;
-                       uvc_event->speed = f->config->cdev->gadget->speed;
-                       v4l2_event_queue(uvc->vdev, &v4l2_event);
-
-                       uvc->state = UVC_STATE_CONNECTED;
-               }
-
-               return 0;
-       }
-
-       if (interface != uvc->streaming_intf)
-               return -EINVAL;
-
-       /* TODO
-       if (usb_endpoint_xfer_bulk(&uvc->desc.vs_ep))
-               return alt ? -EINVAL : 0;
-       */
-
-       switch (alt) {
-       case 0:
-               if (uvc->state != UVC_STATE_STREAMING)
-                       return 0;
-
-               if (uvc->video.ep)
-                       usb_ep_disable(uvc->video.ep);
-
-               memset(&v4l2_event, 0, sizeof(v4l2_event));
-               v4l2_event.type = UVC_EVENT_STREAMOFF;
-               v4l2_event_queue(uvc->vdev, &v4l2_event);
-
-               uvc->state = UVC_STATE_CONNECTED;
-               return 0;
-
-       case 1:
-               if (uvc->state != UVC_STATE_CONNECTED)
-                       return 0;
-
-               if (uvc->video.ep) {
-                       ret = config_ep_by_speed(f->config->cdev->gadget,
-                                       &(uvc->func), uvc->video.ep);
-                       if (ret)
-                               return ret;
-                       usb_ep_enable(uvc->video.ep);
-               }
-
-               memset(&v4l2_event, 0, sizeof(v4l2_event));
-               v4l2_event.type = UVC_EVENT_STREAMON;
-               v4l2_event_queue(uvc->vdev, &v4l2_event);
-               return USB_GADGET_DELAYED_STATUS;
-
-       default:
-               return -EINVAL;
-       }
-}
-
-static void
-uvc_function_disable(struct usb_function *f)
-{
-       struct uvc_device *uvc = to_uvc(f);
-       struct v4l2_event v4l2_event;
-
-       INFO(f->config->cdev, "uvc_function_disable\n");
-
-       memset(&v4l2_event, 0, sizeof(v4l2_event));
-       v4l2_event.type = UVC_EVENT_DISCONNECT;
-       v4l2_event_queue(uvc->vdev, &v4l2_event);
-
-       uvc->state = UVC_STATE_DISCONNECTED;
-}
-
-/* --------------------------------------------------------------------------
- * Connection / disconnection
- */
-
-void
-uvc_function_connect(struct uvc_device *uvc)
-{
-       struct usb_composite_dev *cdev = uvc->func.config->cdev;
-       int ret;
-
-       if ((ret = usb_function_activate(&uvc->func)) < 0)
-               INFO(cdev, "UVC connect failed with %d\n", ret);
-}
-
-void
-uvc_function_disconnect(struct uvc_device *uvc)
-{
-       struct usb_composite_dev *cdev = uvc->func.config->cdev;
-       int ret;
-
-       if ((ret = usb_function_deactivate(&uvc->func)) < 0)
-               INFO(cdev, "UVC disconnect failed with %d\n", ret);
-}
-
-/* --------------------------------------------------------------------------
- * USB probe and disconnect
- */
-
-static int
-uvc_register_video(struct uvc_device *uvc)
-{
-       struct usb_composite_dev *cdev = uvc->func.config->cdev;
-       struct video_device *video;
-
-       /* TODO reference counting. */
-       video = video_device_alloc();
-       if (video == NULL)
-               return -ENOMEM;
-
-       video->v4l2_dev = &uvc->v4l2_dev;
-       video->fops = &uvc_v4l2_fops;
-       video->release = video_device_release;
-       strlcpy(video->name, cdev->gadget->name, sizeof(video->name));
-
-       uvc->vdev = video;
-       video_set_drvdata(video, uvc);
-
-       return video_register_device(video, VFL_TYPE_GRABBER, -1);
-}
-
-#define UVC_COPY_DESCRIPTOR(mem, dst, desc) \
-       do { \
-               memcpy(mem, desc, (desc)->bLength); \
-               *(dst)++ = mem; \
-               mem += (desc)->bLength; \
-       } while (0);
-
-#define UVC_COPY_DESCRIPTORS(mem, dst, src) \
-       do { \
-               const struct usb_descriptor_header * const *__src; \
-               for (__src = src; *__src; ++__src) { \
-                       memcpy(mem, *__src, (*__src)->bLength); \
-                       *dst++ = mem; \
-                       mem += (*__src)->bLength; \
-               } \
-       } while (0)
-
-static struct usb_descriptor_header ** __init
-uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
-{
-       struct uvc_input_header_descriptor *uvc_streaming_header;
-       struct uvc_header_descriptor *uvc_control_header;
-       const struct uvc_descriptor_header * const *uvc_control_desc;
-       const struct uvc_descriptor_header * const *uvc_streaming_cls;
-       const struct usb_descriptor_header * const *uvc_streaming_std;
-       const struct usb_descriptor_header * const *src;
-       struct usb_descriptor_header **dst;
-       struct usb_descriptor_header **hdr;
-       unsigned int control_size;
-       unsigned int streaming_size;
-       unsigned int n_desc;
-       unsigned int bytes;
-       void *mem;
-
-       switch (speed) {
-       case USB_SPEED_SUPER:
-               uvc_control_desc = uvc->desc.ss_control;
-               uvc_streaming_cls = uvc->desc.ss_streaming;
-               uvc_streaming_std = uvc_ss_streaming;
-               break;
-
-       case USB_SPEED_HIGH:
-               uvc_control_desc = uvc->desc.fs_control;
-               uvc_streaming_cls = uvc->desc.hs_streaming;
-               uvc_streaming_std = uvc_hs_streaming;
-               break;
-
-       case USB_SPEED_FULL:
-       default:
-               uvc_control_desc = uvc->desc.fs_control;
-               uvc_streaming_cls = uvc->desc.fs_streaming;
-               uvc_streaming_std = uvc_fs_streaming;
-               break;
-       }
-
-       /* Descriptors layout
-        *
-        * uvc_iad
-        * uvc_control_intf
-        * Class-specific UVC control descriptors
-        * uvc_control_ep
-        * uvc_control_cs_ep
-        * uvc_ss_control_comp (for SS only)
-        * uvc_streaming_intf_alt0
-        * Class-specific UVC streaming descriptors
-        * uvc_{fs|hs}_streaming
-        */
-
-       /* Count descriptors and compute their size. */
-       control_size = 0;
-       streaming_size = 0;
-       bytes = uvc_iad.bLength + uvc_control_intf.bLength
-             + uvc_control_ep.bLength + uvc_control_cs_ep.bLength
-             + uvc_streaming_intf_alt0.bLength;
-
-       if (speed == USB_SPEED_SUPER) {
-               bytes += uvc_ss_control_comp.bLength;
-               n_desc = 6;
-       } else {
-               n_desc = 5;
-       }
-
-       for (src = (const struct usb_descriptor_header **)uvc_control_desc;
-            *src; ++src) {
-               control_size += (*src)->bLength;
-               bytes += (*src)->bLength;
-               n_desc++;
-       }
-       for (src = (const struct usb_descriptor_header **)uvc_streaming_cls;
-            *src; ++src) {
-               streaming_size += (*src)->bLength;
-               bytes += (*src)->bLength;
-               n_desc++;
-       }
-       for (src = uvc_streaming_std; *src; ++src) {
-               bytes += (*src)->bLength;
-               n_desc++;
-       }
-
-       mem = kmalloc((n_desc + 1) * sizeof(*src) + bytes, GFP_KERNEL);
-       if (mem == NULL)
-               return NULL;
-
-       hdr = mem;
-       dst = mem;
-       mem += (n_desc + 1) * sizeof(*src);
-
-       /* Copy the descriptors. */
-       UVC_COPY_DESCRIPTOR(mem, dst, &uvc_iad);
-       UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_intf);
-
-       uvc_control_header = mem;
-       UVC_COPY_DESCRIPTORS(mem, dst,
-               (const struct usb_descriptor_header **)uvc_control_desc);
-       uvc_control_header->wTotalLength = cpu_to_le16(control_size);
-       uvc_control_header->bInCollection = 1;
-       uvc_control_header->baInterfaceNr[0] = uvc->streaming_intf;
-
-       UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_ep);
-       if (speed == USB_SPEED_SUPER)
-               UVC_COPY_DESCRIPTOR(mem, dst, &uvc_ss_control_comp);
-
-       UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_cs_ep);
-       UVC_COPY_DESCRIPTOR(mem, dst, &uvc_streaming_intf_alt0);
-
-       uvc_streaming_header = mem;
-       UVC_COPY_DESCRIPTORS(mem, dst,
-               (const struct usb_descriptor_header**)uvc_streaming_cls);
-       uvc_streaming_header->wTotalLength = cpu_to_le16(streaming_size);
-       uvc_streaming_header->bEndpointAddress = uvc->video.ep->address;
-
-       UVC_COPY_DESCRIPTORS(mem, dst, uvc_streaming_std);
-
-       *dst = NULL;
-       return hdr;
-}
-
-static void
-uvc_function_unbind(struct usb_configuration *c, struct usb_function *f)
-{
-       struct usb_composite_dev *cdev = c->cdev;
-       struct uvc_device *uvc = to_uvc(f);
-
-       INFO(cdev, "uvc_function_unbind\n");
-
-       video_unregister_device(uvc->vdev);
-       v4l2_device_unregister(&uvc->v4l2_dev);
-       uvc->control_ep->driver_data = NULL;
-       uvc->video.ep->driver_data = NULL;
-
-       uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id = 0;
-       usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
-       kfree(uvc->control_buf);
-
-       usb_free_all_descriptors(f);
-
-       kfree(uvc);
-}
-
-static int __init
-uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
-{
-       struct usb_composite_dev *cdev = c->cdev;
-       struct uvc_device *uvc = to_uvc(f);
-       unsigned int max_packet_mult;
-       unsigned int max_packet_size;
-       struct usb_ep *ep;
-       int ret = -EINVAL;
-
-       INFO(cdev, "uvc_function_bind\n");
-
-       /* Sanity check the streaming endpoint module parameters.
-        */
-       streaming_interval = clamp(streaming_interval, 1U, 16U);
-       streaming_maxpacket = clamp(streaming_maxpacket, 1U, 3072U);
-       streaming_maxburst = min(streaming_maxburst, 15U);
-
-       /* Fill in the FS/HS/SS Video Streaming specific descriptors from the
-        * module parameters.
-        *
-        * NOTE: We assume that the user knows what they are doing and won't
-        * give parameters that their UDC doesn't support.
-        */
-       if (streaming_maxpacket <= 1024) {
-               max_packet_mult = 1;
-               max_packet_size = streaming_maxpacket;
-       } else if (streaming_maxpacket <= 2048) {
-               max_packet_mult = 2;
-               max_packet_size = streaming_maxpacket / 2;
-       } else {
-               max_packet_mult = 3;
-               max_packet_size = streaming_maxpacket / 3;
-       }
-
-       uvc_fs_streaming_ep.wMaxPacketSize = min(streaming_maxpacket, 1023U);
-       uvc_fs_streaming_ep.bInterval = streaming_interval;
-
-       uvc_hs_streaming_ep.wMaxPacketSize = max_packet_size;
-       uvc_hs_streaming_ep.wMaxPacketSize |= ((max_packet_mult - 1) << 11);
-       uvc_hs_streaming_ep.bInterval = streaming_interval;
-
-       uvc_ss_streaming_ep.wMaxPacketSize = max_packet_size;
-       uvc_ss_streaming_ep.bInterval = streaming_interval;
-       uvc_ss_streaming_comp.bmAttributes = max_packet_mult - 1;
-       uvc_ss_streaming_comp.bMaxBurst = streaming_maxburst;
-       uvc_ss_streaming_comp.wBytesPerInterval =
-               max_packet_size * max_packet_mult * streaming_maxburst;
-
-       /* Allocate endpoints. */
-       ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep);
-       if (!ep) {
-               INFO(cdev, "Unable to allocate control EP\n");
-               goto error;
-       }
-       uvc->control_ep = ep;
-       ep->driver_data = uvc;
-
-       if (gadget_is_superspeed(c->cdev->gadget))
-               ep = usb_ep_autoconfig_ss(cdev->gadget, &uvc_ss_streaming_ep,
-                                         &uvc_ss_streaming_comp);
-       else if (gadget_is_dualspeed(cdev->gadget))
-               ep = usb_ep_autoconfig(cdev->gadget, &uvc_hs_streaming_ep);
-       else
-               ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep);
-
-       if (!ep) {
-               INFO(cdev, "Unable to allocate streaming EP\n");
-               goto error;
-       }
-       uvc->video.ep = ep;
-       ep->driver_data = uvc;
-
-       uvc_fs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
-       uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
-       uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address;
-
-       /* Allocate interface IDs. */
-       if ((ret = usb_interface_id(c, f)) < 0)
-               goto error;
-       uvc_iad.bFirstInterface = ret;
-       uvc_control_intf.bInterfaceNumber = ret;
-       uvc->control_intf = ret;
-
-       if ((ret = usb_interface_id(c, f)) < 0)
-               goto error;
-       uvc_streaming_intf_alt0.bInterfaceNumber = ret;
-       uvc_streaming_intf_alt1.bInterfaceNumber = ret;
-       uvc->streaming_intf = ret;
-
-       /* Copy descriptors */
-       f->fs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL);
-       if (gadget_is_dualspeed(cdev->gadget))
-               f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH);
-       if (gadget_is_superspeed(c->cdev->gadget))
-               f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER);
-
-       /* Preallocate control endpoint request. */
-       uvc->control_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL);
-       uvc->control_buf = kmalloc(UVC_MAX_REQUEST_SIZE, GFP_KERNEL);
-       if (uvc->control_req == NULL || uvc->control_buf == NULL) {
-               ret = -ENOMEM;
-               goto error;
-       }
-
-       uvc->control_req->buf = uvc->control_buf;
-       uvc->control_req->complete = uvc_function_ep0_complete;
-       uvc->control_req->context = uvc;
-
-       /* Avoid letting this gadget enumerate until the userspace server is
-        * active.
-        */
-       if ((ret = usb_function_deactivate(f)) < 0)
-               goto error;
-
-       if (v4l2_device_register(&cdev->gadget->dev, &uvc->v4l2_dev)) {
-               printk(KERN_INFO "v4l2_device_register failed\n");
-               goto error;
-       }
-
-       /* Initialise video. */
-       ret = uvc_video_init(&uvc->video);
-       if (ret < 0)
-               goto error;
-
-       /* Register a V4L2 device. */
-       ret = uvc_register_video(uvc);
-       if (ret < 0) {
-               printk(KERN_INFO "Unable to register video device\n");
-               goto error;
-       }
-
-       return 0;
-
-error:
-       v4l2_device_unregister(&uvc->v4l2_dev);
-       if (uvc->vdev)
-               video_device_release(uvc->vdev);
-
-       if (uvc->control_ep)
-               uvc->control_ep->driver_data = NULL;
-       if (uvc->video.ep)
-               uvc->video.ep->driver_data = NULL;
-
-       if (uvc->control_req) {
-               usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
-               kfree(uvc->control_buf);
-       }
-
-       usb_free_all_descriptors(f);
-       return ret;
-}
-
-/* --------------------------------------------------------------------------
- * USB gadget function
- */
-
-/**
- * uvc_bind_config - add a UVC function to a configuration
- * @c: the configuration to support the UVC instance
- * Context: single threaded during gadget setup
- *
- * Returns zero on success, else negative errno.
- *
- * Caller must have called @uvc_setup(). Caller is also responsible for
- * calling @uvc_cleanup() before module unload.
- */
-int __init
-uvc_bind_config(struct usb_configuration *c,
-               const struct uvc_descriptor_header * const *fs_control,
-               const struct uvc_descriptor_header * const *ss_control,
-               const struct uvc_descriptor_header * const *fs_streaming,
-               const struct uvc_descriptor_header * const *hs_streaming,
-               const struct uvc_descriptor_header * const *ss_streaming)
-{
-       struct uvc_device *uvc;
-       int ret = 0;
-
-       /* TODO Check if the USB device controller supports the required
-        * features.
-        */
-       if (!gadget_is_dualspeed(c->cdev->gadget))
-               return -EINVAL;
-
-       uvc = kzalloc(sizeof(*uvc), GFP_KERNEL);
-       if (uvc == NULL)
-               return -ENOMEM;
-
-       uvc->state = UVC_STATE_DISCONNECTED;
-
-       /* Validate the descriptors. */
-       if (fs_control == NULL || fs_control[0] == NULL ||
-           fs_control[0]->bDescriptorSubType != UVC_VC_HEADER)
-               goto error;
-
-       if (ss_control == NULL || ss_control[0] == NULL ||
-           ss_control[0]->bDescriptorSubType != UVC_VC_HEADER)
-               goto error;
-
-       if (fs_streaming == NULL || fs_streaming[0] == NULL ||
-           fs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER)
-               goto error;
-
-       if (hs_streaming == NULL || hs_streaming[0] == NULL ||
-           hs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER)
-               goto error;
-
-       if (ss_streaming == NULL || ss_streaming[0] == NULL ||
-           ss_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER)
-               goto error;
-
-       uvc->desc.fs_control = fs_control;
-       uvc->desc.ss_control = ss_control;
-       uvc->desc.fs_streaming = fs_streaming;
-       uvc->desc.hs_streaming = hs_streaming;
-       uvc->desc.ss_streaming = ss_streaming;
-
-       /* String descriptors are global, we only need to allocate string IDs
-        * for the first UVC function. UVC functions beyond the first (if any)
-        * will reuse the same IDs.
-        */
-       if (uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id == 0) {
-               ret = usb_string_ids_tab(c->cdev, uvc_en_us_strings);
-               if (ret)
-                       goto error;
-               uvc_iad.iFunction =
-                       uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id;
-               uvc_control_intf.iInterface =
-                       uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id;
-               ret = uvc_en_us_strings[UVC_STRING_STREAMING_IDX].id;
-               uvc_streaming_intf_alt0.iInterface = ret;
-               uvc_streaming_intf_alt1.iInterface = ret;
-       }
-
-       /* Register the function. */
-       uvc->func.name = "uvc";
-       uvc->func.strings = uvc_function_strings;
-       uvc->func.bind = uvc_function_bind;
-       uvc->func.unbind = uvc_function_unbind;
-       uvc->func.get_alt = uvc_function_get_alt;
-       uvc->func.set_alt = uvc_function_set_alt;
-       uvc->func.disable = uvc_function_disable;
-       uvc->func.setup = uvc_function_setup;
-
-       ret = usb_add_function(c, &uvc->func);
-       if (ret)
-               kfree(uvc);
-
-       return ret;
-
-error:
-       kfree(uvc);
-       return ret;
-}
-
-module_param_named(trace, uvc_gadget_trace_param, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(trace, "Trace level bitmask");
-
diff --git a/drivers/usb/gadget/f_uvc.h b/drivers/usb/gadget/f_uvc.h
deleted file mode 100644 (file)
index ec52752..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- *     f_uvc.h  --  USB Video Class Gadget driver
- *
- *     Copyright (C) 2009-2010
- *         Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- *
- *     This program is free software; you can redistribute it and/or modify
- *     it under the terms of the GNU General Public License as published by
- *     the Free Software Foundation; either version 2 of the License, or
- *     (at your option) any later version.
- */
-
-#ifndef _F_UVC_H_
-#define _F_UVC_H_
-
-#include <linux/usb/composite.h>
-#include <linux/usb/video.h>
-
-int uvc_bind_config(struct usb_configuration *c,
-                   const struct uvc_descriptor_header * const *fs_control,
-                   const struct uvc_descriptor_header * const *hs_control,
-                   const struct uvc_descriptor_header * const *fs_streaming,
-                   const struct uvc_descriptor_header * const *hs_streaming,
-                   const struct uvc_descriptor_header * const *ss_streaming);
-
-#endif /* _F_UVC_H_ */
-
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
new file mode 100644 (file)
index 0000000..6d91f21
--- /dev/null
@@ -0,0 +1,34 @@
+#
+# USB peripheral controller drivers
+#
+
+ccflags-y                      := -I$(PWD)/drivers/usb/gadget/
+ccflags-y                      += -I$(PWD)/drivers/usb/gadget/udc/
+
+# USB Functions
+usb_f_acm-y                    := f_acm.o
+obj-$(CONFIG_USB_F_ACM)                += usb_f_acm.o
+usb_f_ss_lb-y                  := f_loopback.o f_sourcesink.o
+obj-$(CONFIG_USB_F_SS_LB)      += usb_f_ss_lb.o
+obj-$(CONFIG_USB_U_SERIAL)     += u_serial.o
+usb_f_serial-y                 := f_serial.o
+obj-$(CONFIG_USB_F_SERIAL)     += usb_f_serial.o
+usb_f_obex-y                   := f_obex.o
+obj-$(CONFIG_USB_F_OBEX)       += usb_f_obex.o
+obj-$(CONFIG_USB_U_ETHER)      += u_ether.o
+usb_f_ncm-y                    := f_ncm.o
+obj-$(CONFIG_USB_F_NCM)                += usb_f_ncm.o
+usb_f_ecm-y                    := f_ecm.o
+obj-$(CONFIG_USB_F_ECM)                += usb_f_ecm.o
+usb_f_phonet-y                 := f_phonet.o
+obj-$(CONFIG_USB_F_PHONET)     += usb_f_phonet.o
+usb_f_eem-y                    := f_eem.o
+obj-$(CONFIG_USB_F_EEM)                += usb_f_eem.o
+usb_f_ecm_subset-y             := f_subset.o
+obj-$(CONFIG_USB_F_SUBSET)     += usb_f_ecm_subset.o
+usb_f_rndis-y                  := f_rndis.o rndis.o
+obj-$(CONFIG_USB_F_RNDIS)      += usb_f_rndis.o
+usb_f_mass_storage-y           := f_mass_storage.o storage_common.o
+obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o
+usb_f_fs-y                     := f_fs.o
+obj-$(CONFIG_USB_F_FS)         += usb_f_fs.o
diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c
new file mode 100644 (file)
index 0000000..ab1065a
--- /dev/null
@@ -0,0 +1,848 @@
+/*
+ * f_acm.c -- USB CDC serial (ACM) function driver
+ *
+ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2008 by David Brownell
+ * Copyright (C) 2008 by Nokia Corporation
+ * Copyright (C) 2009 by Samsung Electronics
+ * Author: Michal Nazarewicz (mina86@mina86.com)
+ *
+ * This software is distributed under the terms of the GNU General
+ * Public License ("GPL") as published by the Free Software Foundation,
+ * either version 2 of that License or (at your option) any later version.
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
+
+#include "u_serial.h"
+#include "gadget_chips.h"
+
+
+/*
+ * This CDC ACM function support just wraps control functions and
+ * notifications around the generic serial-over-usb code.
+ *
+ * Because CDC ACM is standardized by the USB-IF, many host operating
+ * systems have drivers for it.  Accordingly, ACM is the preferred
+ * interop solution for serial-port type connections.  The control
+ * models are often not necessary, and in any case don't do much in
+ * this bare-bones implementation.
+ *
+ * Note that even MS-Windows has some support for ACM.  However, that
+ * support is somewhat broken because when you use ACM in a composite
+ * device, having multiple interfaces confuses the poor OS.  It doesn't
+ * seem to understand CDC Union descriptors.  The new "association"
+ * descriptors (roughly equivalent to CDC Unions) may sometimes help.
+ */
+
+struct f_acm {
+       struct gserial                  port;
+       u8                              ctrl_id, data_id;
+       u8                              port_num;
+
+       u8                              pending;
+
+       /* lock is mostly for pending and notify_req ... they get accessed
+        * by callbacks both from tty (open/close/break) under its spinlock,
+        * and notify_req.complete() which can't use that lock.
+        */
+       spinlock_t                      lock;
+
+       struct usb_ep                   *notify;
+       struct usb_request              *notify_req;
+
+       struct usb_cdc_line_coding      port_line_coding;       /* 8-N-1 etc */
+
+       /* SetControlLineState request -- CDC 1.1 section 6.2.14 (INPUT) */
+       u16                             port_handshake_bits;
+#define ACM_CTRL_RTS   (1 << 1)        /* unused with full duplex */
+#define ACM_CTRL_DTR   (1 << 0)        /* host is ready for data r/w */
+
+       /* SerialState notification -- CDC 1.1 section 6.3.5 (OUTPUT) */
+       u16                             serial_state;
+#define ACM_CTRL_OVERRUN       (1 << 6)
+#define ACM_CTRL_PARITY                (1 << 5)
+#define ACM_CTRL_FRAMING       (1 << 4)
+#define ACM_CTRL_RI            (1 << 3)
+#define ACM_CTRL_BRK           (1 << 2)
+#define ACM_CTRL_DSR           (1 << 1)
+#define ACM_CTRL_DCD           (1 << 0)
+};
+
+static inline struct f_acm *func_to_acm(struct usb_function *f)
+{
+       return container_of(f, struct f_acm, port.func);
+}
+
+static inline struct f_acm *port_to_acm(struct gserial *p)
+{
+       return container_of(p, struct f_acm, port);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* notification endpoint uses smallish and infrequent fixed-size messages */
+
+#define GS_NOTIFY_INTERVAL_MS          32
+#define GS_NOTIFY_MAXPACKET            10      /* notification + 2 bytes */
+
+/* interface and class descriptors: */
+
+static struct usb_interface_assoc_descriptor
+acm_iad_descriptor = {
+       .bLength =              sizeof acm_iad_descriptor,
+       .bDescriptorType =      USB_DT_INTERFACE_ASSOCIATION,
+
+       /* .bFirstInterface =   DYNAMIC, */
+       .bInterfaceCount =      2,      // control + data
+       .bFunctionClass =       USB_CLASS_COMM,
+       .bFunctionSubClass =    USB_CDC_SUBCLASS_ACM,
+       .bFunctionProtocol =    USB_CDC_ACM_PROTO_AT_V25TER,
+       /* .iFunction =         DYNAMIC */
+};
+
+
+static struct usb_interface_descriptor acm_control_interface_desc = {
+       .bLength =              USB_DT_INTERFACE_SIZE,
+       .bDescriptorType =      USB_DT_INTERFACE,
+       /* .bInterfaceNumber = DYNAMIC */
+       .bNumEndpoints =        1,
+       .bInterfaceClass =      USB_CLASS_COMM,
+       .bInterfaceSubClass =   USB_CDC_SUBCLASS_ACM,
+       .bInterfaceProtocol =   USB_CDC_ACM_PROTO_AT_V25TER,
+       /* .iInterface = DYNAMIC */
+};
+
+static struct usb_interface_descriptor acm_data_interface_desc = {
+       .bLength =              USB_DT_INTERFACE_SIZE,
+       .bDescriptorType =      USB_DT_INTERFACE,
+       /* .bInterfaceNumber = DYNAMIC */
+       .bNumEndpoints =        2,
+       .bInterfaceClass =      USB_CLASS_CDC_DATA,
+       .bInterfaceSubClass =   0,
+       .bInterfaceProtocol =   0,
+       /* .iInterface = DYNAMIC */
+};
+
+static struct usb_cdc_header_desc acm_header_desc = {
+       .bLength =              sizeof(acm_header_desc),
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubType =   USB_CDC_HEADER_TYPE,
+       .bcdCDC =               cpu_to_le16(0x0110),
+};
+
+static struct usb_cdc_call_mgmt_descriptor
+acm_call_mgmt_descriptor = {
+       .bLength =              sizeof(acm_call_mgmt_descriptor),
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubType =   USB_CDC_CALL_MANAGEMENT_TYPE,
+       .bmCapabilities =       0,
+       /* .bDataInterface = DYNAMIC */
+};
+
+static struct usb_cdc_acm_descriptor acm_descriptor = {
+       .bLength =              sizeof(acm_descriptor),
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubType =   USB_CDC_ACM_TYPE,
+       .bmCapabilities =       USB_CDC_CAP_LINE,
+};
+
+static struct usb_cdc_union_desc acm_union_desc = {
+       .bLength =              sizeof(acm_union_desc),
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubType =   USB_CDC_UNION_TYPE,
+       /* .bMasterInterface0 = DYNAMIC */
+       /* .bSlaveInterface0 =  DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor acm_fs_notify_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_INT,
+       .wMaxPacketSize =       cpu_to_le16(GS_NOTIFY_MAXPACKET),
+       .bInterval =            GS_NOTIFY_INTERVAL_MS,
+};
+
+static struct usb_endpoint_descriptor acm_fs_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor acm_fs_out_desc = {
+       .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[] = {
+       (struct usb_descriptor_header *) &acm_iad_descriptor,
+       (struct usb_descriptor_header *) &acm_control_interface_desc,
+       (struct usb_descriptor_header *) &acm_header_desc,
+       (struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
+       (struct usb_descriptor_header *) &acm_descriptor,
+       (struct usb_descriptor_header *) &acm_union_desc,
+       (struct usb_descriptor_header *) &acm_fs_notify_desc,
+       (struct usb_descriptor_header *) &acm_data_interface_desc,
+       (struct usb_descriptor_header *) &acm_fs_in_desc,
+       (struct usb_descriptor_header *) &acm_fs_out_desc,
+       NULL,
+};
+
+/* high speed support: */
+static struct usb_endpoint_descriptor acm_hs_notify_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_INT,
+       .wMaxPacketSize =       cpu_to_le16(GS_NOTIFY_MAXPACKET),
+       .bInterval =            USB_MS_TO_HS_INTERVAL(GS_NOTIFY_INTERVAL_MS),
+};
+
+static struct usb_endpoint_descriptor acm_hs_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor acm_hs_out_desc = {
+       .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[] = {
+       (struct usb_descriptor_header *) &acm_iad_descriptor,
+       (struct usb_descriptor_header *) &acm_control_interface_desc,
+       (struct usb_descriptor_header *) &acm_header_desc,
+       (struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
+       (struct usb_descriptor_header *) &acm_descriptor,
+       (struct usb_descriptor_header *) &acm_union_desc,
+       (struct usb_descriptor_header *) &acm_hs_notify_desc,
+       (struct usb_descriptor_header *) &acm_data_interface_desc,
+       (struct usb_descriptor_header *) &acm_hs_in_desc,
+       (struct usb_descriptor_header *) &acm_hs_out_desc,
+       NULL,
+};
+
+static struct usb_endpoint_descriptor acm_ss_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor acm_ss_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor acm_ss_bulk_comp_desc = {
+       .bLength =              sizeof acm_ss_bulk_comp_desc,
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_descriptor_header *acm_ss_function[] = {
+       (struct usb_descriptor_header *) &acm_iad_descriptor,
+       (struct usb_descriptor_header *) &acm_control_interface_desc,
+       (struct usb_descriptor_header *) &acm_header_desc,
+       (struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
+       (struct usb_descriptor_header *) &acm_descriptor,
+       (struct usb_descriptor_header *) &acm_union_desc,
+       (struct usb_descriptor_header *) &acm_hs_notify_desc,
+       (struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
+       (struct usb_descriptor_header *) &acm_data_interface_desc,
+       (struct usb_descriptor_header *) &acm_ss_in_desc,
+       (struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
+       (struct usb_descriptor_header *) &acm_ss_out_desc,
+       (struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
+       NULL,
+};
+
+/* string descriptors: */
+
+#define ACM_CTRL_IDX   0
+#define ACM_DATA_IDX   1
+#define ACM_IAD_IDX    2
+
+/* static strings, in UTF-8 */
+static struct usb_string acm_string_defs[] = {
+       [ACM_CTRL_IDX].s = "CDC Abstract Control Model (ACM)",
+       [ACM_DATA_IDX].s = "CDC ACM Data",
+       [ACM_IAD_IDX ].s = "CDC Serial",
+       {  } /* end of list */
+};
+
+static struct usb_gadget_strings acm_string_table = {
+       .language =             0x0409, /* en-us */
+       .strings =              acm_string_defs,
+};
+
+static struct usb_gadget_strings *acm_strings[] = {
+       &acm_string_table,
+       NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* ACM control ... data handling is delegated to tty library code.
+ * The main task of this function is to activate and deactivate
+ * that code based on device state; track parameters like line
+ * speed, handshake state, and so on; and issue notifications.
+ */
+
+static void acm_complete_set_line_coding(struct usb_ep *ep,
+               struct usb_request *req)
+{
+       struct f_acm    *acm = ep->driver_data;
+       struct usb_composite_dev *cdev = acm->port.func.config->cdev;
+
+       if (req->status != 0) {
+               DBG(cdev, "acm ttyGS%d completion, err %d\n",
+                               acm->port_num, req->status);
+               return;
+       }
+
+       /* normal completion */
+       if (req->actual != sizeof(acm->port_line_coding)) {
+               DBG(cdev, "acm ttyGS%d short resp, len %d\n",
+                               acm->port_num, req->actual);
+               usb_ep_set_halt(ep);
+       } else {
+               struct usb_cdc_line_coding      *value = req->buf;
+
+               /* REVISIT:  we currently just remember this data.
+                * If we change that, (a) validate it first, then
+                * (b) update whatever hardware needs updating,
+                * (c) worry about locking.  This is information on
+                * the order of 9600-8-N-1 ... most of which means
+                * nothing unless we control a real RS232 line.
+                */
+               acm->port_line_coding = *value;
+       }
+}
+
+static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+       struct f_acm            *acm = func_to_acm(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+       struct usb_request      *req = cdev->req;
+       int                     value = -EOPNOTSUPP;
+       u16                     w_index = le16_to_cpu(ctrl->wIndex);
+       u16                     w_value = le16_to_cpu(ctrl->wValue);
+       u16                     w_length = le16_to_cpu(ctrl->wLength);
+
+       /* composite driver infrastructure handles everything except
+        * CDC class messages; interface activation uses set_alt().
+        *
+        * Note CDC spec table 4 lists the ACM request profile.  It requires
+        * encapsulated command support ... we don't handle any, and respond
+        * to them by stalling.  Options include get/set/clear comm features
+        * (not that useful) and SEND_BREAK.
+        */
+       switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+
+       /* SET_LINE_CODING ... just read and save what the host sends */
+       case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+                       | USB_CDC_REQ_SET_LINE_CODING:
+               if (w_length != sizeof(struct usb_cdc_line_coding)
+                               || w_index != acm->ctrl_id)
+                       goto invalid;
+
+               value = w_length;
+               cdev->gadget->ep0->driver_data = acm;
+               req->complete = acm_complete_set_line_coding;
+               break;
+
+       /* GET_LINE_CODING ... return what host sent, or initial value */
+       case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+                       | USB_CDC_REQ_GET_LINE_CODING:
+               if (w_index != acm->ctrl_id)
+                       goto invalid;
+
+               value = min_t(unsigned, w_length,
+                               sizeof(struct usb_cdc_line_coding));
+               memcpy(req->buf, &acm->port_line_coding, value);
+               break;
+
+       /* SET_CONTROL_LINE_STATE ... save what the host sent */
+       case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+                       | USB_CDC_REQ_SET_CONTROL_LINE_STATE:
+               if (w_index != acm->ctrl_id)
+                       goto invalid;
+
+               value = 0;
+
+               /* FIXME we should not allow data to flow until the
+                * host sets the ACM_CTRL_DTR bit; and when it clears
+                * that bit, we should return to that no-flow state.
+                */
+               acm->port_handshake_bits = w_value;
+               break;
+
+       default:
+invalid:
+               VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+                       ctrl->bRequestType, ctrl->bRequest,
+                       w_value, w_index, w_length);
+       }
+
+       /* respond with data transfer or status phase? */
+       if (value >= 0) {
+               DBG(cdev, "acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n",
+                       acm->port_num, ctrl->bRequestType, ctrl->bRequest,
+                       w_value, w_index, w_length);
+               req->zero = 0;
+               req->length = value;
+               value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+               if (value < 0)
+                       ERROR(cdev, "acm response on ttyGS%d, err %d\n",
+                                       acm->port_num, value);
+       }
+
+       /* device either stalls (value < 0) or reports success */
+       return value;
+}
+
+static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+       struct f_acm            *acm = func_to_acm(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+
+       /* we know alt == 0, so this is an activation or a reset */
+
+       if (intf == acm->ctrl_id) {
+               if (acm->notify->driver_data) {
+                       VDBG(cdev, "reset acm control interface %d\n", intf);
+                       usb_ep_disable(acm->notify);
+               } else {
+                       VDBG(cdev, "init acm ctrl interface %d\n", intf);
+                       if (config_ep_by_speed(cdev->gadget, f, acm->notify))
+                               return -EINVAL;
+               }
+               usb_ep_enable(acm->notify);
+               acm->notify->driver_data = acm;
+
+       } else if (intf == acm->data_id) {
+               if (acm->port.in->driver_data) {
+                       DBG(cdev, "reset acm ttyGS%d\n", acm->port_num);
+                       gserial_disconnect(&acm->port);
+               }
+               if (!acm->port.in->desc || !acm->port.out->desc) {
+                       DBG(cdev, "activate acm ttyGS%d\n", acm->port_num);
+                       if (config_ep_by_speed(cdev->gadget, f,
+                                              acm->port.in) ||
+                           config_ep_by_speed(cdev->gadget, f,
+                                              acm->port.out)) {
+                               acm->port.in->desc = NULL;
+                               acm->port.out->desc = NULL;
+                               return -EINVAL;
+                       }
+               }
+               gserial_connect(&acm->port, acm->port_num);
+
+       } else
+               return -EINVAL;
+
+       return 0;
+}
+
+static void acm_disable(struct usb_function *f)
+{
+       struct f_acm    *acm = func_to_acm(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+
+       DBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num);
+       gserial_disconnect(&acm->port);
+       usb_ep_disable(acm->notify);
+       acm->notify->driver_data = NULL;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * acm_cdc_notify - issue CDC notification to host
+ * @acm: wraps host to be notified
+ * @type: notification type
+ * @value: Refer to cdc specs, wValue field.
+ * @data: data to be sent
+ * @length: size of data
+ * Context: irqs blocked, acm->lock held, acm_notify_req non-null
+ *
+ * Returns zero on success or a negative errno.
+ *
+ * See section 6.3.5 of the CDC 1.1 specification for information
+ * about the only notification we issue:  SerialState change.
+ */
+static int acm_cdc_notify(struct f_acm *acm, u8 type, u16 value,
+               void *data, unsigned length)
+{
+       struct usb_ep                   *ep = acm->notify;
+       struct usb_request              *req;
+       struct usb_cdc_notification     *notify;
+       const unsigned                  len = sizeof(*notify) + length;
+       void                            *buf;
+       int                             status;
+
+       req = acm->notify_req;
+       acm->notify_req = NULL;
+       acm->pending = false;
+
+       req->length = len;
+       notify = req->buf;
+       buf = notify + 1;
+
+       notify->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
+                       | USB_RECIP_INTERFACE;
+       notify->bNotificationType = type;
+       notify->wValue = cpu_to_le16(value);
+       notify->wIndex = cpu_to_le16(acm->ctrl_id);
+       notify->wLength = cpu_to_le16(length);
+       memcpy(buf, data, length);
+
+       /* ep_queue() can complete immediately if it fills the fifo... */
+       spin_unlock(&acm->lock);
+       status = usb_ep_queue(ep, req, GFP_ATOMIC);
+       spin_lock(&acm->lock);
+
+       if (status < 0) {
+               ERROR(acm->port.func.config->cdev,
+                               "acm ttyGS%d can't notify serial state, %d\n",
+                               acm->port_num, status);
+               acm->notify_req = req;
+       }
+
+       return status;
+}
+
+static int acm_notify_serial_state(struct f_acm *acm)
+{
+       struct usb_composite_dev *cdev = acm->port.func.config->cdev;
+       int                     status;
+
+       spin_lock(&acm->lock);
+       if (acm->notify_req) {
+               DBG(cdev, "acm ttyGS%d serial state %04x\n",
+                               acm->port_num, acm->serial_state);
+               status = acm_cdc_notify(acm, USB_CDC_NOTIFY_SERIAL_STATE,
+                               0, &acm->serial_state, sizeof(acm->serial_state));
+       } else {
+               acm->pending = true;
+               status = 0;
+       }
+       spin_unlock(&acm->lock);
+       return status;
+}
+
+static void acm_cdc_notify_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct f_acm            *acm = req->context;
+       u8                      doit = false;
+
+       /* on this call path we do NOT hold the port spinlock,
+        * which is why ACM needs its own spinlock
+        */
+       spin_lock(&acm->lock);
+       if (req->status != -ESHUTDOWN)
+               doit = acm->pending;
+       acm->notify_req = req;
+       spin_unlock(&acm->lock);
+
+       if (doit)
+               acm_notify_serial_state(acm);
+}
+
+/* connect == the TTY link is open */
+
+static void acm_connect(struct gserial *port)
+{
+       struct f_acm            *acm = port_to_acm(port);
+
+       acm->serial_state |= ACM_CTRL_DSR | ACM_CTRL_DCD;
+       acm_notify_serial_state(acm);
+}
+
+static void acm_disconnect(struct gserial *port)
+{
+       struct f_acm            *acm = port_to_acm(port);
+
+       acm->serial_state &= ~(ACM_CTRL_DSR | ACM_CTRL_DCD);
+       acm_notify_serial_state(acm);
+}
+
+static int acm_send_break(struct gserial *port, int duration)
+{
+       struct f_acm            *acm = port_to_acm(port);
+       u16                     state;
+
+       state = acm->serial_state;
+       state &= ~ACM_CTRL_BRK;
+       if (duration)
+               state |= ACM_CTRL_BRK;
+
+       acm->serial_state = state;
+       return acm_notify_serial_state(acm);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* ACM function driver setup/binding */
+static int
+acm_bind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct usb_composite_dev *cdev = c->cdev;
+       struct f_acm            *acm = func_to_acm(f);
+       struct usb_string       *us;
+       int                     status;
+       struct usb_ep           *ep;
+
+       /* REVISIT might want instance-specific strings to help
+        * distinguish instances ...
+        */
+
+       /* maybe allocate device-global string IDs, and patch descriptors */
+       us = usb_gstrings_attach(cdev, acm_strings,
+                       ARRAY_SIZE(acm_string_defs));
+       if (IS_ERR(us))
+               return PTR_ERR(us);
+       acm_control_interface_desc.iInterface = us[ACM_CTRL_IDX].id;
+       acm_data_interface_desc.iInterface = us[ACM_DATA_IDX].id;
+       acm_iad_descriptor.iFunction = us[ACM_IAD_IDX].id;
+
+       /* allocate instance-specific interface IDs, and patch descriptors */
+       status = usb_interface_id(c, f);
+       if (status < 0)
+               goto fail;
+       acm->ctrl_id = status;
+       acm_iad_descriptor.bFirstInterface = status;
+
+       acm_control_interface_desc.bInterfaceNumber = status;
+       acm_union_desc .bMasterInterface0 = status;
+
+       status = usb_interface_id(c, f);
+       if (status < 0)
+               goto fail;
+       acm->data_id = status;
+
+       acm_data_interface_desc.bInterfaceNumber = status;
+       acm_union_desc.bSlaveInterface0 = status;
+       acm_call_mgmt_descriptor.bDataInterface = status;
+
+       status = -ENODEV;
+
+       /* allocate instance-specific endpoints */
+       ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_in_desc);
+       if (!ep)
+               goto fail;
+       acm->port.in = ep;
+       ep->driver_data = cdev; /* claim */
+
+       ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc);
+       if (!ep)
+               goto fail;
+       acm->port.out = ep;
+       ep->driver_data = cdev; /* claim */
+
+       ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc);
+       if (!ep)
+               goto fail;
+       acm->notify = ep;
+       ep->driver_data = cdev; /* claim */
+
+       /* allocate notification */
+       acm->notify_req = gs_alloc_req(ep,
+                       sizeof(struct usb_cdc_notification) + 2,
+                       GFP_KERNEL);
+       if (!acm->notify_req)
+               goto fail;
+
+       acm->notify_req->complete = acm_cdc_notify_complete;
+       acm->notify_req->context = acm;
+
+       /* support all relevant hardware speeds... we expect that when
+        * hardware is dual speed, all bulk-capable endpoints work at
+        * both speeds
+        */
+       acm_hs_in_desc.bEndpointAddress = acm_fs_in_desc.bEndpointAddress;
+       acm_hs_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress;
+       acm_hs_notify_desc.bEndpointAddress =
+               acm_fs_notify_desc.bEndpointAddress;
+
+       acm_ss_in_desc.bEndpointAddress = acm_fs_in_desc.bEndpointAddress;
+       acm_ss_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress;
+
+       status = usb_assign_descriptors(f, acm_fs_function, acm_hs_function,
+                       acm_ss_function);
+       if (status)
+               goto fail;
+
+       DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n",
+                       acm->port_num,
+                       gadget_is_superspeed(c->cdev->gadget) ? "super" :
+                       gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+                       acm->port.in->name, acm->port.out->name,
+                       acm->notify->name);
+       return 0;
+
+fail:
+       if (acm->notify_req)
+               gs_free_req(acm->notify, acm->notify_req);
+
+       /* we might as well release our claims on endpoints */
+       if (acm->notify)
+               acm->notify->driver_data = NULL;
+       if (acm->port.out)
+               acm->port.out->driver_data = NULL;
+       if (acm->port.in)
+               acm->port.in->driver_data = NULL;
+
+       ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status);
+
+       return status;
+}
+
+static void acm_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct f_acm            *acm = func_to_acm(f);
+
+       acm_string_defs[0].id = 0;
+       usb_free_all_descriptors(f);
+       if (acm->notify_req)
+               gs_free_req(acm->notify, acm->notify_req);
+}
+
+static void acm_free_func(struct usb_function *f)
+{
+       struct f_acm            *acm = func_to_acm(f);
+
+       kfree(acm);
+}
+
+static struct usb_function *acm_alloc_func(struct usb_function_instance *fi)
+{
+       struct f_serial_opts *opts;
+       struct f_acm *acm;
+
+       acm = kzalloc(sizeof(*acm), GFP_KERNEL);
+       if (!acm)
+               return ERR_PTR(-ENOMEM);
+
+       spin_lock_init(&acm->lock);
+
+       acm->port.connect = acm_connect;
+       acm->port.disconnect = acm_disconnect;
+       acm->port.send_break = acm_send_break;
+
+       acm->port.func.name = "acm";
+       acm->port.func.strings = acm_strings;
+       /* descriptors are per-instance copies */
+       acm->port.func.bind = acm_bind;
+       acm->port.func.set_alt = acm_set_alt;
+       acm->port.func.setup = acm_setup;
+       acm->port.func.disable = acm_disable;
+
+       opts = container_of(fi, struct f_serial_opts, func_inst);
+       acm->port_num = opts->port_num;
+       acm->port.func.unbind = acm_unbind;
+       acm->port.func.free_func = acm_free_func;
+
+       return &acm->port.func;
+}
+
+static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct f_serial_opts,
+                       func_inst.group);
+}
+
+CONFIGFS_ATTR_STRUCT(f_serial_opts);
+static ssize_t f_acm_attr_show(struct config_item *item,
+                                struct configfs_attribute *attr,
+                                char *page)
+{
+       struct f_serial_opts *opts = to_f_serial_opts(item);
+       struct f_serial_opts_attribute *f_serial_opts_attr =
+               container_of(attr, struct f_serial_opts_attribute, attr);
+       ssize_t ret = 0;
+
+       if (f_serial_opts_attr->show)
+               ret = f_serial_opts_attr->show(opts, page);
+       return ret;
+}
+
+static void acm_attr_release(struct config_item *item)
+{
+       struct f_serial_opts *opts = to_f_serial_opts(item);
+
+       usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations acm_item_ops = {
+       .release                = acm_attr_release,
+       .show_attribute         = f_acm_attr_show,
+};
+
+static ssize_t f_acm_port_num_show(struct f_serial_opts *opts, char *page)
+{
+       return sprintf(page, "%u\n", opts->port_num);
+}
+
+static struct f_serial_opts_attribute f_acm_port_num =
+       __CONFIGFS_ATTR_RO(port_num, f_acm_port_num_show);
+
+
+static struct configfs_attribute *acm_attrs[] = {
+       &f_acm_port_num.attr,
+       NULL,
+};
+
+static struct config_item_type acm_func_type = {
+       .ct_item_ops    = &acm_item_ops,
+       .ct_attrs       = acm_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+static void acm_free_instance(struct usb_function_instance *fi)
+{
+       struct f_serial_opts *opts;
+
+       opts = container_of(fi, struct f_serial_opts, func_inst);
+       gserial_free_line(opts->port_num);
+       kfree(opts);
+}
+
+static struct usb_function_instance *acm_alloc_instance(void)
+{
+       struct f_serial_opts *opts;
+       int ret;
+
+       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+       if (!opts)
+               return ERR_PTR(-ENOMEM);
+       opts->func_inst.free_func_inst = acm_free_instance;
+       ret = gserial_alloc_line(&opts->port_num);
+       if (ret) {
+               kfree(opts);
+               return ERR_PTR(ret);
+       }
+       config_group_init_type_name(&opts->func_inst.group, "",
+                       &acm_func_type);
+       return &opts->func_inst;
+}
+DECLARE_USB_FUNCTION_INIT(acm, acm_alloc_instance, acm_alloc_func);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c
new file mode 100644 (file)
index 0000000..798760f
--- /dev/null
@@ -0,0 +1,973 @@
+/*
+ * f_ecm.c -- USB CDC Ethernet (ECM) link function driver
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * 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.
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/etherdevice.h>
+
+#include "u_ether.h"
+#include "u_ether_configfs.h"
+#include "u_ecm.h"
+
+
+/*
+ * This function is a "CDC Ethernet Networking Control Model" (CDC ECM)
+ * Ethernet link.  The data transfer model is simple (packets sent and
+ * received over bulk endpoints using normal short packet termination),
+ * and the control model exposes various data and optional notifications.
+ *
+ * ECM is well standardized and (except for Microsoft) supported by most
+ * operating systems with USB host support.  It's the preferred interop
+ * solution for Ethernet over USB, at least for firmware based solutions.
+ * (Hardware solutions tend to be more minimalist.)  A newer and simpler
+ * "Ethernet Emulation Model" (CDC EEM) hasn't yet caught on.
+ *
+ * Note that ECM requires the use of "alternate settings" for its data
+ * interface.  This means that the set_alt() method has real work to do,
+ * and also means that a get_alt() method is required.
+ */
+
+
+enum ecm_notify_state {
+       ECM_NOTIFY_NONE,                /* don't notify */
+       ECM_NOTIFY_CONNECT,             /* issue CONNECT next */
+       ECM_NOTIFY_SPEED,               /* issue SPEED_CHANGE next */
+};
+
+struct f_ecm {
+       struct gether                   port;
+       u8                              ctrl_id, data_id;
+
+       char                            ethaddr[14];
+
+       struct usb_ep                   *notify;
+       struct usb_request              *notify_req;
+       u8                              notify_state;
+       bool                            is_open;
+
+       /* FIXME is_open needs some irq-ish locking
+        * ... possibly the same as port.ioport
+        */
+};
+
+static inline struct f_ecm *func_to_ecm(struct usb_function *f)
+{
+       return container_of(f, struct f_ecm, port.func);
+}
+
+/* peak (theoretical) bulk transfer rate in bits-per-second */
+static inline unsigned ecm_bitrate(struct usb_gadget *g)
+{
+       if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
+               return 13 * 1024 * 8 * 1000 * 8;
+       else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+               return 13 * 512 * 8 * 1000 * 8;
+       else
+               return 19 * 64 * 1 * 1000 * 8;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Include the status endpoint if we can, even though it's optional.
+ *
+ * Use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one
+ * packet, to simplify cancellation; and a big transfer interval, to
+ * waste less bandwidth.
+ *
+ * Some drivers (like Linux 2.4 cdc-ether!) "need" it to exist even
+ * if they ignore the connect/disconnect notifications that real aether
+ * can provide.  More advanced cdc configurations might want to support
+ * encapsulated commands (vendor-specific, using control-OUT).
+ */
+
+#define ECM_STATUS_INTERVAL_MS         32
+#define ECM_STATUS_BYTECOUNT           16      /* 8 byte header + data */
+
+
+/* interface descriptor: */
+
+static struct usb_interface_assoc_descriptor
+ecm_iad_descriptor = {
+       .bLength =              sizeof ecm_iad_descriptor,
+       .bDescriptorType =      USB_DT_INTERFACE_ASSOCIATION,
+
+       /* .bFirstInterface =   DYNAMIC, */
+       .bInterfaceCount =      2,      /* control + data */
+       .bFunctionClass =       USB_CLASS_COMM,
+       .bFunctionSubClass =    USB_CDC_SUBCLASS_ETHERNET,
+       .bFunctionProtocol =    USB_CDC_PROTO_NONE,
+       /* .iFunction =         DYNAMIC */
+};
+
+
+static struct usb_interface_descriptor ecm_control_intf = {
+       .bLength =              sizeof ecm_control_intf,
+       .bDescriptorType =      USB_DT_INTERFACE,
+
+       /* .bInterfaceNumber = DYNAMIC */
+       /* status endpoint is optional; this could be patched later */
+       .bNumEndpoints =        1,
+       .bInterfaceClass =      USB_CLASS_COMM,
+       .bInterfaceSubClass =   USB_CDC_SUBCLASS_ETHERNET,
+       .bInterfaceProtocol =   USB_CDC_PROTO_NONE,
+       /* .iInterface = DYNAMIC */
+};
+
+static struct usb_cdc_header_desc ecm_header_desc = {
+       .bLength =              sizeof ecm_header_desc,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubType =   USB_CDC_HEADER_TYPE,
+
+       .bcdCDC =               cpu_to_le16(0x0110),
+};
+
+static struct usb_cdc_union_desc ecm_union_desc = {
+       .bLength =              sizeof(ecm_union_desc),
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubType =   USB_CDC_UNION_TYPE,
+       /* .bMasterInterface0 = DYNAMIC */
+       /* .bSlaveInterface0 =  DYNAMIC */
+};
+
+static struct usb_cdc_ether_desc ecm_desc = {
+       .bLength =              sizeof ecm_desc,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubType =   USB_CDC_ETHERNET_TYPE,
+
+       /* this descriptor actually adds value, surprise! */
+       /* .iMACAddress = DYNAMIC */
+       .bmEthernetStatistics = cpu_to_le32(0), /* no statistics */
+       .wMaxSegmentSize =      cpu_to_le16(ETH_FRAME_LEN),
+       .wNumberMCFilters =     cpu_to_le16(0),
+       .bNumberPowerFilters =  0,
+};
+
+/* the default data interface has no endpoints ... */
+
+static struct usb_interface_descriptor ecm_data_nop_intf = {
+       .bLength =              sizeof ecm_data_nop_intf,
+       .bDescriptorType =      USB_DT_INTERFACE,
+
+       .bInterfaceNumber =     1,
+       .bAlternateSetting =    0,
+       .bNumEndpoints =        0,
+       .bInterfaceClass =      USB_CLASS_CDC_DATA,
+       .bInterfaceSubClass =   0,
+       .bInterfaceProtocol =   0,
+       /* .iInterface = DYNAMIC */
+};
+
+/* ... but the "real" data interface has two bulk endpoints */
+
+static struct usb_interface_descriptor ecm_data_intf = {
+       .bLength =              sizeof ecm_data_intf,
+       .bDescriptorType =      USB_DT_INTERFACE,
+
+       .bInterfaceNumber =     1,
+       .bAlternateSetting =    1,
+       .bNumEndpoints =        2,
+       .bInterfaceClass =      USB_CLASS_CDC_DATA,
+       .bInterfaceSubClass =   0,
+       .bInterfaceProtocol =   0,
+       /* .iInterface = DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor fs_ecm_notify_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_INT,
+       .wMaxPacketSize =       cpu_to_le16(ECM_STATUS_BYTECOUNT),
+       .bInterval =            ECM_STATUS_INTERVAL_MS,
+};
+
+static struct usb_endpoint_descriptor fs_ecm_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor fs_ecm_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *ecm_fs_function[] = {
+       /* CDC ECM control descriptors */
+       (struct usb_descriptor_header *) &ecm_iad_descriptor,
+       (struct usb_descriptor_header *) &ecm_control_intf,
+       (struct usb_descriptor_header *) &ecm_header_desc,
+       (struct usb_descriptor_header *) &ecm_union_desc,
+       (struct usb_descriptor_header *) &ecm_desc,
+
+       /* NOTE: status endpoint might need to be removed */
+       (struct usb_descriptor_header *) &fs_ecm_notify_desc,
+
+       /* data interface, altsettings 0 and 1 */
+       (struct usb_descriptor_header *) &ecm_data_nop_intf,
+       (struct usb_descriptor_header *) &ecm_data_intf,
+       (struct usb_descriptor_header *) &fs_ecm_in_desc,
+       (struct usb_descriptor_header *) &fs_ecm_out_desc,
+       NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor hs_ecm_notify_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_INT,
+       .wMaxPacketSize =       cpu_to_le16(ECM_STATUS_BYTECOUNT),
+       .bInterval =            USB_MS_TO_HS_INTERVAL(ECM_STATUS_INTERVAL_MS),
+};
+
+static struct usb_endpoint_descriptor hs_ecm_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor hs_ecm_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *ecm_hs_function[] = {
+       /* CDC ECM control descriptors */
+       (struct usb_descriptor_header *) &ecm_iad_descriptor,
+       (struct usb_descriptor_header *) &ecm_control_intf,
+       (struct usb_descriptor_header *) &ecm_header_desc,
+       (struct usb_descriptor_header *) &ecm_union_desc,
+       (struct usb_descriptor_header *) &ecm_desc,
+
+       /* NOTE: status endpoint might need to be removed */
+       (struct usb_descriptor_header *) &hs_ecm_notify_desc,
+
+       /* data interface, altsettings 0 and 1 */
+       (struct usb_descriptor_header *) &ecm_data_nop_intf,
+       (struct usb_descriptor_header *) &ecm_data_intf,
+       (struct usb_descriptor_header *) &hs_ecm_in_desc,
+       (struct usb_descriptor_header *) &hs_ecm_out_desc,
+       NULL,
+};
+
+/* super speed support: */
+
+static struct usb_endpoint_descriptor ss_ecm_notify_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_INT,
+       .wMaxPacketSize =       cpu_to_le16(ECM_STATUS_BYTECOUNT),
+       .bInterval =            USB_MS_TO_HS_INTERVAL(ECM_STATUS_INTERVAL_MS),
+};
+
+static struct usb_ss_ep_comp_descriptor ss_ecm_intr_comp_desc = {
+       .bLength =              sizeof ss_ecm_intr_comp_desc,
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+
+       /* the following 3 values can be tweaked if necessary */
+       /* .bMaxBurst =         0, */
+       /* .bmAttributes =      0, */
+       .wBytesPerInterval =    cpu_to_le16(ECM_STATUS_BYTECOUNT),
+};
+
+static struct usb_endpoint_descriptor ss_ecm_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor ss_ecm_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ss_ecm_bulk_comp_desc = {
+       .bLength =              sizeof ss_ecm_bulk_comp_desc,
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+
+       /* the following 2 values can be tweaked if necessary */
+       /* .bMaxBurst =         0, */
+       /* .bmAttributes =      0, */
+};
+
+static struct usb_descriptor_header *ecm_ss_function[] = {
+       /* CDC ECM control descriptors */
+       (struct usb_descriptor_header *) &ecm_iad_descriptor,
+       (struct usb_descriptor_header *) &ecm_control_intf,
+       (struct usb_descriptor_header *) &ecm_header_desc,
+       (struct usb_descriptor_header *) &ecm_union_desc,
+       (struct usb_descriptor_header *) &ecm_desc,
+
+       /* NOTE: status endpoint might need to be removed */
+       (struct usb_descriptor_header *) &ss_ecm_notify_desc,
+       (struct usb_descriptor_header *) &ss_ecm_intr_comp_desc,
+
+       /* data interface, altsettings 0 and 1 */
+       (struct usb_descriptor_header *) &ecm_data_nop_intf,
+       (struct usb_descriptor_header *) &ecm_data_intf,
+       (struct usb_descriptor_header *) &ss_ecm_in_desc,
+       (struct usb_descriptor_header *) &ss_ecm_bulk_comp_desc,
+       (struct usb_descriptor_header *) &ss_ecm_out_desc,
+       (struct usb_descriptor_header *) &ss_ecm_bulk_comp_desc,
+       NULL,
+};
+
+/* string descriptors: */
+
+static struct usb_string ecm_string_defs[] = {
+       [0].s = "CDC Ethernet Control Model (ECM)",
+       [1].s = "",
+       [2].s = "CDC Ethernet Data",
+       [3].s = "CDC ECM",
+       {  } /* end of list */
+};
+
+static struct usb_gadget_strings ecm_string_table = {
+       .language =             0x0409, /* en-us */
+       .strings =              ecm_string_defs,
+};
+
+static struct usb_gadget_strings *ecm_strings[] = {
+       &ecm_string_table,
+       NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static void ecm_do_notify(struct f_ecm *ecm)
+{
+       struct usb_request              *req = ecm->notify_req;
+       struct usb_cdc_notification     *event;
+       struct usb_composite_dev        *cdev = ecm->port.func.config->cdev;
+       __le32                          *data;
+       int                             status;
+
+       /* notification already in flight? */
+       if (!req)
+               return;
+
+       event = req->buf;
+       switch (ecm->notify_state) {
+       case ECM_NOTIFY_NONE:
+               return;
+
+       case ECM_NOTIFY_CONNECT:
+               event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
+               if (ecm->is_open)
+                       event->wValue = cpu_to_le16(1);
+               else
+                       event->wValue = cpu_to_le16(0);
+               event->wLength = 0;
+               req->length = sizeof *event;
+
+               DBG(cdev, "notify connect %s\n",
+                               ecm->is_open ? "true" : "false");
+               ecm->notify_state = ECM_NOTIFY_SPEED;
+               break;
+
+       case ECM_NOTIFY_SPEED:
+               event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE;
+               event->wValue = cpu_to_le16(0);
+               event->wLength = cpu_to_le16(8);
+               req->length = ECM_STATUS_BYTECOUNT;
+
+               /* SPEED_CHANGE data is up/down speeds in bits/sec */
+               data = req->buf + sizeof *event;
+               data[0] = cpu_to_le32(ecm_bitrate(cdev->gadget));
+               data[1] = data[0];
+
+               DBG(cdev, "notify speed %d\n", ecm_bitrate(cdev->gadget));
+               ecm->notify_state = ECM_NOTIFY_NONE;
+               break;
+       }
+       event->bmRequestType = 0xA1;
+       event->wIndex = cpu_to_le16(ecm->ctrl_id);
+
+       ecm->notify_req = NULL;
+       status = usb_ep_queue(ecm->notify, req, GFP_ATOMIC);
+       if (status < 0) {
+               ecm->notify_req = req;
+               DBG(cdev, "notify --> %d\n", status);
+       }
+}
+
+static void ecm_notify(struct f_ecm *ecm)
+{
+       /* NOTE on most versions of Linux, host side cdc-ethernet
+        * won't listen for notifications until its netdevice opens.
+        * The first notification then sits in the FIFO for a long
+        * time, and the second one is queued.
+        */
+       ecm->notify_state = ECM_NOTIFY_CONNECT;
+       ecm_do_notify(ecm);
+}
+
+static void ecm_notify_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct f_ecm                    *ecm = req->context;
+       struct usb_composite_dev        *cdev = ecm->port.func.config->cdev;
+       struct usb_cdc_notification     *event = req->buf;
+
+       switch (req->status) {
+       case 0:
+               /* no fault */
+               break;
+       case -ECONNRESET:
+       case -ESHUTDOWN:
+               ecm->notify_state = ECM_NOTIFY_NONE;
+               break;
+       default:
+               DBG(cdev, "event %02x --> %d\n",
+                       event->bNotificationType, req->status);
+               break;
+       }
+       ecm->notify_req = req;
+       ecm_do_notify(ecm);
+}
+
+static int ecm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+       struct f_ecm            *ecm = func_to_ecm(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+       struct usb_request      *req = cdev->req;
+       int                     value = -EOPNOTSUPP;
+       u16                     w_index = le16_to_cpu(ctrl->wIndex);
+       u16                     w_value = le16_to_cpu(ctrl->wValue);
+       u16                     w_length = le16_to_cpu(ctrl->wLength);
+
+       /* composite driver infrastructure handles everything except
+        * CDC class messages; interface activation uses set_alt().
+        */
+       switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+       case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+                       | USB_CDC_SET_ETHERNET_PACKET_FILTER:
+               /* see 6.2.30: no data, wIndex = interface,
+                * wValue = packet filter bitmap
+                */
+               if (w_length != 0 || w_index != ecm->ctrl_id)
+                       goto invalid;
+               DBG(cdev, "packet filter %02x\n", w_value);
+               /* REVISIT locking of cdc_filter.  This assumes the UDC
+                * driver won't have a concurrent packet TX irq running on
+                * another CPU; or that if it does, this write is atomic...
+                */
+               ecm->port.cdc_filter = w_value;
+               value = 0;
+               break;
+
+       /* and optionally:
+        * case USB_CDC_SEND_ENCAPSULATED_COMMAND:
+        * case USB_CDC_GET_ENCAPSULATED_RESPONSE:
+        * case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS:
+        * case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER:
+        * case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER:
+        * case USB_CDC_GET_ETHERNET_STATISTIC:
+        */
+
+       default:
+invalid:
+               DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+                       ctrl->bRequestType, ctrl->bRequest,
+                       w_value, w_index, w_length);
+       }
+
+       /* respond with data transfer or status phase? */
+       if (value >= 0) {
+               DBG(cdev, "ecm req%02x.%02x v%04x i%04x l%d\n",
+                       ctrl->bRequestType, ctrl->bRequest,
+                       w_value, w_index, w_length);
+               req->zero = 0;
+               req->length = value;
+               value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+               if (value < 0)
+                       ERROR(cdev, "ecm req %02x.%02x response err %d\n",
+                                       ctrl->bRequestType, ctrl->bRequest,
+                                       value);
+       }
+
+       /* device either stalls (value < 0) or reports success */
+       return value;
+}
+
+
+static int ecm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+       struct f_ecm            *ecm = func_to_ecm(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+
+       /* Control interface has only altsetting 0 */
+       if (intf == ecm->ctrl_id) {
+               if (alt != 0)
+                       goto fail;
+
+               if (ecm->notify->driver_data) {
+                       VDBG(cdev, "reset ecm control %d\n", intf);
+                       usb_ep_disable(ecm->notify);
+               }
+               if (!(ecm->notify->desc)) {
+                       VDBG(cdev, "init ecm ctrl %d\n", intf);
+                       if (config_ep_by_speed(cdev->gadget, f, ecm->notify))
+                               goto fail;
+               }
+               usb_ep_enable(ecm->notify);
+               ecm->notify->driver_data = ecm;
+
+       /* Data interface has two altsettings, 0 and 1 */
+       } else if (intf == ecm->data_id) {
+               if (alt > 1)
+                       goto fail;
+
+               if (ecm->port.in_ep->driver_data) {
+                       DBG(cdev, "reset ecm\n");
+                       gether_disconnect(&ecm->port);
+               }
+
+               if (!ecm->port.in_ep->desc ||
+                   !ecm->port.out_ep->desc) {
+                       DBG(cdev, "init ecm\n");
+                       if (config_ep_by_speed(cdev->gadget, f,
+                                              ecm->port.in_ep) ||
+                           config_ep_by_speed(cdev->gadget, f,
+                                              ecm->port.out_ep)) {
+                               ecm->port.in_ep->desc = NULL;
+                               ecm->port.out_ep->desc = NULL;
+                               goto fail;
+                       }
+               }
+
+               /* CDC Ethernet only sends data in non-default altsettings.
+                * Changing altsettings resets filters, statistics, etc.
+                */
+               if (alt == 1) {
+                       struct net_device       *net;
+
+                       /* Enable zlps by default for ECM conformance;
+                        * override for musb_hdrc (avoids txdma ovhead).
+                        */
+                       ecm->port.is_zlp_ok = !(gadget_is_musbhdrc(cdev->gadget)
+                               );
+                       ecm->port.cdc_filter = DEFAULT_FILTER;
+                       DBG(cdev, "activate ecm\n");
+                       net = gether_connect(&ecm->port);
+                       if (IS_ERR(net))
+                               return PTR_ERR(net);
+               }
+
+               /* NOTE this can be a minor disagreement with the ECM spec,
+                * which says speed notifications will "always" follow
+                * connection notifications.  But we allow one connect to
+                * follow another (if the first is in flight), and instead
+                * just guarantee that a speed notification is always sent.
+                */
+               ecm_notify(ecm);
+       } else
+               goto fail;
+
+       return 0;
+fail:
+       return -EINVAL;
+}
+
+/* Because the data interface supports multiple altsettings,
+ * this ECM function *MUST* implement a get_alt() method.
+ */
+static int ecm_get_alt(struct usb_function *f, unsigned intf)
+{
+       struct f_ecm            *ecm = func_to_ecm(f);
+
+       if (intf == ecm->ctrl_id)
+               return 0;
+       return ecm->port.in_ep->driver_data ? 1 : 0;
+}
+
+static void ecm_disable(struct usb_function *f)
+{
+       struct f_ecm            *ecm = func_to_ecm(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+
+       DBG(cdev, "ecm deactivated\n");
+
+       if (ecm->port.in_ep->driver_data)
+               gether_disconnect(&ecm->port);
+
+       if (ecm->notify->driver_data) {
+               usb_ep_disable(ecm->notify);
+               ecm->notify->driver_data = NULL;
+               ecm->notify->desc = NULL;
+       }
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Callbacks let us notify the host about connect/disconnect when the
+ * net device is opened or closed.
+ *
+ * For testing, note that link states on this side include both opened
+ * and closed variants of:
+ *
+ *   - disconnected/unconfigured
+ *   - configured but inactive (data alt 0)
+ *   - configured and active (data alt 1)
+ *
+ * Each needs to be tested with unplug, rmmod, SET_CONFIGURATION, and
+ * SET_INTERFACE (altsetting).  Remember also that "configured" doesn't
+ * imply the host is actually polling the notification endpoint, and
+ * likewise that "active" doesn't imply it's actually using the data
+ * endpoints for traffic.
+ */
+
+static void ecm_open(struct gether *geth)
+{
+       struct f_ecm            *ecm = func_to_ecm(&geth->func);
+
+       DBG(ecm->port.func.config->cdev, "%s\n", __func__);
+
+       ecm->is_open = true;
+       ecm_notify(ecm);
+}
+
+static void ecm_close(struct gether *geth)
+{
+       struct f_ecm            *ecm = func_to_ecm(&geth->func);
+
+       DBG(ecm->port.func.config->cdev, "%s\n", __func__);
+
+       ecm->is_open = false;
+       ecm_notify(ecm);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* ethernet function driver setup/binding */
+
+static int
+ecm_bind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct usb_composite_dev *cdev = c->cdev;
+       struct f_ecm            *ecm = func_to_ecm(f);
+       struct usb_string       *us;
+       int                     status;
+       struct usb_ep           *ep;
+
+       struct f_ecm_opts       *ecm_opts;
+
+       if (!can_support_ecm(cdev->gadget))
+               return -EINVAL;
+
+       ecm_opts = container_of(f->fi, struct f_ecm_opts, func_inst);
+
+       /*
+        * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
+        * configurations are bound in sequence with list_for_each_entry,
+        * in each configuration its functions are bound in sequence
+        * with list_for_each_entry, so we assume no race condition
+        * with regard to ecm_opts->bound access
+        */
+       if (!ecm_opts->bound) {
+               mutex_lock(&ecm_opts->lock);
+               gether_set_gadget(ecm_opts->net, cdev->gadget);
+               status = gether_register_netdev(ecm_opts->net);
+               mutex_unlock(&ecm_opts->lock);
+               if (status)
+                       return status;
+               ecm_opts->bound = true;
+       }
+
+       us = usb_gstrings_attach(cdev, ecm_strings,
+                                ARRAY_SIZE(ecm_string_defs));
+       if (IS_ERR(us))
+               return PTR_ERR(us);
+       ecm_control_intf.iInterface = us[0].id;
+       ecm_data_intf.iInterface = us[2].id;
+       ecm_desc.iMACAddress = us[1].id;
+       ecm_iad_descriptor.iFunction = us[3].id;
+
+       /* allocate instance-specific interface IDs */
+       status = usb_interface_id(c, f);
+       if (status < 0)
+               goto fail;
+       ecm->ctrl_id = status;
+       ecm_iad_descriptor.bFirstInterface = status;
+
+       ecm_control_intf.bInterfaceNumber = status;
+       ecm_union_desc.bMasterInterface0 = status;
+
+       status = usb_interface_id(c, f);
+       if (status < 0)
+               goto fail;
+       ecm->data_id = status;
+
+       ecm_data_nop_intf.bInterfaceNumber = status;
+       ecm_data_intf.bInterfaceNumber = status;
+       ecm_union_desc.bSlaveInterface0 = status;
+
+       status = -ENODEV;
+
+       /* allocate instance-specific endpoints */
+       ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_in_desc);
+       if (!ep)
+               goto fail;
+       ecm->port.in_ep = ep;
+       ep->driver_data = cdev; /* claim */
+
+       ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_out_desc);
+       if (!ep)
+               goto fail;
+       ecm->port.out_ep = ep;
+       ep->driver_data = cdev; /* claim */
+
+       /* NOTE:  a status/notification endpoint is *OPTIONAL* but we
+        * don't treat it that way.  It's simpler, and some newer CDC
+        * profiles (wireless handsets) no longer treat it as optional.
+        */
+       ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_notify_desc);
+       if (!ep)
+               goto fail;
+       ecm->notify = ep;
+       ep->driver_data = cdev; /* claim */
+
+       status = -ENOMEM;
+
+       /* allocate notification request and buffer */
+       ecm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
+       if (!ecm->notify_req)
+               goto fail;
+       ecm->notify_req->buf = kmalloc(ECM_STATUS_BYTECOUNT, GFP_KERNEL);
+       if (!ecm->notify_req->buf)
+               goto fail;
+       ecm->notify_req->context = ecm;
+       ecm->notify_req->complete = ecm_notify_complete;
+
+       /* support all relevant hardware speeds... we expect that when
+        * hardware is dual speed, all bulk-capable endpoints work at
+        * both speeds
+        */
+       hs_ecm_in_desc.bEndpointAddress = fs_ecm_in_desc.bEndpointAddress;
+       hs_ecm_out_desc.bEndpointAddress = fs_ecm_out_desc.bEndpointAddress;
+       hs_ecm_notify_desc.bEndpointAddress =
+               fs_ecm_notify_desc.bEndpointAddress;
+
+       ss_ecm_in_desc.bEndpointAddress = fs_ecm_in_desc.bEndpointAddress;
+       ss_ecm_out_desc.bEndpointAddress = fs_ecm_out_desc.bEndpointAddress;
+       ss_ecm_notify_desc.bEndpointAddress =
+               fs_ecm_notify_desc.bEndpointAddress;
+
+       status = usb_assign_descriptors(f, ecm_fs_function, ecm_hs_function,
+                       ecm_ss_function);
+       if (status)
+               goto fail;
+
+       /* NOTE:  all that is done without knowing or caring about
+        * the network link ... which is unavailable to this code
+        * until we're activated via set_alt().
+        */
+
+       ecm->port.open = ecm_open;
+       ecm->port.close = ecm_close;
+
+       DBG(cdev, "CDC Ethernet: %s speed IN/%s OUT/%s NOTIFY/%s\n",
+                       gadget_is_superspeed(c->cdev->gadget) ? "super" :
+                       gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+                       ecm->port.in_ep->name, ecm->port.out_ep->name,
+                       ecm->notify->name);
+       return 0;
+
+fail:
+       if (ecm->notify_req) {
+               kfree(ecm->notify_req->buf);
+               usb_ep_free_request(ecm->notify, ecm->notify_req);
+       }
+
+       /* we might as well release our claims on endpoints */
+       if (ecm->notify)
+               ecm->notify->driver_data = NULL;
+       if (ecm->port.out_ep)
+               ecm->port.out_ep->driver_data = NULL;
+       if (ecm->port.in_ep)
+               ecm->port.in_ep->driver_data = NULL;
+
+       ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
+
+       return status;
+}
+
+static inline struct f_ecm_opts *to_f_ecm_opts(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct f_ecm_opts,
+                           func_inst.group);
+}
+
+/* f_ecm_item_ops */
+USB_ETHERNET_CONFIGFS_ITEM(ecm);
+
+/* f_ecm_opts_dev_addr */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ecm);
+
+/* f_ecm_opts_host_addr */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ecm);
+
+/* f_ecm_opts_qmult */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ecm);
+
+/* f_ecm_opts_ifname */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ecm);
+
+static struct configfs_attribute *ecm_attrs[] = {
+       &f_ecm_opts_dev_addr.attr,
+       &f_ecm_opts_host_addr.attr,
+       &f_ecm_opts_qmult.attr,
+       &f_ecm_opts_ifname.attr,
+       NULL,
+};
+
+static struct config_item_type ecm_func_type = {
+       .ct_item_ops    = &ecm_item_ops,
+       .ct_attrs       = ecm_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+static void ecm_free_inst(struct usb_function_instance *f)
+{
+       struct f_ecm_opts *opts;
+
+       opts = container_of(f, struct f_ecm_opts, func_inst);
+       if (opts->bound)
+               gether_cleanup(netdev_priv(opts->net));
+       else
+               free_netdev(opts->net);
+       kfree(opts);
+}
+
+static struct usb_function_instance *ecm_alloc_inst(void)
+{
+       struct f_ecm_opts *opts;
+
+       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+       if (!opts)
+               return ERR_PTR(-ENOMEM);
+       mutex_init(&opts->lock);
+       opts->func_inst.free_func_inst = ecm_free_inst;
+       opts->net = gether_setup_default();
+       if (IS_ERR(opts->net)) {
+               struct net_device *net = opts->net;
+               kfree(opts);
+               return ERR_CAST(net);
+       }
+
+       config_group_init_type_name(&opts->func_inst.group, "", &ecm_func_type);
+
+       return &opts->func_inst;
+}
+
+static void ecm_free(struct usb_function *f)
+{
+       struct f_ecm *ecm;
+       struct f_ecm_opts *opts;
+
+       ecm = func_to_ecm(f);
+       opts = container_of(f->fi, struct f_ecm_opts, func_inst);
+       kfree(ecm);
+       mutex_lock(&opts->lock);
+       opts->refcnt--;
+       mutex_unlock(&opts->lock);
+}
+
+static void ecm_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct f_ecm            *ecm = func_to_ecm(f);
+
+       DBG(c->cdev, "ecm unbind\n");
+
+       usb_free_all_descriptors(f);
+
+       kfree(ecm->notify_req->buf);
+       usb_ep_free_request(ecm->notify, ecm->notify_req);
+}
+
+static struct usb_function *ecm_alloc(struct usb_function_instance *fi)
+{
+       struct f_ecm    *ecm;
+       struct f_ecm_opts *opts;
+       int status;
+
+       /* allocate and initialize one new instance */
+       ecm = kzalloc(sizeof(*ecm), GFP_KERNEL);
+       if (!ecm)
+               return ERR_PTR(-ENOMEM);
+
+       opts = container_of(fi, struct f_ecm_opts, func_inst);
+       mutex_lock(&opts->lock);
+       opts->refcnt++;
+
+       /* export host's Ethernet address in CDC format */
+       status = gether_get_host_addr_cdc(opts->net, ecm->ethaddr,
+                                         sizeof(ecm->ethaddr));
+       if (status < 12) {
+               kfree(ecm);
+               mutex_unlock(&opts->lock);
+               return ERR_PTR(-EINVAL);
+       }
+       ecm_string_defs[1].s = ecm->ethaddr;
+
+       ecm->port.ioport = netdev_priv(opts->net);
+       mutex_unlock(&opts->lock);
+       ecm->port.cdc_filter = DEFAULT_FILTER;
+
+       ecm->port.func.name = "cdc_ethernet";
+       /* descriptors are per-instance copies */
+       ecm->port.func.bind = ecm_bind;
+       ecm->port.func.unbind = ecm_unbind;
+       ecm->port.func.set_alt = ecm_set_alt;
+       ecm->port.func.get_alt = ecm_get_alt;
+       ecm->port.func.setup = ecm_setup;
+       ecm->port.func.disable = ecm_disable;
+       ecm->port.func.free_func = ecm_free;
+
+       return &ecm->port.func;
+}
+
+DECLARE_USB_FUNCTION_INIT(ecm, ecm_alloc_inst, ecm_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/function/f_eem.c b/drivers/usb/gadget/function/f_eem.c
new file mode 100644 (file)
index 0000000..4d8b236
--- /dev/null
@@ -0,0 +1,660 @@
+/*
+ * f_eem.c -- USB CDC Ethernet (EEM) link function driver
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2008 Nokia Corporation
+ * Copyright (C) 2009 EF Johnson 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/etherdevice.h>
+#include <linux/crc32.h>
+#include <linux/slab.h>
+
+#include "u_ether.h"
+#include "u_ether_configfs.h"
+#include "u_eem.h"
+
+#define EEM_HLEN 2
+
+/*
+ * This function is a "CDC Ethernet Emulation Model" (CDC EEM)
+ * Ethernet link.
+ */
+
+struct f_eem {
+       struct gether                   port;
+       u8                              ctrl_id;
+};
+
+static inline struct f_eem *func_to_eem(struct usb_function *f)
+{
+       return container_of(f, struct f_eem, port.func);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* interface descriptor: */
+
+static struct usb_interface_descriptor eem_intf = {
+       .bLength =              sizeof eem_intf,
+       .bDescriptorType =      USB_DT_INTERFACE,
+
+       /* .bInterfaceNumber = DYNAMIC */
+       .bNumEndpoints =        2,
+       .bInterfaceClass =      USB_CLASS_COMM,
+       .bInterfaceSubClass =   USB_CDC_SUBCLASS_EEM,
+       .bInterfaceProtocol =   USB_CDC_PROTO_EEM,
+       /* .iInterface = DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor eem_fs_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor eem_fs_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *eem_fs_function[] = {
+       /* CDC EEM control descriptors */
+       (struct usb_descriptor_header *) &eem_intf,
+       (struct usb_descriptor_header *) &eem_fs_in_desc,
+       (struct usb_descriptor_header *) &eem_fs_out_desc,
+       NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor eem_hs_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor eem_hs_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *eem_hs_function[] = {
+       /* CDC EEM control descriptors */
+       (struct usb_descriptor_header *) &eem_intf,
+       (struct usb_descriptor_header *) &eem_hs_in_desc,
+       (struct usb_descriptor_header *) &eem_hs_out_desc,
+       NULL,
+};
+
+/* super speed support: */
+
+static struct usb_endpoint_descriptor eem_ss_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor eem_ss_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor eem_ss_bulk_comp_desc = {
+       .bLength =              sizeof eem_ss_bulk_comp_desc,
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+
+       /* the following 2 values can be tweaked if necessary */
+       /* .bMaxBurst =         0, */
+       /* .bmAttributes =      0, */
+};
+
+static struct usb_descriptor_header *eem_ss_function[] = {
+       /* CDC EEM control descriptors */
+       (struct usb_descriptor_header *) &eem_intf,
+       (struct usb_descriptor_header *) &eem_ss_in_desc,
+       (struct usb_descriptor_header *) &eem_ss_bulk_comp_desc,
+       (struct usb_descriptor_header *) &eem_ss_out_desc,
+       (struct usb_descriptor_header *) &eem_ss_bulk_comp_desc,
+       NULL,
+};
+
+/* string descriptors: */
+
+static struct usb_string eem_string_defs[] = {
+       [0].s = "CDC Ethernet Emulation Model (EEM)",
+       {  } /* end of list */
+};
+
+static struct usb_gadget_strings eem_string_table = {
+       .language =             0x0409, /* en-us */
+       .strings =              eem_string_defs,
+};
+
+static struct usb_gadget_strings *eem_strings[] = {
+       &eem_string_table,
+       NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int eem_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+       struct usb_composite_dev *cdev = f->config->cdev;
+       int                     value = -EOPNOTSUPP;
+       u16                     w_index = le16_to_cpu(ctrl->wIndex);
+       u16                     w_value = le16_to_cpu(ctrl->wValue);
+       u16                     w_length = le16_to_cpu(ctrl->wLength);
+
+       DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+               ctrl->bRequestType, ctrl->bRequest,
+               w_value, w_index, w_length);
+
+       /* device either stalls (value < 0) or reports success */
+       return value;
+}
+
+
+static int eem_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+       struct f_eem            *eem = func_to_eem(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+       struct net_device       *net;
+
+       /* we know alt == 0, so this is an activation or a reset */
+       if (alt != 0)
+               goto fail;
+
+       if (intf == eem->ctrl_id) {
+
+               if (eem->port.in_ep->driver_data) {
+                       DBG(cdev, "reset eem\n");
+                       gether_disconnect(&eem->port);
+               }
+
+               if (!eem->port.in_ep->desc || !eem->port.out_ep->desc) {
+                       DBG(cdev, "init eem\n");
+                       if (config_ep_by_speed(cdev->gadget, f,
+                                              eem->port.in_ep) ||
+                           config_ep_by_speed(cdev->gadget, f,
+                                              eem->port.out_ep)) {
+                               eem->port.in_ep->desc = NULL;
+                               eem->port.out_ep->desc = NULL;
+                               goto fail;
+                       }
+               }
+
+               /* zlps should not occur because zero-length EEM packets
+                * will be inserted in those cases where they would occur
+                */
+               eem->port.is_zlp_ok = 1;
+               eem->port.cdc_filter = DEFAULT_FILTER;
+               DBG(cdev, "activate eem\n");
+               net = gether_connect(&eem->port);
+               if (IS_ERR(net))
+                       return PTR_ERR(net);
+       } else
+               goto fail;
+
+       return 0;
+fail:
+       return -EINVAL;
+}
+
+static void eem_disable(struct usb_function *f)
+{
+       struct f_eem            *eem = func_to_eem(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+
+       DBG(cdev, "eem deactivated\n");
+
+       if (eem->port.in_ep->driver_data)
+               gether_disconnect(&eem->port);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* EEM function driver setup/binding */
+
+static int eem_bind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct usb_composite_dev *cdev = c->cdev;
+       struct f_eem            *eem = func_to_eem(f);
+       struct usb_string       *us;
+       int                     status;
+       struct usb_ep           *ep;
+
+       struct f_eem_opts       *eem_opts;
+
+       eem_opts = container_of(f->fi, struct f_eem_opts, func_inst);
+       /*
+        * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
+        * configurations are bound in sequence with list_for_each_entry,
+        * in each configuration its functions are bound in sequence
+        * with list_for_each_entry, so we assume no race condition
+        * with regard to eem_opts->bound access
+        */
+       if (!eem_opts->bound) {
+               mutex_lock(&eem_opts->lock);
+               gether_set_gadget(eem_opts->net, cdev->gadget);
+               status = gether_register_netdev(eem_opts->net);
+               mutex_unlock(&eem_opts->lock);
+               if (status)
+                       return status;
+               eem_opts->bound = true;
+       }
+
+       us = usb_gstrings_attach(cdev, eem_strings,
+                                ARRAY_SIZE(eem_string_defs));
+       if (IS_ERR(us))
+               return PTR_ERR(us);
+       eem_intf.iInterface = us[0].id;
+
+       /* allocate instance-specific interface IDs */
+       status = usb_interface_id(c, f);
+       if (status < 0)
+               goto fail;
+       eem->ctrl_id = status;
+       eem_intf.bInterfaceNumber = status;
+
+       status = -ENODEV;
+
+       /* allocate instance-specific endpoints */
+       ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_in_desc);
+       if (!ep)
+               goto fail;
+       eem->port.in_ep = ep;
+       ep->driver_data = cdev; /* claim */
+
+       ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_out_desc);
+       if (!ep)
+               goto fail;
+       eem->port.out_ep = ep;
+       ep->driver_data = cdev; /* claim */
+
+       status = -ENOMEM;
+
+       /* support all relevant hardware speeds... we expect that when
+        * hardware is dual speed, all bulk-capable endpoints work at
+        * both speeds
+        */
+       eem_hs_in_desc.bEndpointAddress = eem_fs_in_desc.bEndpointAddress;
+       eem_hs_out_desc.bEndpointAddress = eem_fs_out_desc.bEndpointAddress;
+
+       eem_ss_in_desc.bEndpointAddress = eem_fs_in_desc.bEndpointAddress;
+       eem_ss_out_desc.bEndpointAddress = eem_fs_out_desc.bEndpointAddress;
+
+       status = usb_assign_descriptors(f, eem_fs_function, eem_hs_function,
+                       eem_ss_function);
+       if (status)
+               goto fail;
+
+       DBG(cdev, "CDC Ethernet (EEM): %s speed IN/%s OUT/%s\n",
+                       gadget_is_superspeed(c->cdev->gadget) ? "super" :
+                       gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+                       eem->port.in_ep->name, eem->port.out_ep->name);
+       return 0;
+
+fail:
+       usb_free_all_descriptors(f);
+       if (eem->port.out_ep)
+               eem->port.out_ep->driver_data = NULL;
+       if (eem->port.in_ep)
+               eem->port.in_ep->driver_data = NULL;
+
+       ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
+
+       return status;
+}
+
+static void eem_cmd_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct sk_buff *skb = (struct sk_buff *)req->context;
+
+       dev_kfree_skb_any(skb);
+}
+
+/*
+ * Add the EEM header and ethernet checksum.
+ * We currently do not attempt to put multiple ethernet frames
+ * into a single USB transfer
+ */
+static struct sk_buff *eem_wrap(struct gether *port, struct sk_buff *skb)
+{
+       struct sk_buff  *skb2 = NULL;
+       struct usb_ep   *in = port->in_ep;
+       int             padlen = 0;
+       u16             len = skb->len;
+
+       int headroom = skb_headroom(skb);
+       int tailroom = skb_tailroom(skb);
+
+       /* When (len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) is 0,
+        * stick two bytes of zero-length EEM packet on the end.
+        */
+       if (((len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) == 0)
+               padlen += 2;
+
+       if ((tailroom >= (ETH_FCS_LEN + padlen)) &&
+                       (headroom >= EEM_HLEN) && !skb_cloned(skb))
+               goto done;
+
+       skb2 = skb_copy_expand(skb, EEM_HLEN, ETH_FCS_LEN + padlen, GFP_ATOMIC);
+       dev_kfree_skb_any(skb);
+       skb = skb2;
+       if (!skb)
+               return skb;
+
+done:
+       /* use the "no CRC" option */
+       put_unaligned_be32(0xdeadbeef, skb_put(skb, 4));
+
+       /* EEM packet header format:
+        * b0..13:      length of ethernet frame
+        * b14:         bmCRC (0 == sentinel CRC)
+        * b15:         bmType (0 == data)
+        */
+       len = skb->len;
+       put_unaligned_le16(len & 0x3FFF, skb_push(skb, 2));
+
+       /* add a zero-length EEM packet, if needed */
+       if (padlen)
+               put_unaligned_le16(0, skb_put(skb, 2));
+
+       return skb;
+}
+
+/*
+ * Remove the EEM header.  Note that there can be many EEM packets in a single
+ * USB transfer, so we need to break them out and handle them independently.
+ */
+static int eem_unwrap(struct gether *port,
+                       struct sk_buff *skb,
+                       struct sk_buff_head *list)
+{
+       struct usb_composite_dev        *cdev = port->func.config->cdev;
+       int                             status = 0;
+
+       do {
+               struct sk_buff  *skb2;
+               u16             header;
+               u16             len = 0;
+
+               if (skb->len < EEM_HLEN) {
+                       status = -EINVAL;
+                       DBG(cdev, "invalid EEM header\n");
+                       goto error;
+               }
+
+               /* remove the EEM header */
+               header = get_unaligned_le16(skb->data);
+               skb_pull(skb, EEM_HLEN);
+
+               /* EEM packet header format:
+                * b0..14:      EEM type dependent (data or command)
+                * b15:         bmType (0 == data, 1 == command)
+                */
+               if (header & BIT(15)) {
+                       struct usb_request      *req = cdev->req;
+                       u16                     bmEEMCmd;
+
+                       /* EEM command packet format:
+                        * b0..10:      bmEEMCmdParam
+                        * b11..13:     bmEEMCmd
+                        * b14:         reserved (must be zero)
+                        * b15:         bmType (1 == command)
+                        */
+                       if (header & BIT(14))
+                               continue;
+
+                       bmEEMCmd = (header >> 11) & 0x7;
+                       switch (bmEEMCmd) {
+                       case 0: /* echo */
+                               len = header & 0x7FF;
+                               if (skb->len < len) {
+                                       status = -EOVERFLOW;
+                                       goto error;
+                               }
+
+                               skb2 = skb_clone(skb, GFP_ATOMIC);
+                               if (unlikely(!skb2)) {
+                                       DBG(cdev, "EEM echo response error\n");
+                                       goto next;
+                               }
+                               skb_trim(skb2, len);
+                               put_unaligned_le16(BIT(15) | BIT(11) | len,
+                                                       skb_push(skb2, 2));
+                               skb_copy_bits(skb2, 0, req->buf, skb2->len);
+                               req->length = skb2->len;
+                               req->complete = eem_cmd_complete;
+                               req->zero = 1;
+                               req->context = skb2;
+                               if (usb_ep_queue(port->in_ep, req, GFP_ATOMIC))
+                                       DBG(cdev, "echo response queue fail\n");
+                               break;
+
+                       case 1:  /* echo response */
+                       case 2:  /* suspend hint */
+                       case 3:  /* response hint */
+                       case 4:  /* response complete hint */
+                       case 5:  /* tickle */
+                       default: /* reserved */
+                               continue;
+                       }
+               } else {
+                       u32             crc, crc2;
+                       struct sk_buff  *skb3;
+
+                       /* check for zero-length EEM packet */
+                       if (header == 0)
+                               continue;
+
+                       /* EEM data packet format:
+                        * b0..13:      length of ethernet frame
+                        * b14:         bmCRC (0 == sentinel, 1 == calculated)
+                        * b15:         bmType (0 == data)
+                        */
+                       len = header & 0x3FFF;
+                       if ((skb->len < len)
+                                       || (len < (ETH_HLEN + ETH_FCS_LEN))) {
+                               status = -EINVAL;
+                               goto error;
+                       }
+
+                       /* validate CRC */
+                       if (header & BIT(14)) {
+                               crc = get_unaligned_le32(skb->data + len
+                                                       - ETH_FCS_LEN);
+                               crc2 = ~crc32_le(~0,
+                                               skb->data, len - ETH_FCS_LEN);
+                       } else {
+                               crc = get_unaligned_be32(skb->data + len
+                                                       - ETH_FCS_LEN);
+                               crc2 = 0xdeadbeef;
+                       }
+                       if (crc != crc2) {
+                               DBG(cdev, "invalid EEM CRC\n");
+                               goto next;
+                       }
+
+                       skb2 = skb_clone(skb, GFP_ATOMIC);
+                       if (unlikely(!skb2)) {
+                               DBG(cdev, "unable to unframe EEM packet\n");
+                               continue;
+                       }
+                       skb_trim(skb2, len - ETH_FCS_LEN);
+
+                       skb3 = skb_copy_expand(skb2,
+                                               NET_IP_ALIGN,
+                                               0,
+                                               GFP_ATOMIC);
+                       if (unlikely(!skb3)) {
+                               DBG(cdev, "unable to realign EEM packet\n");
+                               dev_kfree_skb_any(skb2);
+                               continue;
+                       }
+                       dev_kfree_skb_any(skb2);
+                       skb_queue_tail(list, skb3);
+               }
+next:
+               skb_pull(skb, len);
+       } while (skb->len);
+
+error:
+       dev_kfree_skb_any(skb);
+       return status;
+}
+
+static inline struct f_eem_opts *to_f_eem_opts(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct f_eem_opts,
+                           func_inst.group);
+}
+
+/* f_eem_item_ops */
+USB_ETHERNET_CONFIGFS_ITEM(eem);
+
+/* f_eem_opts_dev_addr */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(eem);
+
+/* f_eem_opts_host_addr */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(eem);
+
+/* f_eem_opts_qmult */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(eem);
+
+/* f_eem_opts_ifname */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(eem);
+
+static struct configfs_attribute *eem_attrs[] = {
+       &f_eem_opts_dev_addr.attr,
+       &f_eem_opts_host_addr.attr,
+       &f_eem_opts_qmult.attr,
+       &f_eem_opts_ifname.attr,
+       NULL,
+};
+
+static struct config_item_type eem_func_type = {
+       .ct_item_ops    = &eem_item_ops,
+       .ct_attrs       = eem_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+static void eem_free_inst(struct usb_function_instance *f)
+{
+       struct f_eem_opts *opts;
+
+       opts = container_of(f, struct f_eem_opts, func_inst);
+       if (opts->bound)
+               gether_cleanup(netdev_priv(opts->net));
+       else
+               free_netdev(opts->net);
+       kfree(opts);
+}
+
+static struct usb_function_instance *eem_alloc_inst(void)
+{
+       struct f_eem_opts *opts;
+
+       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+       if (!opts)
+               return ERR_PTR(-ENOMEM);
+       mutex_init(&opts->lock);
+       opts->func_inst.free_func_inst = eem_free_inst;
+       opts->net = gether_setup_default();
+       if (IS_ERR(opts->net)) {
+               struct net_device *net = opts->net;
+               kfree(opts);
+               return ERR_CAST(net);
+       }
+
+       config_group_init_type_name(&opts->func_inst.group, "", &eem_func_type);
+
+       return &opts->func_inst;
+}
+
+static void eem_free(struct usb_function *f)
+{
+       struct f_eem *eem;
+       struct f_eem_opts *opts;
+
+       eem = func_to_eem(f);
+       opts = container_of(f->fi, struct f_eem_opts, func_inst);
+       kfree(eem);
+       mutex_lock(&opts->lock);
+       opts->refcnt--;
+       mutex_unlock(&opts->lock);
+}
+
+static void eem_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+       DBG(c->cdev, "eem unbind\n");
+
+       usb_free_all_descriptors(f);
+}
+
+static struct usb_function *eem_alloc(struct usb_function_instance *fi)
+{
+       struct f_eem    *eem;
+       struct f_eem_opts *opts;
+
+       /* allocate and initialize one new instance */
+       eem = kzalloc(sizeof(*eem), GFP_KERNEL);
+       if (!eem)
+               return ERR_PTR(-ENOMEM);
+
+       opts = container_of(fi, struct f_eem_opts, func_inst);
+       mutex_lock(&opts->lock);
+       opts->refcnt++;
+
+       eem->port.ioport = netdev_priv(opts->net);
+       mutex_unlock(&opts->lock);
+       eem->port.cdc_filter = DEFAULT_FILTER;
+
+       eem->port.func.name = "cdc_eem";
+       /* descriptors are per-instance copies */
+       eem->port.func.bind = eem_bind;
+       eem->port.func.unbind = eem_unbind;
+       eem->port.func.set_alt = eem_set_alt;
+       eem->port.func.setup = eem_setup;
+       eem->port.func.disable = eem_disable;
+       eem->port.func.free_func = eem_free;
+       eem->port.wrap = eem_wrap;
+       eem->port.unwrap = eem_unwrap;
+       eem->port.header_len = EEM_HLEN;
+
+       return &eem->port.func;
+}
+
+DECLARE_USB_FUNCTION_INIT(eem, eem_alloc_inst, eem_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
new file mode 100644 (file)
index 0000000..fe45060
--- /dev/null
@@ -0,0 +1,3347 @@
+/*
+ * f_fs.c -- user mode file system API for USB composite function controllers
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ * Author: Michal Nazarewicz <mina86@mina86.com>
+ *
+ * Based on inode.c (GadgetFS) which was:
+ * 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.
+ */
+
+
+/* #define DEBUG */
+/* #define VERBOSE_DEBUG */
+
+#include <linux/blkdev.h>
+#include <linux/pagemap.h>
+#include <linux/export.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <asm/unaligned.h>
+
+#include <linux/usb/composite.h>
+#include <linux/usb/functionfs.h>
+
+#include <linux/aio.h>
+#include <linux/mmu_context.h>
+#include <linux/poll.h>
+
+#include "u_fs.h"
+#include "u_f.h"
+#include "u_os_desc.h"
+#include "configfs.h"
+
+#define FUNCTIONFS_MAGIC       0xa647361 /* Chosen by a honest dice roll ;) */
+
+/* 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 ownership 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 inline enum ffs_setup_state
+ffs_setup_state_clear_cancelled(struct ffs_data *ffs)
+{
+       return (enum ffs_setup_state)
+               cmpxchg(&ffs->setup_state, FFS_SETUP_CANCELLED, FFS_NO_SETUP);
+}
+
+
+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 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, [2]: super speed */
+       struct usb_endpoint_descriptor  *descs[3];
+
+       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;
+};
+
+/*  ffs_io_data structure ***************************************************/
+
+struct ffs_io_data {
+       bool aio;
+       bool read;
+
+       struct kiocb *kiocb;
+       const struct iovec *iovec;
+       unsigned long nr_segs;
+       char __user *buf;
+       size_t len;
+
+       struct mm_struct *mm;
+       struct work_struct work;
+
+       struct usb_ep *ep;
+       struct usb_request *req;
+};
+
+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);
+
+/* Devices management *******************************************************/
+
+DEFINE_MUTEX(ffs_lock);
+EXPORT_SYMBOL_GPL(ffs_lock);
+
+static struct ffs_dev *_ffs_find_dev(const char *name);
+static struct ffs_dev *_ffs_alloc_dev(void);
+static int _ffs_name_dev(struct ffs_dev *dev, const char *name);
+static void _ffs_free_dev(struct ffs_dev *dev);
+static void *ffs_acquire_dev(const char *dev_name);
+static void ffs_release_dev(struct ffs_data *ffs_data);
+static int ffs_ready(struct ffs_data *ffs);
+static void ffs_closed(struct ffs_data *ffs);
+
+/* Misc helper functions ****************************************************/
+
+static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock)
+       __attribute__((warn_unused_result, nonnull));
+static char *ffs_prepare_buffer(const char __user *buf, size_t len)
+       __attribute__((warn_unused_result, nonnull));
+
+
+/* Control file aka ep0 *****************************************************/
+
+static void ffs_ep0_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct ffs_data *ffs = req->context;
+
+       complete_all(&ffs->ep0req_completion);
+}
+
+static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
+{
+       struct usb_request *req = ffs->ep0req;
+       int ret;
+
+       req->zero     = len < le16_to_cpu(ffs->ev.setup.wLength);
+
+       spin_unlock_irq(&ffs->ev.waitq.lock);
+
+       req->buf      = data;
+       req->length   = len;
+
+       /*
+        * UDC layer requires to provide a buffer even for ZLP, but should
+        * not use it at all. Let's provide some poisoned pointer to catch
+        * possible bug in the driver.
+        */
+       if (req->buf == NULL)
+               req->buf = (void *)0xDEADBABE;
+
+       reinit_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 req->status ? req->status : req->actual;
+}
+
+static int __ffs_ep0_stall(struct ffs_data *ffs)
+{
+       if (ffs->ev.can_stall) {
+               pr_vdebug("ep0 stall\n");
+               usb_ep_set_halt(ffs->gadget->ep0);
+               ffs->setup_state = FFS_NO_SETUP;
+               return -EL2HLT;
+       } else {
+               pr_debug("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_clear_cancelled(ffs) == FFS_SETUP_CANCELLED)
+               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 (IS_ERR(data)) {
+                       ret = PTR_ERR(data);
+                       break;
+               }
+
+               /* Handle data */
+               if (ffs->state == FFS_READ_DESCRIPTORS) {
+                       pr_info("read descriptors\n");
+                       ret = __ffs_data_got_descs(ffs, data, len);
+                       if (unlikely(ret < 0))
+                               break;
+
+                       ffs->state = FFS_READ_STRINGS;
+                       ret = len;
+               } else {
+                       pr_info("read strings\n");
+                       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 = ffs_ready(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_clear_cancelled(ffs)) {
+               case FFS_SETUP_CANCELLED:
+                       ret = -EIDRM;
+                       goto done_spin;
+
+               case FFS_NO_SETUP:
+                       ret = -ESRCH;
+                       goto done_spin;
+
+               case FFS_SETUP_PENDING:
+                       break;
+               }
+
+               /* FFS_SETUP_PENDING */
+               if (!(ffs->ev.setup.bRequestType & USB_DIR_IN)) {
+                       spin_unlock_irq(&ffs->ev.waitq.lock);
+                       ret = __ffs_ep0_stall(ffs);
+                       break;
+               }
+
+               /* FFS_SETUP_PENDING and not stall */
+               len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength));
+
+               spin_unlock_irq(&ffs->ev.waitq.lock);
+
+               data = ffs_prepare_buffer(buf, len);
+               if (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_CANCELLED 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_clear_cancelled(ffs) ==
+                   FFS_SETUP_CANCELLED) {
+                       ret = -EIDRM;
+done_spin:
+                       spin_unlock_irq(&ffs->ev.waitq.lock);
+               } else {
+                       /* unlocks spinlock */
+                       ret = __ffs_ep0_queue_wait(ffs, data, len);
+               }
+               kfree(data);
+               break;
+
+       default:
+               ret = -EBADFD;
+               break;
+       }
+
+       mutex_unlock(&ffs->mutex);
+       return ret;
+}
+
+static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
+                                    size_t n)
+{
+       /*
+        * We are holding ffs->ev.waitq.lock and ffs->mutex and we need
+        * to release them.
+        */
+       struct usb_functionfs_event events[n];
+       unsigned i = 0;
+
+       memset(events, 0, sizeof events);
+
+       do {
+               events[i].type = ffs->ev.types[i];
+               if (events[i].type == FUNCTIONFS_SETUP) {
+                       events[i].u.setup = ffs->ev.setup;
+                       ffs->setup_state = FFS_SETUP_PENDING;
+               }
+       } while (++i < n);
+
+       if (n < ffs->ev.count) {
+               ffs->ev.count -= n;
+               memmove(ffs->ev.types, ffs->ev.types + n,
+                       ffs->ev.count * sizeof *ffs->ev.types);
+       } else {
+               ffs->ev.count = 0;
+       }
+
+       spin_unlock_irq(&ffs->ev.waitq.lock);
+       mutex_unlock(&ffs->mutex);
+
+       return unlikely(__copy_to_user(buf, events, sizeof events))
+               ? -EFAULT : sizeof events;
+}
+
+static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
+                           size_t len, loff_t *ptr)
+{
+       struct ffs_data *ffs = file->private_data;
+       char *data = NULL;
+       size_t n;
+       int ret;
+
+       ENTER();
+
+       /* Fast check if setup was canceled */
+       if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED)
+               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_clear_cancelled(ffs)) {
+       case FFS_SETUP_CANCELLED:
+               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 (wait_event_interruptible_exclusive_locked_irq(ffs->ev.waitq,
+                                                       ffs->ev.count)) {
+                       ret = -EINTR;
+                       break;
+               }
+
+               return __ffs_ep0_read_events(ffs, buf,
+                                            min(n, (size_t)ffs->ev.count));
+
+       case FFS_SETUP_PENDING:
+               if (ffs->ev.setup.bRequestType & USB_DIR_IN) {
+                       spin_unlock_irq(&ffs->ev.waitq.lock);
+                       ret = __ffs_ep0_stall(ffs);
+                       goto done_mutex;
+               }
+
+               len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength));
+
+               spin_unlock_irq(&ffs->ev.waitq.lock);
+
+               if (likely(len)) {
+                       data = kmalloc(len, GFP_KERNEL);
+                       if (unlikely(!data)) {
+                               ret = -ENOMEM;
+                               goto done_mutex;
+                       }
+               }
+
+               spin_lock_irq(&ffs->ev.waitq.lock);
+
+               /* See ffs_ep0_write() */
+               if (ffs_setup_state_clear_cancelled(ffs) ==
+                   FFS_SETUP_CANCELLED) {
+                       ret = -EIDRM;
+                       break;
+               }
+
+               /* unlocks spinlock */
+               ret = __ffs_ep0_queue_wait(ffs, data, len);
+               if (likely(ret > 0) && unlikely(__copy_to_user(buf, data, len)))
+                       ret = -EFAULT;
+               goto done_mutex;
+
+       default:
+               ret = -EBADFD;
+               break;
+       }
+
+       spin_unlock_irq(&ffs->ev.waitq.lock);
+done_mutex:
+       mutex_unlock(&ffs->mutex);
+       kfree(data);
+       return ret;
+}
+
+static int ffs_ep0_open(struct inode *inode, struct file *file)
+{
+       struct ffs_data *ffs = inode->i_private;
+
+       ENTER();
+
+       if (unlikely(ffs->state == FFS_CLOSING))
+               return -EBUSY;
+
+       file->private_data = ffs;
+       ffs_data_opened(ffs);
+
+       return 0;
+}
+
+static int ffs_ep0_release(struct inode *inode, struct file *file)
+{
+       struct ffs_data *ffs = file->private_data;
+
+       ENTER();
+
+       ffs_data_closed(ffs);
+
+       return 0;
+}
+
+static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value)
+{
+       struct ffs_data *ffs = file->private_data;
+       struct usb_gadget *gadget = ffs->gadget;
+       long ret;
+
+       ENTER();
+
+       if (code == FUNCTIONFS_INTERFACE_REVMAP) {
+               struct ffs_function *func = ffs->func;
+               ret = func ? ffs_func_revmap_intf(func, value) : -ENODEV;
+       } else if (gadget && gadget->ops->ioctl) {
+               ret = gadget->ops->ioctl(gadget, code, value);
+       } else {
+               ret = -ENOTTY;
+       }
+
+       return ret;
+}
+
+static unsigned int ffs_ep0_poll(struct file *file, poll_table *wait)
+{
+       struct ffs_data *ffs = file->private_data;
+       unsigned int mask = POLLWRNORM;
+       int ret;
+
+       poll_wait(file, &ffs->ev.waitq, wait);
+
+       ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
+       if (unlikely(ret < 0))
+               return mask;
+
+       switch (ffs->state) {
+       case FFS_READ_DESCRIPTORS:
+       case FFS_READ_STRINGS:
+               mask |= POLLOUT;
+               break;
+
+       case FFS_ACTIVE:
+               switch (ffs->setup_state) {
+               case FFS_NO_SETUP:
+                       if (ffs->ev.count)
+                               mask |= POLLIN;
+                       break;
+
+               case FFS_SETUP_PENDING:
+               case FFS_SETUP_CANCELLED:
+                       mask |= (POLLIN | POLLOUT);
+                       break;
+               }
+       case FFS_CLOSING:
+               break;
+       }
+
+       mutex_unlock(&ffs->mutex);
+
+       return mask;
+}
+
+static const struct file_operations ffs_ep0_operations = {
+       .llseek =       no_llseek,
+
+       .open =         ffs_ep0_open,
+       .write =        ffs_ep0_write,
+       .read =         ffs_ep0_read,
+       .release =      ffs_ep0_release,
+       .unlocked_ioctl =       ffs_ep0_ioctl,
+       .poll =         ffs_ep0_poll,
+};
+
+
+/* "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 void ffs_user_copy_worker(struct work_struct *work)
+{
+       struct ffs_io_data *io_data = container_of(work, struct ffs_io_data,
+                                                  work);
+       int ret = io_data->req->status ? io_data->req->status :
+                                        io_data->req->actual;
+
+       if (io_data->read && ret > 0) {
+               int i;
+               size_t pos = 0;
+               use_mm(io_data->mm);
+               for (i = 0; i < io_data->nr_segs; i++) {
+                       if (unlikely(copy_to_user(io_data->iovec[i].iov_base,
+                                                &io_data->buf[pos],
+                                                io_data->iovec[i].iov_len))) {
+                               ret = -EFAULT;
+                               break;
+                       }
+                       pos += io_data->iovec[i].iov_len;
+               }
+               unuse_mm(io_data->mm);
+       }
+
+       aio_complete(io_data->kiocb, ret, ret);
+
+       usb_ep_free_request(io_data->ep, io_data->req);
+
+       io_data->kiocb->private = NULL;
+       if (io_data->read)
+               kfree(io_data->iovec);
+       kfree(io_data->buf);
+       kfree(io_data);
+}
+
+static void ffs_epfile_async_io_complete(struct usb_ep *_ep,
+                                        struct usb_request *req)
+{
+       struct ffs_io_data *io_data = req->context;
+
+       ENTER();
+
+       INIT_WORK(&io_data->work, ffs_user_copy_worker);
+       schedule_work(&io_data->work);
+}
+
+static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
+{
+       struct ffs_epfile *epfile = file->private_data;
+       struct ffs_ep *ep;
+       char *data = NULL;
+       ssize_t ret, data_len;
+       int halt;
+
+       /* 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;
+               }
+
+               ret = wait_event_interruptible(epfile->wait, (ep = epfile->ep));
+               if (ret) {
+                       ret = -EINTR;
+                       goto error;
+               }
+       }
+
+       /* Do we halt? */
+       halt = (!io_data->read == !epfile->in);
+       if (halt && epfile->isoc) {
+               ret = -EINVAL;
+               goto error;
+       }
+
+       /* Allocate & copy */
+       if (!halt) {
+               /*
+                * if we _do_ wait above, the epfile->ffs->gadget might be NULL
+                * before the waiting completes, so do not assign to 'gadget' earlier
+                */
+               struct usb_gadget *gadget = epfile->ffs->gadget;
+
+               spin_lock_irq(&epfile->ffs->eps_lock);
+               /* In the meantime, endpoint got disabled or changed. */
+               if (epfile->ep != ep) {
+                       spin_unlock_irq(&epfile->ffs->eps_lock);
+                       return -ESHUTDOWN;
+               }
+               /*
+                * Controller may require buffer size to be aligned to
+                * maxpacketsize of an out endpoint.
+                */
+               data_len = io_data->read ?
+                          usb_ep_align_maybe(gadget, ep->ep, io_data->len) :
+                          io_data->len;
+               spin_unlock_irq(&epfile->ffs->eps_lock);
+
+               data = kmalloc(data_len, GFP_KERNEL);
+               if (unlikely(!data))
+                       return -ENOMEM;
+               if (io_data->aio && !io_data->read) {
+                       int i;
+                       size_t pos = 0;
+                       for (i = 0; i < io_data->nr_segs; i++) {
+                               if (unlikely(copy_from_user(&data[pos],
+                                            io_data->iovec[i].iov_base,
+                                            io_data->iovec[i].iov_len))) {
+                                       ret = -EFAULT;
+                                       goto error;
+                               }
+                               pos += io_data->iovec[i].iov_len;
+                       }
+               } else {
+                       if (!io_data->read &&
+                           unlikely(__copy_from_user(data, io_data->buf,
+                                                     io_data->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;
+
+       spin_lock_irq(&epfile->ffs->eps_lock);
+
+       if (epfile->ep != ep) {
+               /* In the meantime, endpoint got disabled or changed. */
+               ret = -ESHUTDOWN;
+               spin_unlock_irq(&epfile->ffs->eps_lock);
+       } else if (halt) {
+               /* 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 */
+               struct usb_request *req;
+
+               if (io_data->aio) {
+                       req = usb_ep_alloc_request(ep->ep, GFP_KERNEL);
+                       if (unlikely(!req))
+                               goto error_lock;
+
+                       req->buf      = data;
+                       req->length   = io_data->len;
+
+                       io_data->buf = data;
+                       io_data->ep = ep->ep;
+                       io_data->req = req;
+
+                       req->context  = io_data;
+                       req->complete = ffs_epfile_async_io_complete;
+
+                       ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
+                       if (unlikely(ret)) {
+                               usb_ep_free_request(ep->ep, req);
+                               goto error_lock;
+                       }
+                       ret = -EIOCBQUEUED;
+
+                       spin_unlock_irq(&epfile->ffs->eps_lock);
+               } else {
+                       DECLARE_COMPLETION_ONSTACK(done);
+
+                       req = ep->req;
+                       req->buf      = data;
+                       req->length   = io_data->len;
+
+                       req->context  = &done;
+                       req->complete = ffs_epfile_io_complete;
+
+                       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 {
+                               /*
+                                * XXX We may end up silently droping data
+                                * here.  Since data_len (i.e. req->length) may
+                                * be bigger than len (after being rounded up
+                                * to maxpacketsize), we may end up with more
+                                * data then user space has space for.
+                                */
+                               ret = ep->status;
+                               if (io_data->read && ret > 0) {
+                                       ret = min_t(size_t, ret, io_data->len);
+
+                                       if (unlikely(copy_to_user(io_data->buf,
+                                               data, ret)))
+                                               ret = -EFAULT;
+                               }
+                       }
+                       kfree(data);
+               }
+       }
+
+       mutex_unlock(&epfile->mutex);
+       return ret;
+
+error_lock:
+       spin_unlock_irq(&epfile->ffs->eps_lock);
+       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)
+{
+       struct ffs_io_data io_data;
+
+       ENTER();
+
+       io_data.aio = false;
+       io_data.read = false;
+       io_data.buf = (char * __user)buf;
+       io_data.len = len;
+
+       return ffs_epfile_io(file, &io_data);
+}
+
+static ssize_t
+ffs_epfile_read(struct file *file, char __user *buf, size_t len, loff_t *ptr)
+{
+       struct ffs_io_data io_data;
+
+       ENTER();
+
+       io_data.aio = false;
+       io_data.read = true;
+       io_data.buf = buf;
+       io_data.len = len;
+
+       return ffs_epfile_io(file, &io_data);
+}
+
+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_aio_cancel(struct kiocb *kiocb)
+{
+       struct ffs_io_data *io_data = kiocb->private;
+       struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
+       int value;
+
+       ENTER();
+
+       spin_lock_irq(&epfile->ffs->eps_lock);
+
+       if (likely(io_data && io_data->ep && io_data->req))
+               value = usb_ep_dequeue(io_data->ep, io_data->req);
+       else
+               value = -EINVAL;
+
+       spin_unlock_irq(&epfile->ffs->eps_lock);
+
+       return value;
+}
+
+static ssize_t ffs_epfile_aio_write(struct kiocb *kiocb,
+                                   const struct iovec *iovec,
+                                   unsigned long nr_segs, loff_t loff)
+{
+       struct ffs_io_data *io_data;
+
+       ENTER();
+
+       io_data = kmalloc(sizeof(*io_data), GFP_KERNEL);
+       if (unlikely(!io_data))
+               return -ENOMEM;
+
+       io_data->aio = true;
+       io_data->read = false;
+       io_data->kiocb = kiocb;
+       io_data->iovec = iovec;
+       io_data->nr_segs = nr_segs;
+       io_data->len = kiocb->ki_nbytes;
+       io_data->mm = current->mm;
+
+       kiocb->private = io_data;
+
+       kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
+
+       return ffs_epfile_io(kiocb->ki_filp, io_data);
+}
+
+static ssize_t ffs_epfile_aio_read(struct kiocb *kiocb,
+                                  const struct iovec *iovec,
+                                  unsigned long nr_segs, loff_t loff)
+{
+       struct ffs_io_data *io_data;
+       struct iovec *iovec_copy;
+
+       ENTER();
+
+       iovec_copy = kmalloc_array(nr_segs, sizeof(*iovec_copy), GFP_KERNEL);
+       if (unlikely(!iovec_copy))
+               return -ENOMEM;
+
+       memcpy(iovec_copy, iovec, sizeof(struct iovec)*nr_segs);
+
+       io_data = kmalloc(sizeof(*io_data), GFP_KERNEL);
+       if (unlikely(!io_data)) {
+               kfree(iovec_copy);
+               return -ENOMEM;
+       }
+
+       io_data->aio = true;
+       io_data->read = true;
+       io_data->kiocb = kiocb;
+       io_data->iovec = iovec_copy;
+       io_data->nr_segs = nr_segs;
+       io_data->len = kiocb->ki_nbytes;
+       io_data->mm = current->mm;
+
+       kiocb->private = io_data;
+
+       kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
+
+       return ffs_epfile_io(kiocb->ki_filp, io_data);
+}
+
+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 = {
+       .llseek =       no_llseek,
+
+       .open =         ffs_epfile_open,
+       .write =        ffs_epfile_write,
+       .read =         ffs_epfile_read,
+       .aio_write =    ffs_epfile_aio_write,
+       .aio_read =     ffs_epfile_aio_read,
+       .release =      ffs_epfile_release,
+       .unlocked_ioctl =       ffs_epfile_ioctl,
+};
+
+
+/* File system and super block operations ***********************************/
+
+/*
+ * Mounting the file system 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_ino     = get_next_ino();
+               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;
+       struct ffs_data *ffs_data;
+};
+
+static int ffs_sb_fill(struct super_block *sb, void *_data, int silent)
+{
+       struct ffs_sb_fill_data *data = _data;
+       struct inode    *inode;
+       struct ffs_data *ffs = data->ffs_data;
+
+       ENTER();
+
+       ffs->sb              = sb;
+       data->ffs_data       = NULL;
+       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);
+       sb->s_root = d_make_root(inode);
+       if (unlikely(!sb->s_root))
+               return -ENOMEM;
+
+       /* EP0 file */
+       if (unlikely(!ffs_sb_create_file(sb, "ep0", ffs,
+                                        &ffs_ep0_operations, NULL)))
+               return -ENOMEM;
+
+       return 0;
+}
+
+static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
+{
+       ENTER();
+
+       if (!opts || !*opts)
+               return 0;
+
+       for (;;) {
+               unsigned long value;
+               char *eq, *comma;
+
+               /* Option limit */
+               comma = strchr(opts, ',');
+               if (comma)
+                       *comma = 0;
+
+               /* Value limit */
+               eq = strchr(opts, '=');
+               if (unlikely(!eq)) {
+                       pr_err("'=' missing in %s\n", opts);
+                       return -EINVAL;
+               }
+               *eq = 0;
+
+               /* Parse value */
+               if (kstrtoul(eq + 1, 0, &value)) {
+                       pr_err("%s: invalid value: %s\n", 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 = make_kuid(current_user_ns(), value);
+                               if (!uid_valid(data->perms.uid)) {
+                                       pr_err("%s: unmapped value: %lu\n", opts, value);
+                                       return -EINVAL;
+                               }
+                       } else if (!memcmp(opts, "gid", 3)) {
+                               data->perms.gid = make_kgid(current_user_ns(), value);
+                               if (!gid_valid(data->perms.gid)) {
+                                       pr_err("%s: unmapped value: %lu\n", opts, value);
+                                       return -EINVAL;
+                               }
+                       } else {
+                               goto invalid;
+                       }
+                       break;
+
+               default:
+invalid:
+                       pr_err("%s: invalid option\n", opts);
+                       return -EINVAL;
+               }
+
+               /* Next iteration */
+               if (!comma)
+                       break;
+               opts = comma + 1;
+       }
+
+       return 0;
+}
+
+/* "mount -t functionfs dev_name /dev/function" ends up here */
+
+static struct dentry *
+ffs_fs_mount(struct file_system_type *t, int flags,
+             const char *dev_name, void *opts)
+{
+       struct ffs_sb_fill_data data = {
+               .perms = {
+                       .mode = S_IFREG | 0600,
+                       .uid = GLOBAL_ROOT_UID,
+                       .gid = GLOBAL_ROOT_GID,
+               },
+               .root_mode = S_IFDIR | 0500,
+       };
+       struct dentry *rv;
+       int ret;
+       void *ffs_dev;
+       struct ffs_data *ffs;
+
+       ENTER();
+
+       ret = ffs_fs_parse_opts(&data, opts);
+       if (unlikely(ret < 0))
+               return ERR_PTR(ret);
+
+       ffs = ffs_data_new();
+       if (unlikely(!ffs))
+               return ERR_PTR(-ENOMEM);
+       ffs->file_perms = data.perms;
+
+       ffs->dev_name = kstrdup(dev_name, GFP_KERNEL);
+       if (unlikely(!ffs->dev_name)) {
+               ffs_data_put(ffs);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       ffs_dev = ffs_acquire_dev(dev_name);
+       if (IS_ERR(ffs_dev)) {
+               ffs_data_put(ffs);
+               return ERR_CAST(ffs_dev);
+       }
+       ffs->private_data = ffs_dev;
+       data.ffs_data = ffs;
+
+       rv = mount_nodev(t, flags, &data, ffs_sb_fill);
+       if (IS_ERR(rv) && data.ffs_data) {
+               ffs_release_dev(data.ffs_data);
+               ffs_data_put(data.ffs_data);
+       }
+       return rv;
+}
+
+static void
+ffs_fs_kill_sb(struct super_block *sb)
+{
+       ENTER();
+
+       kill_litter_super(sb);
+       if (sb->s_fs_info) {
+               ffs_release_dev(sb->s_fs_info);
+               ffs_data_put(sb->s_fs_info);
+       }
+}
+
+static struct file_system_type ffs_fs_type = {
+       .owner          = THIS_MODULE,
+       .name           = "functionfs",
+       .mount          = ffs_fs_mount,
+       .kill_sb        = ffs_fs_kill_sb,
+};
+MODULE_ALIAS_FS("functionfs");
+
+
+/* Driver's main init/cleanup functions *************************************/
+
+static int functionfs_init(void)
+{
+       int ret;
+
+       ENTER();
+
+       ret = register_filesystem(&ffs_fs_type);
+       if (likely(!ret))
+               pr_info("file system registered\n");
+       else
+               pr_err("failed registering file system (%d)\n", ret);
+
+       return ret;
+}
+
+static void functionfs_cleanup(void)
+{
+       ENTER();
+
+       pr_info("unloading\n");
+       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))) {
+               pr_info("%s(): freeing\n", __func__);
+               ffs_data_clear(ffs);
+               BUG_ON(waitqueue_active(&ffs->ev.waitq) ||
+                      waitqueue_active(&ffs->ep0req_completion.wait));
+               kfree(ffs->dev_name);
+               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 NULL;
+
+       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))
+               ffs_closed(ffs);
+
+       BUG_ON(ffs->gadget);
+
+       if (ffs->epfiles)
+               ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count);
+
+       kfree(ffs->raw_descs_data);
+       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_data = NULL;
+       ffs->raw_descs = NULL;
+       ffs->raw_strings = NULL;
+       ffs->stringtabs = NULL;
+
+       ffs->raw_descs_length = 0;
+       ffs->fs_descs_count = 0;
+       ffs->hs_descs_count = 0;
+       ffs->ss_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)
+{
+       struct usb_gadget_strings **lang;
+       int first_id;
+
+       ENTER();
+
+       if (WARN_ON(ffs->state != FFS_ACTIVE
+                || test_and_set_bit(FFS_FL_BOUND, &ffs->flags)))
+               return -EBADFD;
+
+       first_id = usb_string_ids_n(cdev, ffs->strings_count);
+       if (unlikely(first_id < 0))
+               return first_id;
+
+       ffs->ep0req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL);
+       if (unlikely(!ffs->ep0req))
+               return -ENOMEM;
+       ffs->ep0req->complete = ffs_ep0_complete;
+       ffs->ep0req->context = ffs;
+
+       lang = ffs->stringtabs;
+       for (lang = ffs->stringtabs; *lang; ++lang) {
+               struct usb_string *str = (*lang)->strings;
+               int id = first_id;
+               for (; str->s; ++id, ++str)
+                       str->id = id;
+       }
+
+       ffs->gadget = cdev->gadget;
+       ffs_data_get(ffs);
+       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;
+               clear_bit(FFS_FL_BOUND, &ffs->flags);
+               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 = kcalloc(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 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;
+               int desc_idx;
+
+               if (ffs->gadget->speed == USB_SPEED_SUPER)
+                       desc_idx = 2;
+               else if (ffs->gadget->speed == USB_SPEED_HIGH)
+                       desc_idx = 1;
+               else
+                       desc_idx = 0;
+
+               /* fall-back to lower speed if desc missing for current speed */
+               do {
+                       ds = ep->descs[desc_idx];
+               } while (!ds && --desc_idx >= 0);
+
+               if (!ds) {
+                       ret = -EINVAL;
+                       break;
+               }
+
+               ep->ep->driver_data = ep;
+               ep->ep->desc = ds;
+               ret = usb_ep_enable(ep->ep);
+               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 after the
+ * descriptor or NULL if data is invalid.
+ */
+
+enum ffs_entity_type {
+       FFS_DESCRIPTOR, FFS_INTERFACE, FFS_STRING, FFS_ENDPOINT
+};
+
+enum ffs_os_desc_type {
+       FFS_OS_DESC, FFS_OS_DESC_EXT_COMPAT, FFS_OS_DESC_EXT_PROP
+};
+
+typedef int (*ffs_entity_callback)(enum ffs_entity_type entity,
+                                  u8 *valuep,
+                                  struct usb_descriptor_header *desc,
+                                  void *priv);
+
+typedef int (*ffs_os_desc_callback)(enum ffs_os_desc_type entity,
+                                   struct usb_os_desc_header *h, void *data,
+                                   unsigned len, void *priv);
+
+static int __must_check ffs_do_single_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) {
+               pr_vdebug("descriptor too short\n");
+               return -EINVAL;
+       }
+
+       /* If we have at least as many bytes as the descriptor takes? */
+       length = _ds->bLength;
+       if (len < length) {
+               pr_vdebug("descriptor longer then available data\n");
+               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 {                                       \
+               pr_vdebug("entity " #type "(%02x)\n", (val));           \
+               if (unlikely(!__entity_check_ ##type(val))) {           \
+                       pr_vdebug("invalid entity's value\n");          \
+                       return -EINVAL;                                 \
+               }                                                       \
+               ret = entity(FFS_ ##type, &val, _ds, priv);             \
+               if (unlikely(ret < 0)) {                                \
+                       pr_debug("entity " #type "(%02x); ret = %d\n",  \
+                                (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 */
+               pr_vdebug("descriptor reserved for gadget: %d\n",
+                     _ds->bDescriptorType);
+               return -EINVAL;
+
+       case USB_DT_INTERFACE: {
+               struct usb_interface_descriptor *ds = (void *)_ds;
+               pr_vdebug("interface descriptor\n");
+               if (length != sizeof *ds)
+                       goto inv_length;
+
+               __entity(INTERFACE, ds->bInterfaceNumber);
+               if (ds->iInterface)
+                       __entity(STRING, ds->iInterface);
+       }
+               break;
+
+       case USB_DT_ENDPOINT: {
+               struct usb_endpoint_descriptor *ds = (void *)_ds;
+               pr_vdebug("endpoint descriptor\n");
+               if (length != USB_DT_ENDPOINT_SIZE &&
+                   length != USB_DT_ENDPOINT_AUDIO_SIZE)
+                       goto inv_length;
+               __entity(ENDPOINT, ds->bEndpointAddress);
+       }
+               break;
+
+       case HID_DT_HID:
+               pr_vdebug("hid descriptor\n");
+               if (length != sizeof(struct hid_descriptor))
+                       goto inv_length;
+               break;
+
+       case USB_DT_OTG:
+               if (length != sizeof(struct usb_otg_descriptor))
+                       goto inv_length;
+               break;
+
+       case USB_DT_INTERFACE_ASSOCIATION: {
+               struct usb_interface_assoc_descriptor *ds = (void *)_ds;
+               pr_vdebug("interface association descriptor\n");
+               if (length != sizeof *ds)
+                       goto inv_length;
+               if (ds->iFunction)
+                       __entity(STRING, ds->iFunction);
+       }
+               break;
+
+       case USB_DT_SS_ENDPOINT_COMP:
+               pr_vdebug("EP SS companion descriptor\n");
+               if (length != sizeof(struct usb_ss_ep_comp_descriptor))
+                       goto inv_length;
+               break;
+
+       case USB_DT_OTHER_SPEED_CONFIG:
+       case USB_DT_INTERFACE_POWER:
+       case USB_DT_DEBUG:
+       case USB_DT_SECURITY:
+       case USB_DT_CS_RADIO_CONTROL:
+               /* TODO */
+               pr_vdebug("unimplemented descriptor: %d\n", _ds->bDescriptorType);
+               return -EINVAL;
+
+       default:
+               /* We should never be here */
+               pr_vdebug("unknown descriptor: %d\n", _ds->bDescriptorType);
+               return -EINVAL;
+
+inv_length:
+               pr_vdebug("invalid length: %d (descriptor %d)\n",
+                         _ds->bLength, _ds->bDescriptorType);
+               return -EINVAL;
+       }
+
+#undef __entity
+#undef __entity_check_DESCRIPTOR
+#undef __entity_check_INTERFACE
+#undef __entity_check_STRING
+#undef __entity_check_ENDPOINT
+
+       return length;
+}
+
+static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
+                                    ffs_entity_callback entity, void *priv)
+{
+       const unsigned _len = len;
+       unsigned long num = 0;
+
+       ENTER();
+
+       for (;;) {
+               int ret;
+
+               if (num == count)
+                       data = NULL;
+
+               /* Record "descriptor" entity */
+               ret = entity(FFS_DESCRIPTOR, (u8 *)num, (void *)data, priv);
+               if (unlikely(ret < 0)) {
+                       pr_debug("entity DESCRIPTOR(%02lx); ret = %d\n",
+                                num, ret);
+                       return ret;
+               }
+
+               if (!data)
+                       return _len - len;
+
+               ret = ffs_do_single_desc(data, len, entity, priv);
+               if (unlikely(ret < 0)) {
+                       pr_debug("%s returns %d\n", __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_do_os_desc_header(enum ffs_os_desc_type *next_type,
+                                  struct usb_os_desc_header *desc)
+{
+       u16 bcd_version = le16_to_cpu(desc->bcdVersion);
+       u16 w_index = le16_to_cpu(desc->wIndex);
+
+       if (bcd_version != 1) {
+               pr_vdebug("unsupported os descriptors version: %d",
+                         bcd_version);
+               return -EINVAL;
+       }
+       switch (w_index) {
+       case 0x4:
+               *next_type = FFS_OS_DESC_EXT_COMPAT;
+               break;
+       case 0x5:
+               *next_type = FFS_OS_DESC_EXT_PROP;
+               break;
+       default:
+               pr_vdebug("unsupported os descriptor type: %d", w_index);
+               return -EINVAL;
+       }
+
+       return sizeof(*desc);
+}
+
+/*
+ * Process all extended compatibility/extended property descriptors
+ * of a feature descriptor
+ */
+static int __must_check ffs_do_single_os_desc(char *data, unsigned len,
+                                             enum ffs_os_desc_type type,
+                                             u16 feature_count,
+                                             ffs_os_desc_callback entity,
+                                             void *priv,
+                                             struct usb_os_desc_header *h)
+{
+       int ret;
+       const unsigned _len = len;
+
+       ENTER();
+
+       /* loop over all ext compat/ext prop descriptors */
+       while (feature_count--) {
+               ret = entity(type, h, data, len, priv);
+               if (unlikely(ret < 0)) {
+                       pr_debug("bad OS descriptor, type: %d\n", type);
+                       return ret;
+               }
+               data += ret;
+               len -= ret;
+       }
+       return _len - len;
+}
+
+/* Process a number of complete Feature Descriptors (Ext Compat or Ext Prop) */
+static int __must_check ffs_do_os_descs(unsigned count,
+                                       char *data, unsigned len,
+                                       ffs_os_desc_callback entity, void *priv)
+{
+       const unsigned _len = len;
+       unsigned long num = 0;
+
+       ENTER();
+
+       for (num = 0; num < count; ++num) {
+               int ret;
+               enum ffs_os_desc_type type;
+               u16 feature_count;
+               struct usb_os_desc_header *desc = (void *)data;
+
+               if (len < sizeof(*desc))
+                       return -EINVAL;
+
+               /*
+                * Record "descriptor" entity.
+                * Process dwLength, bcdVersion, wIndex, get b/wCount.
+                * Move the data pointer to the beginning of extended
+                * compatibilities proper or extended properties proper
+                * portions of the data
+                */
+               if (le32_to_cpu(desc->dwLength) > len)
+                       return -EINVAL;
+
+               ret = __ffs_do_os_desc_header(&type, desc);
+               if (unlikely(ret < 0)) {
+                       pr_debug("entity OS_DESCRIPTOR(%02lx); ret = %d\n",
+                                num, ret);
+                       return ret;
+               }
+               /*
+                * 16-bit hex "?? 00" Little Endian looks like 8-bit hex "??"
+                */
+               feature_count = le16_to_cpu(desc->wCount);
+               if (type == FFS_OS_DESC_EXT_COMPAT &&
+                   (feature_count > 255 || desc->Reserved))
+                               return -EINVAL;
+               len -= ret;
+               data += ret;
+
+               /*
+                * Process all function/property descriptors
+                * of this Feature Descriptor
+                */
+               ret = ffs_do_single_os_desc(data, len, type,
+                                           feature_count, entity, priv, desc);
+               if (unlikely(ret < 0)) {
+                       pr_debug("%s returns %d\n", __func__, ret);
+                       return ret;
+               }
+
+               len -= ret;
+               data += ret;
+       }
+       return _len - len;
+}
+
+/**
+ * Validate contents of the buffer from userspace related to OS descriptors.
+ */
+static int __ffs_data_do_os_desc(enum ffs_os_desc_type type,
+                                struct usb_os_desc_header *h, void *data,
+                                unsigned len, void *priv)
+{
+       struct ffs_data *ffs = priv;
+       u8 length;
+
+       ENTER();
+
+       switch (type) {
+       case FFS_OS_DESC_EXT_COMPAT: {
+               struct usb_ext_compat_desc *d = data;
+               int i;
+
+               if (len < sizeof(*d) ||
+                   d->bFirstInterfaceNumber >= ffs->interfaces_count ||
+                   d->Reserved1)
+                       return -EINVAL;
+               for (i = 0; i < ARRAY_SIZE(d->Reserved2); ++i)
+                       if (d->Reserved2[i])
+                               return -EINVAL;
+
+               length = sizeof(struct usb_ext_compat_desc);
+       }
+               break;
+       case FFS_OS_DESC_EXT_PROP: {
+               struct usb_ext_prop_desc *d = data;
+               u32 type, pdl;
+               u16 pnl;
+
+               if (len < sizeof(*d) || h->interface >= ffs->interfaces_count)
+                       return -EINVAL;
+               length = le32_to_cpu(d->dwSize);
+               type = le32_to_cpu(d->dwPropertyDataType);
+               if (type < USB_EXT_PROP_UNICODE ||
+                   type > USB_EXT_PROP_UNICODE_MULTI) {
+                       pr_vdebug("unsupported os descriptor property type: %d",
+                                 type);
+                       return -EINVAL;
+               }
+               pnl = le16_to_cpu(d->wPropertyNameLength);
+               pdl = le32_to_cpu(*(u32 *)((u8 *)data + 10 + pnl));
+               if (length != 14 + pnl + pdl) {
+                       pr_vdebug("invalid os descriptor length: %d pnl:%d pdl:%d (descriptor %d)\n",
+                                 length, pnl, pdl, type);
+                       return -EINVAL;
+               }
+               ++ffs->ms_os_descs_ext_prop_count;
+               /* property name reported to the host as "WCHAR"s */
+               ffs->ms_os_descs_ext_prop_name_len += pnl * 2;
+               ffs->ms_os_descs_ext_prop_data_len += pdl;
+       }
+               break;
+       default:
+               pr_vdebug("unknown descriptor: %d\n", type);
+               return -EINVAL;
+       }
+       return length;
+}
+
+static int __ffs_data_got_descs(struct ffs_data *ffs,
+                               char *const _data, size_t len)
+{
+       char *data = _data, *raw_descs;
+       unsigned os_descs_count = 0, counts[3], flags;
+       int ret = -EINVAL, i;
+
+       ENTER();
+
+       if (get_unaligned_le32(data + 4) != len)
+               goto error;
+
+       switch (get_unaligned_le32(data)) {
+       case FUNCTIONFS_DESCRIPTORS_MAGIC:
+               flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC;
+               data += 8;
+               len  -= 8;
+               break;
+       case FUNCTIONFS_DESCRIPTORS_MAGIC_V2:
+               flags = get_unaligned_le32(data + 8);
+               if (flags & ~(FUNCTIONFS_HAS_FS_DESC |
+                             FUNCTIONFS_HAS_HS_DESC |
+                             FUNCTIONFS_HAS_SS_DESC |
+                             FUNCTIONFS_HAS_MS_OS_DESC)) {
+                       ret = -ENOSYS;
+                       goto error;
+               }
+               data += 12;
+               len  -= 12;
+               break;
+       default:
+               goto error;
+       }
+
+       /* Read fs_count, hs_count and ss_count (if present) */
+       for (i = 0; i < 3; ++i) {
+               if (!(flags & (1 << i))) {
+                       counts[i] = 0;
+               } else if (len < 4) {
+                       goto error;
+               } else {
+                       counts[i] = get_unaligned_le32(data);
+                       data += 4;
+                       len  -= 4;
+               }
+       }
+       if (flags & (1 << i)) {
+               os_descs_count = get_unaligned_le32(data);
+               data += 4;
+               len -= 4;
+       };
+
+       /* Read descriptors */
+       raw_descs = data;
+       for (i = 0; i < 3; ++i) {
+               if (!counts[i])
+                       continue;
+               ret = ffs_do_descs(counts[i], data, len,
+                                  __ffs_data_do_entity, ffs);
+               if (ret < 0)
+                       goto error;
+               data += ret;
+               len  -= ret;
+       }
+       if (os_descs_count) {
+               ret = ffs_do_os_descs(os_descs_count, data, len,
+                                     __ffs_data_do_os_desc, ffs);
+               if (ret < 0)
+                       goto error;
+               data += ret;
+               len -= ret;
+       }
+
+       if (raw_descs == data || len) {
+               ret = -EINVAL;
+               goto error;
+       }
+
+       ffs->raw_descs_data     = _data;
+       ffs->raw_descs          = raw_descs;
+       ffs->raw_descs_length   = data - raw_descs;
+       ffs->fs_descs_count     = counts[0];
+       ffs->hs_descs_count     = counts[1];
+       ffs->ss_descs_count     = counts[2];
+       ffs->ms_os_descs_count  = os_descs_count;
+
+       return 0;
+
+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 everything in one chunk so there's less maintenance. */
+       {
+               unsigned i = 0;
+               vla_group(d);
+               vla_item(d, struct usb_gadget_strings *, stringtabs,
+                       lang_count + 1);
+               vla_item(d, struct usb_gadget_strings, stringtab, lang_count);
+               vla_item(d, struct usb_string, strings,
+                       lang_count*(needed_count+1));
+
+               char *vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL);
+
+               if (unlikely(!vlabuf)) {
+                       kfree(_data);
+                       return -ENOMEM;
+               }
+
+               /* Initialize the VLA pointers */
+               stringtabs = vla_ptr(vlabuf, d, stringtabs);
+               t = vla_ptr(vlabuf, d, stringtab);
+               i = lang_count;
+               do {
+                       *stringtabs++ = t++;
+               } while (--i);
+               *stringtabs = NULL;
+
+               /* stringtabs = vlabuf = d_stringtabs for later kfree */
+               stringtabs = vla_ptr(vlabuf, d, stringtabs);
+               t = vla_ptr(vlabuf, d, stringtab);
+               s = vla_ptr(vlabuf, 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 ignore 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, rem_type2 = type;
+       int neg = 0;
+
+       /*
+        * Abort any unhandled setup
+        *
+        * We do not need to worry about some cmpxchg() changing value
+        * of ffs->setup_state without holding the lock because when
+        * state is FFS_SETUP_PENDING cmpxchg() in several places in
+        * the source does nothing.
+        */
+       if (ffs->setup_state == FFS_SETUP_PENDING)
+               ffs->setup_state = FFS_SETUP_CANCELLED;
+
+       switch (type) {
+       case FUNCTIONFS_RESUME:
+               rem_type2 = FUNCTIONFS_SUSPEND;
+               /* FALL THROUGH */
+       case FUNCTIONFS_SUSPEND:
+       case FUNCTIONFS_SETUP:
+               rem_type1 = type;
+               /* 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;
+                       else
+                               pr_vdebug("purging event %d\n", *ev);
+               ffs->ev.count = out - ffs->ev.types;
+       }
+
+       pr_vdebug("adding event %d\n", type);
+       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;
+       unsigned ep_desc_id, idx;
+       static const char *speed_names[] = { "full", "high", "super" };
+
+       if (type != FFS_DESCRIPTOR)
+               return 0;
+
+       /*
+        * If ss_descriptors is not NULL, we are reading super speed
+        * descriptors; if hs_descriptors is not NULL, we are reading high
+        * speed descriptors; otherwise, we are reading full speed
+        * descriptors.
+        */
+       if (func->function.ss_descriptors) {
+               ep_desc_id = 2;
+               func->function.ss_descriptors[(long)valuep] = desc;
+       } else if (func->function.hs_descriptors) {
+               ep_desc_id = 1;
+               func->function.hs_descriptors[(long)valuep] = desc;
+       } else {
+               ep_desc_id = 0;
+               func->function.fs_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[ep_desc_id])) {
+               pr_err("two %sspeed descriptors for EP %d\n",
+                         speed_names[ep_desc_id],
+                         ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+               return -EINVAL;
+       }
+       ffs_ep->descs[ep_desc_id] = 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;
+
+               pr_vdebug("autoconfig\n");
+               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;
+       }
+
+       pr_vdebug("%02x -> %02x\n", *valuep, newValue);
+       *valuep = newValue;
+       return 0;
+}
+
+static int __ffs_func_bind_do_os_desc(enum ffs_os_desc_type type,
+                                     struct usb_os_desc_header *h, void *data,
+                                     unsigned len, void *priv)
+{
+       struct ffs_function *func = priv;
+       u8 length = 0;
+
+       switch (type) {
+       case FFS_OS_DESC_EXT_COMPAT: {
+               struct usb_ext_compat_desc *desc = data;
+               struct usb_os_desc_table *t;
+
+               t = &func->function.os_desc_table[desc->bFirstInterfaceNumber];
+               t->if_id = func->interfaces_nums[desc->bFirstInterfaceNumber];
+               memcpy(t->os_desc->ext_compat_id, &desc->CompatibleID,
+                      ARRAY_SIZE(desc->CompatibleID) +
+                      ARRAY_SIZE(desc->SubCompatibleID));
+               length = sizeof(*desc);
+       }
+               break;
+       case FFS_OS_DESC_EXT_PROP: {
+               struct usb_ext_prop_desc *desc = data;
+               struct usb_os_desc_table *t;
+               struct usb_os_desc_ext_prop *ext_prop;
+               char *ext_prop_name;
+               char *ext_prop_data;
+
+               t = &func->function.os_desc_table[h->interface];
+               t->if_id = func->interfaces_nums[h->interface];
+
+               ext_prop = func->ffs->ms_os_descs_ext_prop_avail;
+               func->ffs->ms_os_descs_ext_prop_avail += sizeof(*ext_prop);
+
+               ext_prop->type = le32_to_cpu(desc->dwPropertyDataType);
+               ext_prop->name_len = le16_to_cpu(desc->wPropertyNameLength);
+               ext_prop->data_len = le32_to_cpu(*(u32 *)
+                       usb_ext_prop_data_len_ptr(data, ext_prop->name_len));
+               length = ext_prop->name_len + ext_prop->data_len + 14;
+
+               ext_prop_name = func->ffs->ms_os_descs_ext_prop_name_avail;
+               func->ffs->ms_os_descs_ext_prop_name_avail +=
+                       ext_prop->name_len;
+
+               ext_prop_data = func->ffs->ms_os_descs_ext_prop_data_avail;
+               func->ffs->ms_os_descs_ext_prop_data_avail +=
+                       ext_prop->data_len;
+               memcpy(ext_prop_data,
+                      usb_ext_prop_data_ptr(data, ext_prop->name_len),
+                      ext_prop->data_len);
+               /* unicode data reported to the host as "WCHAR"s */
+               switch (ext_prop->type) {
+               case USB_EXT_PROP_UNICODE:
+               case USB_EXT_PROP_UNICODE_ENV:
+               case USB_EXT_PROP_UNICODE_LINK:
+               case USB_EXT_PROP_UNICODE_MULTI:
+                       ext_prop->data_len *= 2;
+                       break;
+               }
+               ext_prop->data = ext_prop_data;
+
+               memcpy(ext_prop_name, usb_ext_prop_name_ptr(data),
+                      ext_prop->name_len);
+               /* property name reported to the host as "WCHAR"s */
+               ext_prop->name_len *= 2;
+               ext_prop->name = ext_prop_name;
+
+               t->os_desc->ext_prop_len +=
+                       ext_prop->name_len + ext_prop->data_len + 14;
+               ++t->os_desc->ext_prop_count;
+               list_add_tail(&ext_prop->entry, &t->os_desc->ext_prop);
+       }
+               break;
+       default:
+               pr_vdebug("unknown descriptor: %d\n", type);
+       }
+
+       return length;
+}
+
+static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f,
+                                               struct usb_configuration *c)
+{
+       struct ffs_function *func = ffs_func_from_usb(f);
+       struct f_fs_opts *ffs_opts =
+               container_of(f->fi, struct f_fs_opts, func_inst);
+       int ret;
+
+       ENTER();
+
+       /*
+        * Legacy gadget triggers binding in functionfs_ready_callback,
+        * which already uses locking; taking the same lock here would
+        * cause a deadlock.
+        *
+        * Configfs-enabled gadgets however do need ffs_dev_lock.
+        */
+       if (!ffs_opts->no_configfs)
+               ffs_dev_lock();
+       ret = ffs_opts->dev->desc_ready ? 0 : -ENODEV;
+       func->ffs = ffs_opts->dev->ffs_data;
+       if (!ffs_opts->no_configfs)
+               ffs_dev_unlock();
+       if (ret)
+               return ERR_PTR(ret);
+
+       func->conf = c;
+       func->gadget = c->cdev->gadget;
+
+       ffs_data_get(func->ffs);
+
+       /*
+        * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
+        * configurations are bound in sequence with list_for_each_entry,
+        * in each configuration its functions are bound in sequence
+        * with list_for_each_entry, so we assume no race condition
+        * with regard to ffs_opts->bound access
+        */
+       if (!ffs_opts->refcnt) {
+               ret = functionfs_bind(func->ffs, c->cdev);
+               if (ret)
+                       return ERR_PTR(ret);
+       }
+       ffs_opts->refcnt++;
+       func->function.strings = func->ffs->stringtabs;
+
+       return ffs_opts;
+}
+
+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;
+       const int super = gadget_is_superspeed(func->gadget) &&
+               func->ffs->ss_descs_count;
+
+       int fs_len, hs_len, ss_len, ret, i;
+
+       /* Make it a single chunk, less management later on */
+       vla_group(d);
+       vla_item_with_sz(d, struct ffs_ep, eps, ffs->eps_count);
+       vla_item_with_sz(d, struct usb_descriptor_header *, fs_descs,
+               full ? ffs->fs_descs_count + 1 : 0);
+       vla_item_with_sz(d, struct usb_descriptor_header *, hs_descs,
+               high ? ffs->hs_descs_count + 1 : 0);
+       vla_item_with_sz(d, struct usb_descriptor_header *, ss_descs,
+               super ? ffs->ss_descs_count + 1 : 0);
+       vla_item_with_sz(d, short, inums, ffs->interfaces_count);
+       vla_item_with_sz(d, struct usb_os_desc_table, os_desc_table,
+                        c->cdev->use_os_string ? ffs->interfaces_count : 0);
+       vla_item_with_sz(d, char[16], ext_compat,
+                        c->cdev->use_os_string ? ffs->interfaces_count : 0);
+       vla_item_with_sz(d, struct usb_os_desc, os_desc,
+                        c->cdev->use_os_string ? ffs->interfaces_count : 0);
+       vla_item_with_sz(d, struct usb_os_desc_ext_prop, ext_prop,
+                        ffs->ms_os_descs_ext_prop_count);
+       vla_item_with_sz(d, char, ext_prop_name,
+                        ffs->ms_os_descs_ext_prop_name_len);
+       vla_item_with_sz(d, char, ext_prop_data,
+                        ffs->ms_os_descs_ext_prop_data_len);
+       vla_item_with_sz(d, char, raw_descs, ffs->raw_descs_length);
+       char *vlabuf;
+
+       ENTER();
+
+       /* Has descriptors only for speeds gadget does not support */
+       if (unlikely(!(full | high | super)))
+               return -ENOTSUPP;
+
+       /* Allocate a single chunk, less management later on */
+       vlabuf = kzalloc(vla_group_size(d), GFP_KERNEL);
+       if (unlikely(!vlabuf))
+               return -ENOMEM;
+
+       ffs->ms_os_descs_ext_prop_avail = vla_ptr(vlabuf, d, ext_prop);
+       ffs->ms_os_descs_ext_prop_name_avail =
+               vla_ptr(vlabuf, d, ext_prop_name);
+       ffs->ms_os_descs_ext_prop_data_avail =
+               vla_ptr(vlabuf, d, ext_prop_data);
+
+       /* Copy descriptors  */
+       memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs,
+              ffs->raw_descs_length);
+
+       memset(vla_ptr(vlabuf, d, inums), 0xff, d_inums__sz);
+       for (ret = ffs->eps_count; ret; --ret) {
+               struct ffs_ep *ptr;
+
+               ptr = vla_ptr(vlabuf, d, eps);
+               ptr[ret].num = -1;
+       }
+
+       /* Save pointers
+        * d_eps == vlabuf, func->eps used to kfree vlabuf later
+       */
+       func->eps             = vla_ptr(vlabuf, d, eps);
+       func->interfaces_nums = vla_ptr(vlabuf, d, inums);
+
+       /*
+        * Go through all the endpoint descriptors and allocate
+        * endpoints first, so that later we can rewrite the endpoint
+        * numbers without worrying that it may be described later on.
+        */
+       if (likely(full)) {
+               func->function.fs_descriptors = vla_ptr(vlabuf, d, fs_descs);
+               fs_len = ffs_do_descs(ffs->fs_descs_count,
+                                     vla_ptr(vlabuf, d, raw_descs),
+                                     d_raw_descs__sz,
+                                     __ffs_func_bind_do_descs, func);
+               if (unlikely(fs_len < 0)) {
+                       ret = fs_len;
+                       goto error;
+               }
+       } else {
+               fs_len = 0;
+       }
+
+       if (likely(high)) {
+               func->function.hs_descriptors = vla_ptr(vlabuf, d, hs_descs);
+               hs_len = ffs_do_descs(ffs->hs_descs_count,
+                                     vla_ptr(vlabuf, d, raw_descs) + fs_len,
+                                     d_raw_descs__sz - fs_len,
+                                     __ffs_func_bind_do_descs, func);
+               if (unlikely(hs_len < 0)) {
+                       ret = hs_len;
+                       goto error;
+               }
+       } else {
+               hs_len = 0;
+       }
+
+       if (likely(super)) {
+               func->function.ss_descriptors = vla_ptr(vlabuf, d, ss_descs);
+               ss_len = ffs_do_descs(ffs->ss_descs_count,
+                               vla_ptr(vlabuf, d, raw_descs) + fs_len + hs_len,
+                               d_raw_descs__sz - fs_len - hs_len,
+                               __ffs_func_bind_do_descs, func);
+               if (unlikely(ss_len < 0)) {
+                       ret = ss_len;
+                       goto error;
+               }
+       } else {
+               ss_len = 0;
+       }
+
+       /*
+        * Now handle interface numbers allocation and interface and
+        * endpoint numbers rewriting.  We can do that in one go
+        * now.
+        */
+       ret = ffs_do_descs(ffs->fs_descs_count +
+                          (high ? ffs->hs_descs_count : 0) +
+                          (super ? ffs->ss_descs_count : 0),
+                          vla_ptr(vlabuf, d, raw_descs), d_raw_descs__sz,
+                          __ffs_func_bind_do_nums, func);
+       if (unlikely(ret < 0))
+               goto error;
+
+       func->function.os_desc_table = vla_ptr(vlabuf, d, os_desc_table);
+       if (c->cdev->use_os_string)
+               for (i = 0; i < ffs->interfaces_count; ++i) {
+                       struct usb_os_desc *desc;
+
+                       desc = func->function.os_desc_table[i].os_desc =
+                               vla_ptr(vlabuf, d, os_desc) +
+                               i * sizeof(struct usb_os_desc);
+                       desc->ext_compat_id =
+                               vla_ptr(vlabuf, d, ext_compat) + i * 16;
+                       INIT_LIST_HEAD(&desc->ext_prop);
+               }
+       ret = ffs_do_os_descs(ffs->ms_os_descs_count,
+                             vla_ptr(vlabuf, d, raw_descs) +
+                             fs_len + hs_len + ss_len,
+                             d_raw_descs__sz - fs_len - hs_len - ss_len,
+                             __ffs_func_bind_do_os_desc, func);
+       if (unlikely(ret < 0))
+               goto error;
+       func->function.os_desc_n =
+               c->cdev->use_os_string ? ffs->interfaces_count : 0;
+
+       /* 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;
+}
+
+static int ffs_func_bind(struct usb_configuration *c,
+                        struct usb_function *f)
+{
+       struct f_fs_opts *ffs_opts = ffs_do_functionfs_bind(f, c);
+
+       if (IS_ERR(ffs_opts))
+               return PTR_ERR(ffs_opts);
+
+       return _ffs_func_bind(c, f);
+}
+
+
+/* Other USB function hooks *************************************************/
+
+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();
+
+       pr_vdebug("creq->bRequestType = %02x\n", creq->bRequestType);
+       pr_vdebug("creq->bRequest     = %02x\n", creq->bRequest);
+       pr_vdebug("creq->wValue       = %04x\n", le16_to_cpu(creq->wValue));
+       pr_vdebug("creq->wIndex       = %04x\n", le16_to_cpu(creq->wIndex));
+       pr_vdebug("creq->wLength      = %04x\n", le16_to_cpu(creq->wLength));
+
+       /*
+        * Most requests directed to interface go through 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 (unlikely(ret < 0))
+                       return ret;
+               break;
+
+       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);
+}
+
+
+/* Endpoint 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;
+}
+
+
+/* Devices management *******************************************************/
+
+static LIST_HEAD(ffs_devices);
+
+static struct ffs_dev *_ffs_do_find_dev(const char *name)
+{
+       struct ffs_dev *dev;
+
+       list_for_each_entry(dev, &ffs_devices, entry) {
+               if (!dev->name || !name)
+                       continue;
+               if (strcmp(dev->name, name) == 0)
+                       return dev;
+       }
+
+       return NULL;
+}
+
+/*
+ * ffs_lock must be taken by the caller of this function
+ */
+static struct ffs_dev *_ffs_get_single_dev(void)
+{
+       struct ffs_dev *dev;
+
+       if (list_is_singular(&ffs_devices)) {
+               dev = list_first_entry(&ffs_devices, struct ffs_dev, entry);
+               if (dev->single)
+                       return dev;
+       }
+
+       return NULL;
+}
+
+/*
+ * ffs_lock must be taken by the caller of this function
+ */
+static struct ffs_dev *_ffs_find_dev(const char *name)
+{
+       struct ffs_dev *dev;
+
+       dev = _ffs_get_single_dev();
+       if (dev)
+               return dev;
+
+       return _ffs_do_find_dev(name);
+}
+
+/* Configfs support *********************************************************/
+
+static inline struct f_fs_opts *to_ffs_opts(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct f_fs_opts,
+                           func_inst.group);
+}
+
+static void ffs_attr_release(struct config_item *item)
+{
+       struct f_fs_opts *opts = to_ffs_opts(item);
+
+       usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations ffs_item_ops = {
+       .release        = ffs_attr_release,
+};
+
+static struct config_item_type ffs_func_type = {
+       .ct_item_ops    = &ffs_item_ops,
+       .ct_owner       = THIS_MODULE,
+};
+
+
+/* Function registration interface ******************************************/
+
+static void ffs_free_inst(struct usb_function_instance *f)
+{
+       struct f_fs_opts *opts;
+
+       opts = to_f_fs_opts(f);
+       ffs_dev_lock();
+       _ffs_free_dev(opts->dev);
+       ffs_dev_unlock();
+       kfree(opts);
+}
+
+#define MAX_INST_NAME_LEN      40
+
+static int ffs_set_inst_name(struct usb_function_instance *fi, const char *name)
+{
+       struct f_fs_opts *opts;
+       char *ptr;
+       const char *tmp;
+       int name_len, ret;
+
+       name_len = strlen(name) + 1;
+       if (name_len > MAX_INST_NAME_LEN)
+               return -ENAMETOOLONG;
+
+       ptr = kstrndup(name, name_len, GFP_KERNEL);
+       if (!ptr)
+               return -ENOMEM;
+
+       opts = to_f_fs_opts(fi);
+       tmp = NULL;
+
+       ffs_dev_lock();
+
+       tmp = opts->dev->name_allocated ? opts->dev->name : NULL;
+       ret = _ffs_name_dev(opts->dev, ptr);
+       if (ret) {
+               kfree(ptr);
+               ffs_dev_unlock();
+               return ret;
+       }
+       opts->dev->name_allocated = true;
+
+       ffs_dev_unlock();
+
+       kfree(tmp);
+
+       return 0;
+}
+
+static struct usb_function_instance *ffs_alloc_inst(void)
+{
+       struct f_fs_opts *opts;
+       struct ffs_dev *dev;
+
+       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+       if (!opts)
+               return ERR_PTR(-ENOMEM);
+
+       opts->func_inst.set_inst_name = ffs_set_inst_name;
+       opts->func_inst.free_func_inst = ffs_free_inst;
+       ffs_dev_lock();
+       dev = _ffs_alloc_dev();
+       ffs_dev_unlock();
+       if (IS_ERR(dev)) {
+               kfree(opts);
+               return ERR_CAST(dev);
+       }
+       opts->dev = dev;
+       dev->opts = opts;
+
+       config_group_init_type_name(&opts->func_inst.group, "",
+                                   &ffs_func_type);
+       return &opts->func_inst;
+}
+
+static void ffs_free(struct usb_function *f)
+{
+       kfree(ffs_func_from_usb(f));
+}
+
+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;
+       struct f_fs_opts *opts =
+               container_of(f->fi, struct f_fs_opts, func_inst);
+       struct ffs_ep *ep = func->eps;
+       unsigned count = ffs->eps_count;
+       unsigned long flags;
+
+       ENTER();
+       if (ffs->func == func) {
+               ffs_func_eps_disable(func);
+               ffs->func = NULL;
+       }
+
+       if (!--opts->refcnt)
+               functionfs_unbind(ffs);
+
+       /* cleanup after autoconfig */
+       spin_lock_irqsave(&func->ffs->eps_lock, flags);
+       do {
+               if (ep->ep && ep->req)
+                       usb_ep_free_request(ep->ep, ep->req);
+               ep->req = NULL;
+               ++ep;
+       } while (--count);
+       spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
+       kfree(func->eps);
+       func->eps = NULL;
+       /*
+        * eps, descriptors and interfaces_nums are allocated in the
+        * same chunk so only one free is required.
+        */
+       func->function.fs_descriptors = NULL;
+       func->function.hs_descriptors = NULL;
+       func->function.ss_descriptors = NULL;
+       func->interfaces_nums = NULL;
+
+       ffs_event_add(ffs, FUNCTIONFS_UNBIND);
+}
+
+static struct usb_function *ffs_alloc(struct usb_function_instance *fi)
+{
+       struct ffs_function *func;
+
+       ENTER();
+
+       func = kzalloc(sizeof(*func), GFP_KERNEL);
+       if (unlikely(!func))
+               return ERR_PTR(-ENOMEM);
+
+       func->function.name    = "Function FS Gadget";
+
+       func->function.bind    = ffs_func_bind;
+       func->function.unbind  = ffs_func_unbind;
+       func->function.set_alt = ffs_func_set_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->function.free_func = ffs_free;
+
+       return &func->function;
+}
+
+/*
+ * ffs_lock must be taken by the caller of this function
+ */
+static struct ffs_dev *_ffs_alloc_dev(void)
+{
+       struct ffs_dev *dev;
+       int ret;
+
+       if (_ffs_get_single_dev())
+                       return ERR_PTR(-EBUSY);
+
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       if (!dev)
+               return ERR_PTR(-ENOMEM);
+
+       if (list_empty(&ffs_devices)) {
+               ret = functionfs_init();
+               if (ret) {
+                       kfree(dev);
+                       return ERR_PTR(ret);
+               }
+       }
+
+       list_add(&dev->entry, &ffs_devices);
+
+       return dev;
+}
+
+/*
+ * ffs_lock must be taken by the caller of this function
+ * The caller is responsible for "name" being available whenever f_fs needs it
+ */
+static int _ffs_name_dev(struct ffs_dev *dev, const char *name)
+{
+       struct ffs_dev *existing;
+
+       existing = _ffs_do_find_dev(name);
+       if (existing)
+               return -EBUSY;
+
+       dev->name = name;
+
+       return 0;
+}
+
+/*
+ * The caller is responsible for "name" being available whenever f_fs needs it
+ */
+int ffs_name_dev(struct ffs_dev *dev, const char *name)
+{
+       int ret;
+
+       ffs_dev_lock();
+       ret = _ffs_name_dev(dev, name);
+       ffs_dev_unlock();
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(ffs_name_dev);
+
+int ffs_single_dev(struct ffs_dev *dev)
+{
+       int ret;
+
+       ret = 0;
+       ffs_dev_lock();
+
+       if (!list_is_singular(&ffs_devices))
+               ret = -EBUSY;
+       else
+               dev->single = true;
+
+       ffs_dev_unlock();
+       return ret;
+}
+EXPORT_SYMBOL_GPL(ffs_single_dev);
+
+/*
+ * ffs_lock must be taken by the caller of this function
+ */
+static void _ffs_free_dev(struct ffs_dev *dev)
+{
+       list_del(&dev->entry);
+       if (dev->name_allocated)
+               kfree(dev->name);
+       kfree(dev);
+       if (list_empty(&ffs_devices))
+               functionfs_cleanup();
+}
+
+static void *ffs_acquire_dev(const char *dev_name)
+{
+       struct ffs_dev *ffs_dev;
+
+       ENTER();
+       ffs_dev_lock();
+
+       ffs_dev = _ffs_find_dev(dev_name);
+       if (!ffs_dev)
+               ffs_dev = ERR_PTR(-ENOENT);
+       else if (ffs_dev->mounted)
+               ffs_dev = ERR_PTR(-EBUSY);
+       else if (ffs_dev->ffs_acquire_dev_callback &&
+           ffs_dev->ffs_acquire_dev_callback(ffs_dev))
+               ffs_dev = ERR_PTR(-ENOENT);
+       else
+               ffs_dev->mounted = true;
+
+       ffs_dev_unlock();
+       return ffs_dev;
+}
+
+static void ffs_release_dev(struct ffs_data *ffs_data)
+{
+       struct ffs_dev *ffs_dev;
+
+       ENTER();
+       ffs_dev_lock();
+
+       ffs_dev = ffs_data->private_data;
+       if (ffs_dev) {
+               ffs_dev->mounted = false;
+
+               if (ffs_dev->ffs_release_dev_callback)
+                       ffs_dev->ffs_release_dev_callback(ffs_dev);
+       }
+
+       ffs_dev_unlock();
+}
+
+static int ffs_ready(struct ffs_data *ffs)
+{
+       struct ffs_dev *ffs_obj;
+       int ret = 0;
+
+       ENTER();
+       ffs_dev_lock();
+
+       ffs_obj = ffs->private_data;
+       if (!ffs_obj) {
+               ret = -EINVAL;
+               goto done;
+       }
+       if (WARN_ON(ffs_obj->desc_ready)) {
+               ret = -EBUSY;
+               goto done;
+       }
+
+       ffs_obj->desc_ready = true;
+       ffs_obj->ffs_data = ffs;
+
+       if (ffs_obj->ffs_ready_callback)
+               ret = ffs_obj->ffs_ready_callback(ffs);
+
+done:
+       ffs_dev_unlock();
+       return ret;
+}
+
+static void ffs_closed(struct ffs_data *ffs)
+{
+       struct ffs_dev *ffs_obj;
+
+       ENTER();
+       ffs_dev_lock();
+
+       ffs_obj = ffs->private_data;
+       if (!ffs_obj)
+               goto done;
+
+       ffs_obj->desc_ready = false;
+
+       if (ffs_obj->ffs_closed_callback)
+               ffs_obj->ffs_closed_callback(ffs);
+
+       if (!ffs_obj->opts || ffs_obj->opts->no_configfs
+           || !ffs_obj->opts->func_inst.group.cg_item.ci_parent)
+               goto done;
+
+       unregister_gadget_item(ffs_obj->opts->
+                              func_inst.group.cg_item.ci_parent->ci_parent);
+done:
+       ffs_dev_unlock();
+}
+
+/* Misc helper functions ****************************************************/
+
+static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock)
+{
+       return nonblock
+               ? likely(mutex_trylock(mutex)) ? 0 : -EAGAIN
+               : mutex_lock_interruptible(mutex);
+}
+
+static char *ffs_prepare_buffer(const char __user *buf, size_t len)
+{
+       char *data;
+
+       if (unlikely(!len))
+               return NULL;
+
+       data = kmalloc(len, GFP_KERNEL);
+       if (unlikely(!data))
+               return ERR_PTR(-ENOMEM);
+
+       if (unlikely(__copy_from_user(data, buf, len))) {
+               kfree(data);
+               return ERR_PTR(-EFAULT);
+       }
+
+       pr_vdebug("Buffer from user space:\n");
+       ffs_dump_mem("", data, len);
+
+       return data;
+}
+
+DECLARE_USB_FUNCTION_INIT(ffs, ffs_alloc_inst, ffs_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michal Nazarewicz");
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
new file mode 100644 (file)
index 0000000..a95290a
--- /dev/null
@@ -0,0 +1,763 @@
+/*
+ * f_hid.c -- USB HID function driver
+ *
+ * Copyright (C) 2010 Fabien Chouteau <fabien.chouteau@barco.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/hid.h>
+#include <linux/cdev.h>
+#include <linux/mutex.h>
+#include <linux/poll.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/usb/g_hid.h>
+
+#include "u_f.h"
+
+static int major, minors;
+static struct class *hidg_class;
+
+/*-------------------------------------------------------------------------*/
+/*                            HID gadget struct                            */
+
+struct f_hidg_req_list {
+       struct usb_request      *req;
+       unsigned int            pos;
+       struct list_head        list;
+};
+
+struct f_hidg {
+       /* configuration */
+       unsigned char                   bInterfaceSubClass;
+       unsigned char                   bInterfaceProtocol;
+       unsigned short                  report_desc_length;
+       char                            *report_desc;
+       unsigned short                  report_length;
+
+       /* recv report */
+       struct list_head                completed_out_req;
+       spinlock_t                      spinlock;
+       wait_queue_head_t               read_queue;
+       unsigned int                    qlen;
+
+       /* send report */
+       struct mutex                    lock;
+       bool                            write_pending;
+       wait_queue_head_t               write_queue;
+       struct usb_request              *req;
+
+       int                             minor;
+       struct cdev                     cdev;
+       struct usb_function             func;
+
+       struct usb_ep                   *in_ep;
+       struct usb_ep                   *out_ep;
+};
+
+static inline struct f_hidg *func_to_hidg(struct usb_function *f)
+{
+       return container_of(f, struct f_hidg, func);
+}
+
+/*-------------------------------------------------------------------------*/
+/*                           Static descriptors                            */
+
+static struct usb_interface_descriptor hidg_interface_desc = {
+       .bLength                = sizeof hidg_interface_desc,
+       .bDescriptorType        = USB_DT_INTERFACE,
+       /* .bInterfaceNumber    = DYNAMIC */
+       .bAlternateSetting      = 0,
+       .bNumEndpoints          = 2,
+       .bInterfaceClass        = USB_CLASS_HID,
+       /* .bInterfaceSubClass  = DYNAMIC */
+       /* .bInterfaceProtocol  = DYNAMIC */
+       /* .iInterface          = DYNAMIC */
+};
+
+static struct hid_descriptor hidg_desc = {
+       .bLength                        = sizeof hidg_desc,
+       .bDescriptorType                = HID_DT_HID,
+       .bcdHID                         = 0x0101,
+       .bCountryCode                   = 0x00,
+       .bNumDescriptors                = 0x1,
+       /*.desc[0].bDescriptorType      = DYNAMIC */
+       /*.desc[0].wDescriptorLenght    = DYNAMIC */
+};
+
+/* High-Speed Support */
+
+static struct usb_endpoint_descriptor hidg_hs_in_ep_desc = {
+       .bLength                = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType        = USB_DT_ENDPOINT,
+       .bEndpointAddress       = USB_DIR_IN,
+       .bmAttributes           = USB_ENDPOINT_XFER_INT,
+       /*.wMaxPacketSize       = DYNAMIC */
+       .bInterval              = 4, /* FIXME: Add this field in the
+                                     * HID gadget configuration?
+                                     * (struct hidg_func_descriptor)
+                                     */
+};
+
+static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = {
+       .bLength                = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType        = USB_DT_ENDPOINT,
+       .bEndpointAddress       = USB_DIR_OUT,
+       .bmAttributes           = USB_ENDPOINT_XFER_INT,
+       /*.wMaxPacketSize       = DYNAMIC */
+       .bInterval              = 4, /* FIXME: Add this field in the
+                                     * HID gadget configuration?
+                                     * (struct hidg_func_descriptor)
+                                     */
+};
+
+static struct usb_descriptor_header *hidg_hs_descriptors[] = {
+       (struct usb_descriptor_header *)&hidg_interface_desc,
+       (struct usb_descriptor_header *)&hidg_desc,
+       (struct usb_descriptor_header *)&hidg_hs_in_ep_desc,
+       (struct usb_descriptor_header *)&hidg_hs_out_ep_desc,
+       NULL,
+};
+
+/* Full-Speed Support */
+
+static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = {
+       .bLength                = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType        = USB_DT_ENDPOINT,
+       .bEndpointAddress       = USB_DIR_IN,
+       .bmAttributes           = USB_ENDPOINT_XFER_INT,
+       /*.wMaxPacketSize       = DYNAMIC */
+       .bInterval              = 10, /* FIXME: Add this field in the
+                                      * HID gadget configuration?
+                                      * (struct hidg_func_descriptor)
+                                      */
+};
+
+static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = {
+       .bLength                = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType        = USB_DT_ENDPOINT,
+       .bEndpointAddress       = USB_DIR_OUT,
+       .bmAttributes           = USB_ENDPOINT_XFER_INT,
+       /*.wMaxPacketSize       = DYNAMIC */
+       .bInterval              = 10, /* FIXME: Add this field in the
+                                      * HID gadget configuration?
+                                      * (struct hidg_func_descriptor)
+                                      */
+};
+
+static struct usb_descriptor_header *hidg_fs_descriptors[] = {
+       (struct usb_descriptor_header *)&hidg_interface_desc,
+       (struct usb_descriptor_header *)&hidg_desc,
+       (struct usb_descriptor_header *)&hidg_fs_in_ep_desc,
+       (struct usb_descriptor_header *)&hidg_fs_out_ep_desc,
+       NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+/*                              Char Device                                */
+
+static ssize_t f_hidg_read(struct file *file, char __user *buffer,
+                       size_t count, loff_t *ptr)
+{
+       struct f_hidg *hidg = file->private_data;
+       struct f_hidg_req_list *list;
+       struct usb_request *req;
+       unsigned long flags;
+       int ret;
+
+       if (!count)
+               return 0;
+
+       if (!access_ok(VERIFY_WRITE, buffer, count))
+               return -EFAULT;
+
+       spin_lock_irqsave(&hidg->spinlock, flags);
+
+#define READ_COND (!list_empty(&hidg->completed_out_req))
+
+       /* wait for at least one buffer to complete */
+       while (!READ_COND) {
+               spin_unlock_irqrestore(&hidg->spinlock, flags);
+               if (file->f_flags & O_NONBLOCK)
+                       return -EAGAIN;
+
+               if (wait_event_interruptible(hidg->read_queue, READ_COND))
+                       return -ERESTARTSYS;
+
+               spin_lock_irqsave(&hidg->spinlock, flags);
+       }
+
+       /* pick the first one */
+       list = list_first_entry(&hidg->completed_out_req,
+                               struct f_hidg_req_list, list);
+       req = list->req;
+       count = min_t(unsigned int, count, req->actual - list->pos);
+       spin_unlock_irqrestore(&hidg->spinlock, flags);
+
+       /* copy to user outside spinlock */
+       count -= copy_to_user(buffer, req->buf + list->pos, count);
+       list->pos += count;
+
+       /*
+        * if this request is completely handled and transfered to
+        * userspace, remove its entry from the list and requeue it
+        * again. Otherwise, we will revisit it again upon the next
+        * call, taking into account its current read position.
+        */
+       if (list->pos == req->actual) {
+               spin_lock_irqsave(&hidg->spinlock, flags);
+               list_del(&list->list);
+               kfree(list);
+               spin_unlock_irqrestore(&hidg->spinlock, flags);
+
+               req->length = hidg->report_length;
+               ret = usb_ep_queue(hidg->out_ep, req, GFP_KERNEL);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return count;
+}
+
+static void f_hidg_req_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct f_hidg *hidg = (struct f_hidg *)ep->driver_data;
+
+       if (req->status != 0) {
+               ERROR(hidg->func.config->cdev,
+                       "End Point Request ERROR: %d\n", req->status);
+       }
+
+       hidg->write_pending = 0;
+       wake_up(&hidg->write_queue);
+}
+
+static ssize_t f_hidg_write(struct file *file, const char __user *buffer,
+                           size_t count, loff_t *offp)
+{
+       struct f_hidg *hidg  = file->private_data;
+       ssize_t status = -ENOMEM;
+
+       if (!access_ok(VERIFY_READ, buffer, count))
+               return -EFAULT;
+
+       mutex_lock(&hidg->lock);
+
+#define WRITE_COND (!hidg->write_pending)
+
+       /* write queue */
+       while (!WRITE_COND) {
+               mutex_unlock(&hidg->lock);
+               if (file->f_flags & O_NONBLOCK)
+                       return -EAGAIN;
+
+               if (wait_event_interruptible_exclusive(
+                               hidg->write_queue, WRITE_COND))
+                       return -ERESTARTSYS;
+
+               mutex_lock(&hidg->lock);
+       }
+
+       count  = min_t(unsigned, count, hidg->report_length);
+       status = copy_from_user(hidg->req->buf, buffer, count);
+
+       if (status != 0) {
+               ERROR(hidg->func.config->cdev,
+                       "copy_from_user error\n");
+               mutex_unlock(&hidg->lock);
+               return -EINVAL;
+       }
+
+       hidg->req->status   = 0;
+       hidg->req->zero     = 0;
+       hidg->req->length   = count;
+       hidg->req->complete = f_hidg_req_complete;
+       hidg->req->context  = hidg;
+       hidg->write_pending = 1;
+
+       status = usb_ep_queue(hidg->in_ep, hidg->req, GFP_ATOMIC);
+       if (status < 0) {
+               ERROR(hidg->func.config->cdev,
+                       "usb_ep_queue error on int endpoint %zd\n", status);
+               hidg->write_pending = 0;
+               wake_up(&hidg->write_queue);
+       } else {
+               status = count;
+       }
+
+       mutex_unlock(&hidg->lock);
+
+       return status;
+}
+
+static unsigned int f_hidg_poll(struct file *file, poll_table *wait)
+{
+       struct f_hidg   *hidg  = file->private_data;
+       unsigned int    ret = 0;
+
+       poll_wait(file, &hidg->read_queue, wait);
+       poll_wait(file, &hidg->write_queue, wait);
+
+       if (WRITE_COND)
+               ret |= POLLOUT | POLLWRNORM;
+
+       if (READ_COND)
+               ret |= POLLIN | POLLRDNORM;
+
+       return ret;
+}
+
+#undef WRITE_COND
+#undef READ_COND
+
+static int f_hidg_release(struct inode *inode, struct file *fd)
+{
+       fd->private_data = NULL;
+       return 0;
+}
+
+static int f_hidg_open(struct inode *inode, struct file *fd)
+{
+       struct f_hidg *hidg =
+               container_of(inode->i_cdev, struct f_hidg, cdev);
+
+       fd->private_data = hidg;
+
+       return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+/*                                usb_function                             */
+
+static inline struct usb_request *hidg_alloc_ep_req(struct usb_ep *ep,
+                                                   unsigned length)
+{
+       return alloc_ep_req(ep, length, length);
+}
+
+static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct f_hidg *hidg = (struct f_hidg *) req->context;
+       struct f_hidg_req_list *req_list;
+       unsigned long flags;
+
+       req_list = kzalloc(sizeof(*req_list), GFP_ATOMIC);
+       if (!req_list)
+               return;
+
+       req_list->req = req;
+
+       spin_lock_irqsave(&hidg->spinlock, flags);
+       list_add_tail(&req_list->list, &hidg->completed_out_req);
+       spin_unlock_irqrestore(&hidg->spinlock, flags);
+
+       wake_up(&hidg->read_queue);
+}
+
+static int hidg_setup(struct usb_function *f,
+               const struct usb_ctrlrequest *ctrl)
+{
+       struct f_hidg                   *hidg = func_to_hidg(f);
+       struct usb_composite_dev        *cdev = f->config->cdev;
+       struct usb_request              *req  = cdev->req;
+       int status = 0;
+       __u16 value, length;
+
+       value   = __le16_to_cpu(ctrl->wValue);
+       length  = __le16_to_cpu(ctrl->wLength);
+
+       VDBG(cdev, "hid_setup crtl_request : bRequestType:0x%x bRequest:0x%x "
+               "Value:0x%x\n", ctrl->bRequestType, ctrl->bRequest, value);
+
+       switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+       case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
+                 | HID_REQ_GET_REPORT):
+               VDBG(cdev, "get_report\n");
+
+               /* send an empty report */
+               length = min_t(unsigned, length, hidg->report_length);
+               memset(req->buf, 0x0, length);
+
+               goto respond;
+               break;
+
+       case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
+                 | HID_REQ_GET_PROTOCOL):
+               VDBG(cdev, "get_protocol\n");
+               goto stall;
+               break;
+
+       case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
+                 | HID_REQ_SET_REPORT):
+               VDBG(cdev, "set_report | wLenght=%d\n", ctrl->wLength);
+               goto stall;
+               break;
+
+       case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
+                 | HID_REQ_SET_PROTOCOL):
+               VDBG(cdev, "set_protocol\n");
+               goto stall;
+               break;
+
+       case ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8
+                 | USB_REQ_GET_DESCRIPTOR):
+               switch (value >> 8) {
+               case HID_DT_HID:
+                       VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: HID\n");
+                       length = min_t(unsigned short, length,
+                                                  hidg_desc.bLength);
+                       memcpy(req->buf, &hidg_desc, length);
+                       goto respond;
+                       break;
+               case HID_DT_REPORT:
+                       VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: REPORT\n");
+                       length = min_t(unsigned short, length,
+                                                  hidg->report_desc_length);
+                       memcpy(req->buf, hidg->report_desc, length);
+                       goto respond;
+                       break;
+
+               default:
+                       VDBG(cdev, "Unknown descriptor request 0x%x\n",
+                                value >> 8);
+                       goto stall;
+                       break;
+               }
+               break;
+
+       default:
+               VDBG(cdev, "Unknown request 0x%x\n",
+                        ctrl->bRequest);
+               goto stall;
+               break;
+       }
+
+stall:
+       return -EOPNOTSUPP;
+
+respond:
+       req->zero = 0;
+       req->length = length;
+       status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+       if (status < 0)
+               ERROR(cdev, "usb_ep_queue error on ep0 %d\n", value);
+       return status;
+}
+
+static void hidg_disable(struct usb_function *f)
+{
+       struct f_hidg *hidg = func_to_hidg(f);
+       struct f_hidg_req_list *list, *next;
+
+       usb_ep_disable(hidg->in_ep);
+       hidg->in_ep->driver_data = NULL;
+
+       usb_ep_disable(hidg->out_ep);
+       hidg->out_ep->driver_data = NULL;
+
+       list_for_each_entry_safe(list, next, &hidg->completed_out_req, list) {
+               list_del(&list->list);
+               kfree(list);
+       }
+}
+
+static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+       struct usb_composite_dev                *cdev = f->config->cdev;
+       struct f_hidg                           *hidg = func_to_hidg(f);
+       int i, status = 0;
+
+       VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt);
+
+       if (hidg->in_ep != NULL) {
+               /* restart endpoint */
+               if (hidg->in_ep->driver_data != NULL)
+                       usb_ep_disable(hidg->in_ep);
+
+               status = config_ep_by_speed(f->config->cdev->gadget, f,
+                                           hidg->in_ep);
+               if (status) {
+                       ERROR(cdev, "config_ep_by_speed FAILED!\n");
+                       goto fail;
+               }
+               status = usb_ep_enable(hidg->in_ep);
+               if (status < 0) {
+                       ERROR(cdev, "Enable IN endpoint FAILED!\n");
+                       goto fail;
+               }
+               hidg->in_ep->driver_data = hidg;
+       }
+
+
+       if (hidg->out_ep != NULL) {
+               /* restart endpoint */
+               if (hidg->out_ep->driver_data != NULL)
+                       usb_ep_disable(hidg->out_ep);
+
+               status = config_ep_by_speed(f->config->cdev->gadget, f,
+                                           hidg->out_ep);
+               if (status) {
+                       ERROR(cdev, "config_ep_by_speed FAILED!\n");
+                       goto fail;
+               }
+               status = usb_ep_enable(hidg->out_ep);
+               if (status < 0) {
+                       ERROR(cdev, "Enable IN endpoint FAILED!\n");
+                       goto fail;
+               }
+               hidg->out_ep->driver_data = hidg;
+
+               /*
+                * allocate a bunch of read buffers and queue them all at once.
+                */
+               for (i = 0; i < hidg->qlen && status == 0; i++) {
+                       struct usb_request *req =
+                                       hidg_alloc_ep_req(hidg->out_ep,
+                                                         hidg->report_length);
+                       if (req) {
+                               req->complete = hidg_set_report_complete;
+                               req->context  = hidg;
+                               status = usb_ep_queue(hidg->out_ep, req,
+                                                     GFP_ATOMIC);
+                               if (status)
+                                       ERROR(cdev, "%s queue req --> %d\n",
+                                               hidg->out_ep->name, status);
+                       } else {
+                               usb_ep_disable(hidg->out_ep);
+                               hidg->out_ep->driver_data = NULL;
+                               status = -ENOMEM;
+                               goto fail;
+                       }
+               }
+       }
+
+fail:
+       return status;
+}
+
+const struct file_operations f_hidg_fops = {
+       .owner          = THIS_MODULE,
+       .open           = f_hidg_open,
+       .release        = f_hidg_release,
+       .write          = f_hidg_write,
+       .read           = f_hidg_read,
+       .poll           = f_hidg_poll,
+       .llseek         = noop_llseek,
+};
+
+static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct usb_ep           *ep;
+       struct f_hidg           *hidg = func_to_hidg(f);
+       int                     status;
+       dev_t                   dev;
+
+       /* allocate instance-specific interface IDs, and patch descriptors */
+       status = usb_interface_id(c, f);
+       if (status < 0)
+               goto fail;
+       hidg_interface_desc.bInterfaceNumber = status;
+
+       /* allocate instance-specific endpoints */
+       status = -ENODEV;
+       ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_in_ep_desc);
+       if (!ep)
+               goto fail;
+       ep->driver_data = c->cdev;      /* claim */
+       hidg->in_ep = ep;
+
+       ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_out_ep_desc);
+       if (!ep)
+               goto fail;
+       ep->driver_data = c->cdev;      /* claim */
+       hidg->out_ep = ep;
+
+       /* preallocate request and buffer */
+       status = -ENOMEM;
+       hidg->req = usb_ep_alloc_request(hidg->in_ep, GFP_KERNEL);
+       if (!hidg->req)
+               goto fail;
+
+       hidg->req->buf = kmalloc(hidg->report_length, GFP_KERNEL);
+       if (!hidg->req->buf)
+               goto fail;
+
+       /* set descriptor dynamic values */
+       hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass;
+       hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol;
+       hidg_hs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
+       hidg_fs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
+       hidg_hs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
+       hidg_fs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
+       hidg_desc.desc[0].bDescriptorType = HID_DT_REPORT;
+       hidg_desc.desc[0].wDescriptorLength =
+               cpu_to_le16(hidg->report_desc_length);
+
+       hidg_hs_in_ep_desc.bEndpointAddress =
+               hidg_fs_in_ep_desc.bEndpointAddress;
+       hidg_hs_out_ep_desc.bEndpointAddress =
+               hidg_fs_out_ep_desc.bEndpointAddress;
+
+       status = usb_assign_descriptors(f, hidg_fs_descriptors,
+                       hidg_hs_descriptors, NULL);
+       if (status)
+               goto fail;
+
+       mutex_init(&hidg->lock);
+       spin_lock_init(&hidg->spinlock);
+       init_waitqueue_head(&hidg->write_queue);
+       init_waitqueue_head(&hidg->read_queue);
+       INIT_LIST_HEAD(&hidg->completed_out_req);
+
+       /* create char device */
+       cdev_init(&hidg->cdev, &f_hidg_fops);
+       dev = MKDEV(major, hidg->minor);
+       status = cdev_add(&hidg->cdev, dev, 1);
+       if (status)
+               goto fail;
+
+       device_create(hidg_class, NULL, dev, NULL, "%s%d", "hidg", hidg->minor);
+
+       return 0;
+
+fail:
+       ERROR(f->config->cdev, "hidg_bind FAILED\n");
+       if (hidg->req != NULL) {
+               kfree(hidg->req->buf);
+               if (hidg->in_ep != NULL)
+                       usb_ep_free_request(hidg->in_ep, hidg->req);
+       }
+
+       usb_free_all_descriptors(f);
+       return status;
+}
+
+static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct f_hidg *hidg = func_to_hidg(f);
+
+       device_destroy(hidg_class, MKDEV(major, hidg->minor));
+       cdev_del(&hidg->cdev);
+
+       /* disable/free request and end point */
+       usb_ep_disable(hidg->in_ep);
+       usb_ep_dequeue(hidg->in_ep, hidg->req);
+       kfree(hidg->req->buf);
+       usb_ep_free_request(hidg->in_ep, hidg->req);
+
+       usb_free_all_descriptors(f);
+
+       kfree(hidg->report_desc);
+       kfree(hidg);
+}
+
+/*-------------------------------------------------------------------------*/
+/*                                 Strings                                 */
+
+#define CT_FUNC_HID_IDX        0
+
+static struct usb_string ct_func_string_defs[] = {
+       [CT_FUNC_HID_IDX].s     = "HID Interface",
+       {},                     /* end of list */
+};
+
+static struct usb_gadget_strings ct_func_string_table = {
+       .language       = 0x0409,       /* en-US */
+       .strings        = ct_func_string_defs,
+};
+
+static struct usb_gadget_strings *ct_func_strings[] = {
+       &ct_func_string_table,
+       NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+/*                             usb_configuration                           */
+
+int __init hidg_bind_config(struct usb_configuration *c,
+                           struct hidg_func_descriptor *fdesc, int index)
+{
+       struct f_hidg *hidg;
+       int status;
+
+       if (index >= minors)
+               return -ENOENT;
+
+       /* maybe allocate device-global string IDs, and patch descriptors */
+       if (ct_func_string_defs[CT_FUNC_HID_IDX].id == 0) {
+               status = usb_string_id(c->cdev);
+               if (status < 0)
+                       return status;
+               ct_func_string_defs[CT_FUNC_HID_IDX].id = status;
+               hidg_interface_desc.iInterface = status;
+       }
+
+       /* allocate and initialize one new instance */
+       hidg = kzalloc(sizeof *hidg, GFP_KERNEL);
+       if (!hidg)
+               return -ENOMEM;
+
+       hidg->minor = index;
+       hidg->bInterfaceSubClass = fdesc->subclass;
+       hidg->bInterfaceProtocol = fdesc->protocol;
+       hidg->report_length = fdesc->report_length;
+       hidg->report_desc_length = fdesc->report_desc_length;
+       hidg->report_desc = kmemdup(fdesc->report_desc,
+                                   fdesc->report_desc_length,
+                                   GFP_KERNEL);
+       if (!hidg->report_desc) {
+               kfree(hidg);
+               return -ENOMEM;
+       }
+
+       hidg->func.name    = "hid";
+       hidg->func.strings = ct_func_strings;
+       hidg->func.bind    = hidg_bind;
+       hidg->func.unbind  = hidg_unbind;
+       hidg->func.set_alt = hidg_set_alt;
+       hidg->func.disable = hidg_disable;
+       hidg->func.setup   = hidg_setup;
+
+       /* this could me made configurable at some point */
+       hidg->qlen         = 4;
+
+       status = usb_add_function(c, &hidg->func);
+       if (status)
+               kfree(hidg);
+
+       return status;
+}
+
+int __init ghid_setup(struct usb_gadget *g, int count)
+{
+       int status;
+       dev_t dev;
+
+       hidg_class = class_create(THIS_MODULE, "hidg");
+
+       status = alloc_chrdev_region(&dev, 0, count, "hidg");
+       if (!status) {
+               major = MAJOR(dev);
+               minors = count;
+       }
+
+       return status;
+}
+
+void ghid_cleanup(void)
+{
+       if (major) {
+               unregister_chrdev_region(MKDEV(major, 0), minors);
+               major = minors = 0;
+       }
+
+       class_destroy(hidg_class);
+       hidg_class = NULL;
+}
diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c
new file mode 100644 (file)
index 0000000..4557cd0
--- /dev/null
@@ -0,0 +1,571 @@
+/*
+ * f_loopback.c - USB peripheral loopback configuration driver
+ *
+ * Copyright (C) 2003-2008 David Brownell
+ * Copyright (C) 2008 by Nokia Corporation
+ *
+ * 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.
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/usb/composite.h>
+
+#include "g_zero.h"
+#include "u_f.h"
+
+/*
+ * LOOPBACK FUNCTION ... a testing vehicle for USB peripherals,
+ *
+ * This takes messages of various sizes written OUT to a device, and loops
+ * them back so they can be read IN from it.  It has been used by certain
+ * test applications.  It supports limited testing of data queueing logic.
+ *
+ *
+ * This is currently packaged as a configuration driver, which can't be
+ * combined with other functions to make composite devices.  However, it
+ * can be combined with other independent configurations.
+ */
+struct f_loopback {
+       struct usb_function     function;
+
+       struct usb_ep           *in_ep;
+       struct usb_ep           *out_ep;
+};
+
+static inline struct f_loopback *func_to_loop(struct usb_function *f)
+{
+       return container_of(f, struct f_loopback, function);
+}
+
+static unsigned qlen;
+static unsigned buflen;
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_interface_descriptor loopback_intf = {
+       .bLength =              sizeof loopback_intf,
+       .bDescriptorType =      USB_DT_INTERFACE,
+
+       .bNumEndpoints =        2,
+       .bInterfaceClass =      USB_CLASS_VENDOR_SPEC,
+       /* .iInterface = DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor fs_loop_source_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor fs_loop_sink_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *fs_loopback_descs[] = {
+       (struct usb_descriptor_header *) &loopback_intf,
+       (struct usb_descriptor_header *) &fs_loop_sink_desc,
+       (struct usb_descriptor_header *) &fs_loop_source_desc,
+       NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor hs_loop_source_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor hs_loop_sink_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *hs_loopback_descs[] = {
+       (struct usb_descriptor_header *) &loopback_intf,
+       (struct usb_descriptor_header *) &hs_loop_source_desc,
+       (struct usb_descriptor_header *) &hs_loop_sink_desc,
+       NULL,
+};
+
+/* super speed support: */
+
+static struct usb_endpoint_descriptor ss_loop_source_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ss_loop_source_comp_desc = {
+       .bLength =              USB_DT_SS_EP_COMP_SIZE,
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+       .bMaxBurst =            0,
+       .bmAttributes =         0,
+       .wBytesPerInterval =    0,
+};
+
+static struct usb_endpoint_descriptor ss_loop_sink_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ss_loop_sink_comp_desc = {
+       .bLength =              USB_DT_SS_EP_COMP_SIZE,
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+       .bMaxBurst =            0,
+       .bmAttributes =         0,
+       .wBytesPerInterval =    0,
+};
+
+static struct usb_descriptor_header *ss_loopback_descs[] = {
+       (struct usb_descriptor_header *) &loopback_intf,
+       (struct usb_descriptor_header *) &ss_loop_source_desc,
+       (struct usb_descriptor_header *) &ss_loop_source_comp_desc,
+       (struct usb_descriptor_header *) &ss_loop_sink_desc,
+       (struct usb_descriptor_header *) &ss_loop_sink_comp_desc,
+       NULL,
+};
+
+/* function-specific strings: */
+
+static struct usb_string strings_loopback[] = {
+       [0].s = "loop input to output",
+       {  }                    /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_loop = {
+       .language       = 0x0409,       /* en-us */
+       .strings        = strings_loopback,
+};
+
+static struct usb_gadget_strings *loopback_strings[] = {
+       &stringtab_loop,
+       NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int loopback_bind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct usb_composite_dev *cdev = c->cdev;
+       struct f_loopback       *loop = func_to_loop(f);
+       int                     id;
+       int ret;
+
+       /* allocate interface ID(s) */
+       id = usb_interface_id(c, f);
+       if (id < 0)
+               return id;
+       loopback_intf.bInterfaceNumber = id;
+
+       id = usb_string_id(cdev);
+       if (id < 0)
+               return id;
+       strings_loopback[0].id = id;
+       loopback_intf.iInterface = id;
+
+       /* allocate endpoints */
+
+       loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_source_desc);
+       if (!loop->in_ep) {
+autoconf_fail:
+               ERROR(cdev, "%s: can't autoconfigure on %s\n",
+                       f->name, cdev->gadget->name);
+               return -ENODEV;
+       }
+       loop->in_ep->driver_data = cdev;        /* claim */
+
+       loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_sink_desc);
+       if (!loop->out_ep)
+               goto autoconf_fail;
+       loop->out_ep->driver_data = cdev;       /* claim */
+
+       /* support high speed hardware */
+       hs_loop_source_desc.bEndpointAddress =
+               fs_loop_source_desc.bEndpointAddress;
+       hs_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress;
+
+       /* support super speed hardware */
+       ss_loop_source_desc.bEndpointAddress =
+               fs_loop_source_desc.bEndpointAddress;
+       ss_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress;
+
+       ret = usb_assign_descriptors(f, fs_loopback_descs, hs_loopback_descs,
+                       ss_loopback_descs);
+       if (ret)
+               return ret;
+
+       DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
+           (gadget_is_superspeed(c->cdev->gadget) ? "super" :
+            (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")),
+                       f->name, loop->in_ep->name, loop->out_ep->name);
+       return 0;
+}
+
+static void lb_free_func(struct usb_function *f)
+{
+       struct f_lb_opts *opts;
+
+       opts = container_of(f->fi, struct f_lb_opts, func_inst);
+
+       mutex_lock(&opts->lock);
+       opts->refcnt--;
+       mutex_unlock(&opts->lock);
+
+       usb_free_all_descriptors(f);
+       kfree(func_to_loop(f));
+}
+
+static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct f_loopback       *loop = ep->driver_data;
+       struct usb_composite_dev *cdev = loop->function.config->cdev;
+       int                     status = req->status;
+
+       switch (status) {
+
+       case 0:                         /* normal completion? */
+               if (ep == loop->out_ep) {
+                       /* loop this OUT packet back IN to the host */
+                       req->zero = (req->actual < req->length);
+                       req->length = req->actual;
+                       status = usb_ep_queue(loop->in_ep, req, GFP_ATOMIC);
+                       if (status == 0)
+                               return;
+
+                       /* "should never get here" */
+                       ERROR(cdev, "can't loop %s to %s: %d\n",
+                               ep->name, loop->in_ep->name,
+                               status);
+               }
+
+               /* queue the buffer for some later OUT packet */
+               req->length = buflen;
+               status = usb_ep_queue(loop->out_ep, req, GFP_ATOMIC);
+               if (status == 0)
+                       return;
+
+               /* "should never get here" */
+               /* FALLTHROUGH */
+
+       default:
+               ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name,
+                               status, req->actual, req->length);
+               /* FALLTHROUGH */
+
+       /* NOTE:  since this driver doesn't maintain an explicit record
+        * of requests it submitted (just maintains qlen count), we
+        * rely on the hardware driver to clean up on disconnect or
+        * endpoint disable.
+        */
+       case -ECONNABORTED:             /* hardware forced ep reset */
+       case -ECONNRESET:               /* request dequeued */
+       case -ESHUTDOWN:                /* disconnect from host */
+               free_ep_req(ep, req);
+               return;
+       }
+}
+
+static void disable_loopback(struct f_loopback *loop)
+{
+       struct usb_composite_dev        *cdev;
+
+       cdev = loop->function.config->cdev;
+       disable_endpoints(cdev, loop->in_ep, loop->out_ep, NULL, NULL);
+       VDBG(cdev, "%s disabled\n", loop->function.name);
+}
+
+static inline struct usb_request *lb_alloc_ep_req(struct usb_ep *ep, int len)
+{
+       return alloc_ep_req(ep, len, buflen);
+}
+
+static int
+enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop)
+{
+       int                                     result = 0;
+       struct usb_ep                           *ep;
+       struct usb_request                      *req;
+       unsigned                                i;
+
+       /* one endpoint writes data back IN to the host */
+       ep = loop->in_ep;
+       result = config_ep_by_speed(cdev->gadget, &(loop->function), ep);
+       if (result)
+               return result;
+       result = usb_ep_enable(ep);
+       if (result < 0)
+               return result;
+       ep->driver_data = loop;
+
+       /* one endpoint just reads OUT packets */
+       ep = loop->out_ep;
+       result = config_ep_by_speed(cdev->gadget, &(loop->function), ep);
+       if (result)
+               goto fail0;
+
+       result = usb_ep_enable(ep);
+       if (result < 0) {
+fail0:
+               ep = loop->in_ep;
+               usb_ep_disable(ep);
+               ep->driver_data = NULL;
+               return result;
+       }
+       ep->driver_data = loop;
+
+       /* allocate a bunch of read buffers and queue them all at once.
+        * we buffer at most 'qlen' transfers; fewer if any need more
+        * than 'buflen' bytes each.
+        */
+       for (i = 0; i < qlen && result == 0; i++) {
+               req = lb_alloc_ep_req(ep, 0);
+               if (req) {
+                       req->complete = loopback_complete;
+                       result = usb_ep_queue(ep, req, GFP_ATOMIC);
+                       if (result)
+                               ERROR(cdev, "%s queue req --> %d\n",
+                                               ep->name, result);
+               } else {
+                       usb_ep_disable(ep);
+                       ep->driver_data = NULL;
+                       result = -ENOMEM;
+                       goto fail0;
+               }
+       }
+
+       DBG(cdev, "%s enabled\n", loop->function.name);
+       return result;
+}
+
+static int loopback_set_alt(struct usb_function *f,
+               unsigned intf, unsigned alt)
+{
+       struct f_loopback       *loop = func_to_loop(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+
+       /* we know alt is zero */
+       if (loop->in_ep->driver_data)
+               disable_loopback(loop);
+       return enable_loopback(cdev, loop);
+}
+
+static void loopback_disable(struct usb_function *f)
+{
+       struct f_loopback       *loop = func_to_loop(f);
+
+       disable_loopback(loop);
+}
+
+static struct usb_function *loopback_alloc(struct usb_function_instance *fi)
+{
+       struct f_loopback       *loop;
+       struct f_lb_opts        *lb_opts;
+
+       loop = kzalloc(sizeof *loop, GFP_KERNEL);
+       if (!loop)
+               return ERR_PTR(-ENOMEM);
+
+       lb_opts = container_of(fi, struct f_lb_opts, func_inst);
+
+       mutex_lock(&lb_opts->lock);
+       lb_opts->refcnt++;
+       mutex_unlock(&lb_opts->lock);
+
+       buflen = lb_opts->bulk_buflen;
+       qlen = lb_opts->qlen;
+       if (!qlen)
+               qlen = 32;
+
+       loop->function.name = "loopback";
+       loop->function.bind = loopback_bind;
+       loop->function.set_alt = loopback_set_alt;
+       loop->function.disable = loopback_disable;
+       loop->function.strings = loopback_strings;
+
+       loop->function.free_func = lb_free_func;
+
+       return &loop->function;
+}
+
+static inline struct f_lb_opts *to_f_lb_opts(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct f_lb_opts,
+                           func_inst.group);
+}
+
+CONFIGFS_ATTR_STRUCT(f_lb_opts);
+CONFIGFS_ATTR_OPS(f_lb_opts);
+
+static void lb_attr_release(struct config_item *item)
+{
+       struct f_lb_opts *lb_opts = to_f_lb_opts(item);
+
+       usb_put_function_instance(&lb_opts->func_inst);
+}
+
+static struct configfs_item_operations lb_item_ops = {
+       .release                = lb_attr_release,
+       .show_attribute         = f_lb_opts_attr_show,
+       .store_attribute        = f_lb_opts_attr_store,
+};
+
+static ssize_t f_lb_opts_qlen_show(struct f_lb_opts *opts, char *page)
+{
+       int result;
+
+       mutex_lock(&opts->lock);
+       result = sprintf(page, "%d", opts->qlen);
+       mutex_unlock(&opts->lock);
+
+       return result;
+}
+
+static ssize_t f_lb_opts_qlen_store(struct f_lb_opts *opts,
+                                   const char *page, size_t len)
+{
+       int ret;
+       u32 num;
+
+       mutex_lock(&opts->lock);
+       if (opts->refcnt) {
+               ret = -EBUSY;
+               goto end;
+       }
+
+       ret = kstrtou32(page, 0, &num);
+       if (ret)
+               goto end;
+
+       opts->qlen = num;
+       ret = len;
+end:
+       mutex_unlock(&opts->lock);
+       return ret;
+}
+
+static struct f_lb_opts_attribute f_lb_opts_qlen =
+       __CONFIGFS_ATTR(qlen, S_IRUGO | S_IWUSR,
+                       f_lb_opts_qlen_show,
+                       f_lb_opts_qlen_store);
+
+static ssize_t f_lb_opts_bulk_buflen_show(struct f_lb_opts *opts, char *page)
+{
+       int result;
+
+       mutex_lock(&opts->lock);
+       result = sprintf(page, "%d", opts->bulk_buflen);
+       mutex_unlock(&opts->lock);
+
+       return result;
+}
+
+static ssize_t f_lb_opts_bulk_buflen_store(struct f_lb_opts *opts,
+                                   const char *page, size_t len)
+{
+       int ret;
+       u32 num;
+
+       mutex_lock(&opts->lock);
+       if (opts->refcnt) {
+               ret = -EBUSY;
+               goto end;
+       }
+
+       ret = kstrtou32(page, 0, &num);
+       if (ret)
+               goto end;
+
+       opts->bulk_buflen = num;
+       ret = len;
+end:
+       mutex_unlock(&opts->lock);
+       return ret;
+}
+
+static struct f_lb_opts_attribute f_lb_opts_bulk_buflen =
+       __CONFIGFS_ATTR(buflen, S_IRUGO | S_IWUSR,
+                       f_lb_opts_bulk_buflen_show,
+                       f_lb_opts_bulk_buflen_store);
+
+static struct configfs_attribute *lb_attrs[] = {
+       &f_lb_opts_qlen.attr,
+       &f_lb_opts_bulk_buflen.attr,
+       NULL,
+};
+
+static struct config_item_type lb_func_type = {
+       .ct_item_ops    = &lb_item_ops,
+       .ct_attrs       = lb_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+static void lb_free_instance(struct usb_function_instance *fi)
+{
+       struct f_lb_opts *lb_opts;
+
+       lb_opts = container_of(fi, struct f_lb_opts, func_inst);
+       kfree(lb_opts);
+}
+
+static struct usb_function_instance *loopback_alloc_instance(void)
+{
+       struct f_lb_opts *lb_opts;
+
+       lb_opts = kzalloc(sizeof(*lb_opts), GFP_KERNEL);
+       if (!lb_opts)
+               return ERR_PTR(-ENOMEM);
+       mutex_init(&lb_opts->lock);
+       lb_opts->func_inst.free_func_inst = lb_free_instance;
+       lb_opts->bulk_buflen = GZERO_BULK_BUFLEN;
+       lb_opts->qlen = GZERO_QLEN;
+
+       config_group_init_type_name(&lb_opts->func_inst.group, "",
+                                   &lb_func_type);
+
+       return  &lb_opts->func_inst;
+}
+DECLARE_USB_FUNCTION(Loopback, loopback_alloc_instance, loopback_alloc);
+
+int __init lb_modinit(void)
+{
+       int ret;
+
+       ret = usb_function_register(&Loopbackusb_func);
+       if (ret)
+               return ret;
+       return ret;
+}
+void __exit lb_modexit(void)
+{
+       usb_function_unregister(&Loopbackusb_func);
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
new file mode 100644 (file)
index 0000000..b963939
--- /dev/null
@@ -0,0 +1,3668 @@
+/*
+ * f_mass_storage.c -- Mass Storage USB Composite Function
+ *
+ * Copyright (C) 2003-2008 Alan Stern
+ * Copyright (C) 2009 Samsung Electronics
+ *                    Author: Michal Nazarewicz <mina86@mina86.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders may not be used
+ *    to endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * The Mass Storage Function acts as a USB Mass Storage device,
+ * appearing to the host as a disk drive or as a CD-ROM drive.  In
+ * addition to providing an example of a genuinely useful composite
+ * function for a USB device, it also illustrates a technique of
+ * double-buffering for increased throughput.
+ *
+ * For more information about MSF and in particular its module
+ * parameters and sysfs interface read the
+ * <Documentation/usb/mass-storage.txt> file.
+ */
+
+/*
+ * MSF is configured by specifying a fsg_config structure.  It has the
+ * following fields:
+ *
+ *     nluns           Number of LUNs function have (anywhere from 1
+ *                             to FSG_MAX_LUNS which is 8).
+ *     luns            An array of LUN configuration values.  This
+ *                             should be filled for each LUN that
+ *                             function will include (ie. for "nluns"
+ *                             LUNs).  Each element of the array has
+ *                             the following fields:
+ *     ->filename      The path to the backing file for the LUN.
+ *                             Required if LUN is not marked as
+ *                             removable.
+ *     ->ro            Flag specifying access to the LUN shall be
+ *                             read-only.  This is implied if CD-ROM
+ *                             emulation is enabled as well as when
+ *                             it was impossible to open "filename"
+ *                             in R/W mode.
+ *     ->removable     Flag specifying that LUN shall be indicated as
+ *                             being removable.
+ *     ->cdrom         Flag specifying that LUN shall be reported as
+ *                             being a CD-ROM.
+ *     ->nofua         Flag specifying that FUA flag in SCSI WRITE(10,12)
+ *                             commands for this LUN shall be ignored.
+ *
+ *     vendor_name
+ *     product_name
+ *     release         Information used as a reply to INQUIRY
+ *                             request.  To use default set to NULL,
+ *                             NULL, 0xffff respectively.  The first
+ *                             field should be 8 and the second 16
+ *                             characters or less.
+ *
+ *     can_stall       Set to permit function to halt bulk endpoints.
+ *                             Disabled on some USB devices known not
+ *                             to work correctly.  You should set it
+ *                             to true.
+ *
+ * If "removable" is not set for a LUN then a backing file must be
+ * specified.  If it is set, then NULL filename means the LUN's medium
+ * is not loaded (an empty string as "filename" in the fsg_config
+ * structure causes error).  The CD-ROM emulation includes a single
+ * data track and no audio tracks; hence there need be only one
+ * backing file per LUN.
+ *
+ * This function is heavily based on "File-backed Storage Gadget" by
+ * Alan Stern which in turn is heavily based on "Gadget Zero" by David
+ * Brownell.  The driver's SCSI command interface was based on the
+ * "Information technology - Small Computer System Interface - 2"
+ * document from X3T9.2 Project 375D, Revision 10L, 7-SEP-93,
+ * available at <http://www.t10.org/ftp/t10/drafts/s2/s2-r10l.pdf>.
+ * The single exception is opcode 0x23 (READ FORMAT CAPACITIES), which
+ * was based on the "Universal Serial Bus Mass Storage Class UFI
+ * Command Specification" document, Revision 1.0, December 14, 1998,
+ * available at
+ * <http://www.usb.org/developers/devclass_docs/usbmass-ufi10.pdf>.
+ */
+
+/*
+ *                             Driver Design
+ *
+ * The MSF is fairly straightforward.  There is a main kernel
+ * thread that handles most of the work.  Interrupt routines field
+ * callbacks from the controller driver: bulk- and interrupt-request
+ * completion notifications, endpoint-0 events, and disconnect events.
+ * Completion events are passed to the main thread by wakeup calls.  Many
+ * ep0 requests are handled at interrupt time, but SetInterface,
+ * SetConfiguration, and device reset requests are forwarded to the
+ * thread in the form of "exceptions" using SIGUSR1 signals (since they
+ * should interrupt any ongoing file I/O operations).
+ *
+ * The thread's main routine implements the standard command/data/status
+ * parts of a SCSI interaction.  It and its subroutines are full of tests
+ * for pending signals/exceptions -- all this polling is necessary since
+ * the kernel has no setjmp/longjmp equivalents.  (Maybe this is an
+ * indication that the driver really wants to be running in userspace.)
+ * An important point is that so long as the thread is alive it keeps an
+ * open reference to the backing file.  This will prevent unmounting
+ * the backing file's underlying filesystem and could cause problems
+ * during system shutdown, for example.  To prevent such problems, the
+ * thread catches INT, TERM, and KILL signals and converts them into
+ * an EXIT exception.
+ *
+ * In normal operation the main thread is started during the gadget's
+ * fsg_bind() callback and stopped during fsg_unbind().  But it can
+ * also exit when it receives a signal, and there's no point leaving
+ * the gadget running when the thread is dead.  As of this moment, MSF
+ * provides no way to deregister the gadget when thread dies -- maybe
+ * a callback functions is needed.
+ *
+ * To provide maximum throughput, the driver uses a circular pipeline of
+ * buffer heads (struct fsg_buffhd).  In principle the pipeline can be
+ * arbitrarily long; in practice the benefits don't justify having more
+ * than 2 stages (i.e., double buffering).  But it helps to think of the
+ * pipeline as being a long one.  Each buffer head contains a bulk-in and
+ * a bulk-out request pointer (since the buffer can be used for both
+ * output and input -- directions always are given from the host's
+ * point of view) as well as a pointer to the buffer and various state
+ * variables.
+ *
+ * Use of the pipeline follows a simple protocol.  There is a variable
+ * (fsg->next_buffhd_to_fill) that points to the next buffer head to use.
+ * At any time that buffer head may still be in use from an earlier
+ * request, so each buffer head has a state variable indicating whether
+ * it is EMPTY, FULL, or BUSY.  Typical use involves waiting for the
+ * buffer head to be EMPTY, filling the buffer either by file I/O or by
+ * USB I/O (during which the buffer head is BUSY), and marking the buffer
+ * head FULL when the I/O is complete.  Then the buffer will be emptied
+ * (again possibly by USB I/O, during which it is marked BUSY) and
+ * finally marked EMPTY again (possibly by a completion routine).
+ *
+ * A module parameter tells the driver to avoid stalling the bulk
+ * endpoints wherever the transport specification allows.  This is
+ * necessary for some UDCs like the SuperH, which cannot reliably clear a
+ * halt on a bulk endpoint.  However, under certain circumstances the
+ * Bulk-only specification requires a stall.  In such cases the driver
+ * will halt the endpoint and set a flag indicating that it should clear
+ * the halt in software during the next device reset.  Hopefully this
+ * will permit everything to work correctly.  Furthermore, although the
+ * specification allows the bulk-out endpoint to halt when the host sends
+ * too much data, implementing this would cause an unavoidable race.
+ * The driver will always use the "no-stall" approach for OUT transfers.
+ *
+ * One subtle point concerns sending status-stage responses for ep0
+ * requests.  Some of these requests, such as device reset, can involve
+ * interrupting an ongoing file I/O operation, which might take an
+ * arbitrarily long time.  During that delay the host might give up on
+ * the original ep0 request and issue a new one.  When that happens the
+ * driver should not notify the host about completion of the original
+ * request, as the host will no longer be waiting for it.  So the driver
+ * assigns to each ep0 request a unique tag, and it keeps track of the
+ * tag value of the request associated with a long-running exception
+ * (device-reset, interface-change, or configuration-change).  When the
+ * exception handler is finished, the status-stage response is submitted
+ * only if the current ep0 request tag is equal to the exception request
+ * tag.  Thus only the most recently received ep0 request will get a
+ * status-stage response.
+ *
+ * Warning: This driver source file is too long.  It ought to be split up
+ * into a header file plus about 3 separate .c files, to handle the details
+ * of the Gadget, USB Mass Storage, and SCSI protocols.
+ */
+
+
+/* #define VERBOSE_DEBUG */
+/* #define DUMP_MSGS */
+
+#include <linux/blkdev.h>
+#include <linux/completion.h>
+#include <linux/dcache.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/fcntl.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/kref.h>
+#include <linux/kthread.h>
+#include <linux/limits.h>
+#include <linux/rwsem.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/freezer.h>
+#include <linux/module.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h>
+
+#include "gadget_chips.h"
+#include "configfs.h"
+
+
+/*------------------------------------------------------------------------*/
+
+#define FSG_DRIVER_DESC                "Mass Storage Function"
+#define FSG_DRIVER_VERSION     "2009/09/11"
+
+static const char fsg_string_interface[] = "Mass Storage";
+
+#include "storage_common.h"
+#include "f_mass_storage.h"
+
+/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */
+static struct usb_string               fsg_strings[] = {
+       {FSG_STRING_INTERFACE,          fsg_string_interface},
+       {}
+};
+
+static struct usb_gadget_strings       fsg_stringtab = {
+       .language       = 0x0409,               /* en-us */
+       .strings        = fsg_strings,
+};
+
+static struct usb_gadget_strings *fsg_strings_array[] = {
+       &fsg_stringtab,
+       NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+struct fsg_dev;
+struct fsg_common;
+
+/* Data shared by all the FSG instances. */
+struct fsg_common {
+       struct usb_gadget       *gadget;
+       struct usb_composite_dev *cdev;
+       struct fsg_dev          *fsg, *new_fsg;
+       wait_queue_head_t       fsg_wait;
+
+       /* filesem protects: backing files in use */
+       struct rw_semaphore     filesem;
+
+       /* lock protects: state, all the req_busy's */
+       spinlock_t              lock;
+
+       struct usb_ep           *ep0;           /* Copy of gadget->ep0 */
+       struct usb_request      *ep0req;        /* Copy of cdev->req */
+       unsigned int            ep0_req_tag;
+
+       struct fsg_buffhd       *next_buffhd_to_fill;
+       struct fsg_buffhd       *next_buffhd_to_drain;
+       struct fsg_buffhd       *buffhds;
+       unsigned int            fsg_num_buffers;
+
+       int                     cmnd_size;
+       u8                      cmnd[MAX_COMMAND_SIZE];
+
+       unsigned int            nluns;
+       unsigned int            lun;
+       struct fsg_lun          **luns;
+       struct fsg_lun          *curlun;
+
+       unsigned int            bulk_out_maxpacket;
+       enum fsg_state          state;          /* For exception handling */
+       unsigned int            exception_req_tag;
+
+       enum data_direction     data_dir;
+       u32                     data_size;
+       u32                     data_size_from_cmnd;
+       u32                     tag;
+       u32                     residue;
+       u32                     usb_amount_left;
+
+       unsigned int            can_stall:1;
+       unsigned int            free_storage_on_release:1;
+       unsigned int            phase_error:1;
+       unsigned int            short_packet_received:1;
+       unsigned int            bad_lun_okay:1;
+       unsigned int            running:1;
+       unsigned int            sysfs:1;
+
+       int                     thread_wakeup_needed;
+       struct completion       thread_notifier;
+       struct task_struct      *thread_task;
+
+       /* Callback functions. */
+       const struct fsg_operations     *ops;
+       /* Gadget's private data. */
+       void                    *private_data;
+
+       /*
+        * Vendor (8 chars), product (16 chars), release (4
+        * hexadecimal digits) and NUL byte
+        */
+       char inquiry_string[8 + 16 + 4 + 1];
+
+       struct kref             ref;
+};
+
+struct fsg_dev {
+       struct usb_function     function;
+       struct usb_gadget       *gadget;        /* Copy of cdev->gadget */
+       struct fsg_common       *common;
+
+       u16                     interface_number;
+
+       unsigned int            bulk_in_enabled:1;
+       unsigned int            bulk_out_enabled:1;
+
+       unsigned long           atomic_bitflags;
+#define IGNORE_BULK_OUT                0
+
+       struct usb_ep           *bulk_in;
+       struct usb_ep           *bulk_out;
+};
+
+static inline int __fsg_is_set(struct fsg_common *common,
+                              const char *func, unsigned line)
+{
+       if (common->fsg)
+               return 1;
+       ERROR(common, "common->fsg is NULL in %s at %u\n", func, line);
+       WARN_ON(1);
+       return 0;
+}
+
+#define fsg_is_set(common) likely(__fsg_is_set(common, __func__, __LINE__))
+
+static inline struct fsg_dev *fsg_from_func(struct usb_function *f)
+{
+       return container_of(f, struct fsg_dev, function);
+}
+
+typedef void (*fsg_routine_t)(struct fsg_dev *);
+
+static int exception_in_progress(struct fsg_common *common)
+{
+       return common->state > FSG_STATE_IDLE;
+}
+
+/* Make bulk-out requests be divisible by the maxpacket size */
+static void set_bulk_out_req_length(struct fsg_common *common,
+                                   struct fsg_buffhd *bh, unsigned int length)
+{
+       unsigned int    rem;
+
+       bh->bulk_out_intended_length = length;
+       rem = length % common->bulk_out_maxpacket;
+       if (rem > 0)
+               length += common->bulk_out_maxpacket - rem;
+       bh->outreq->length = length;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep)
+{
+       const char      *name;
+
+       if (ep == fsg->bulk_in)
+               name = "bulk-in";
+       else if (ep == fsg->bulk_out)
+               name = "bulk-out";
+       else
+               name = ep->name;
+       DBG(fsg, "%s set halt\n", name);
+       return usb_ep_set_halt(ep);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* These routines may be called in process context or in_irq */
+
+/* Caller must hold fsg->lock */
+static void wakeup_thread(struct fsg_common *common)
+{
+       smp_wmb();      /* ensure the write of bh->state is complete */
+       /* Tell the main thread that something has happened */
+       common->thread_wakeup_needed = 1;
+       if (common->thread_task)
+               wake_up_process(common->thread_task);
+}
+
+static void raise_exception(struct fsg_common *common, enum fsg_state new_state)
+{
+       unsigned long           flags;
+
+       /*
+        * Do nothing if a higher-priority exception is already in progress.
+        * If a lower-or-equal priority exception is in progress, preempt it
+        * and notify the main thread by sending it a signal.
+        */
+       spin_lock_irqsave(&common->lock, flags);
+       if (common->state <= new_state) {
+               common->exception_req_tag = common->ep0_req_tag;
+               common->state = new_state;
+               if (common->thread_task)
+                       send_sig_info(SIGUSR1, SEND_SIG_FORCED,
+                                     common->thread_task);
+       }
+       spin_unlock_irqrestore(&common->lock, flags);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int ep0_queue(struct fsg_common *common)
+{
+       int     rc;
+
+       rc = usb_ep_queue(common->ep0, common->ep0req, GFP_ATOMIC);
+       common->ep0->driver_data = common;
+       if (rc != 0 && rc != -ESHUTDOWN) {
+               /* We can't do much more than wait for a reset */
+               WARNING(common, "error in submission: %s --> %d\n",
+                       common->ep0->name, rc);
+       }
+       return rc;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* Completion handlers. These always run in_irq. */
+
+static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct fsg_common       *common = ep->driver_data;
+       struct fsg_buffhd       *bh = req->context;
+
+       if (req->status || req->actual != req->length)
+               DBG(common, "%s --> %d, %u/%u\n", __func__,
+                   req->status, req->actual, req->length);
+       if (req->status == -ECONNRESET)         /* Request was cancelled */
+               usb_ep_fifo_flush(ep);
+
+       /* Hold the lock while we update the request and buffer states */
+       smp_wmb();
+       spin_lock(&common->lock);
+       bh->inreq_busy = 0;
+       bh->state = BUF_STATE_EMPTY;
+       wakeup_thread(common);
+       spin_unlock(&common->lock);
+}
+
+static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct fsg_common       *common = ep->driver_data;
+       struct fsg_buffhd       *bh = req->context;
+
+       dump_msg(common, "bulk-out", req->buf, req->actual);
+       if (req->status || req->actual != bh->bulk_out_intended_length)
+               DBG(common, "%s --> %d, %u/%u\n", __func__,
+                   req->status, req->actual, bh->bulk_out_intended_length);
+       if (req->status == -ECONNRESET)         /* Request was cancelled */
+               usb_ep_fifo_flush(ep);
+
+       /* Hold the lock while we update the request and buffer states */
+       smp_wmb();
+       spin_lock(&common->lock);
+       bh->outreq_busy = 0;
+       bh->state = BUF_STATE_FULL;
+       wakeup_thread(common);
+       spin_unlock(&common->lock);
+}
+
+static int fsg_setup(struct usb_function *f,
+                    const struct usb_ctrlrequest *ctrl)
+{
+       struct fsg_dev          *fsg = fsg_from_func(f);
+       struct usb_request      *req = fsg->common->ep0req;
+       u16                     w_index = le16_to_cpu(ctrl->wIndex);
+       u16                     w_value = le16_to_cpu(ctrl->wValue);
+       u16                     w_length = le16_to_cpu(ctrl->wLength);
+
+       if (!fsg_is_set(fsg->common))
+               return -EOPNOTSUPP;
+
+       ++fsg->common->ep0_req_tag;     /* Record arrival of a new request */
+       req->context = NULL;
+       req->length = 0;
+       dump_msg(fsg, "ep0-setup", (u8 *) ctrl, sizeof(*ctrl));
+
+       switch (ctrl->bRequest) {
+
+       case US_BULK_RESET_REQUEST:
+               if (ctrl->bRequestType !=
+                   (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE))
+                       break;
+               if (w_index != fsg->interface_number || w_value != 0 ||
+                               w_length != 0)
+                       return -EDOM;
+
+               /*
+                * Raise an exception to stop the current operation
+                * and reinitialize our state.
+                */
+               DBG(fsg, "bulk reset request\n");
+               raise_exception(fsg->common, FSG_STATE_RESET);
+               return USB_GADGET_DELAYED_STATUS;
+
+       case US_BULK_GET_MAX_LUN:
+               if (ctrl->bRequestType !=
+                   (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE))
+                       break;
+               if (w_index != fsg->interface_number || w_value != 0 ||
+                               w_length != 1)
+                       return -EDOM;
+               VDBG(fsg, "get max LUN\n");
+               *(u8 *)req->buf = fsg->common->nluns - 1;
+
+               /* Respond with data/status */
+               req->length = min((u16)1, w_length);
+               return ep0_queue(fsg->common);
+       }
+
+       VDBG(fsg,
+            "unknown class-specific control req %02x.%02x v%04x i%04x l%u\n",
+            ctrl->bRequestType, ctrl->bRequest,
+            le16_to_cpu(ctrl->wValue), w_index, w_length);
+       return -EOPNOTSUPP;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* All the following routines run in process context */
+
+/* Use this for bulk or interrupt transfers, not ep0 */
+static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep,
+                          struct usb_request *req, int *pbusy,
+                          enum fsg_buffer_state *state)
+{
+       int     rc;
+
+       if (ep == fsg->bulk_in)
+               dump_msg(fsg, "bulk-in", req->buf, req->length);
+
+       spin_lock_irq(&fsg->common->lock);
+       *pbusy = 1;
+       *state = BUF_STATE_BUSY;
+       spin_unlock_irq(&fsg->common->lock);
+       rc = usb_ep_queue(ep, req, GFP_KERNEL);
+       if (rc != 0) {
+               *pbusy = 0;
+               *state = BUF_STATE_EMPTY;
+
+               /* We can't do much more than wait for a reset */
+
+               /*
+                * Note: currently the net2280 driver fails zero-length
+                * submissions if DMA is enabled.
+                */
+               if (rc != -ESHUTDOWN &&
+                   !(rc == -EOPNOTSUPP && req->length == 0))
+                       WARNING(fsg, "error in submission: %s --> %d\n",
+                               ep->name, rc);
+       }
+}
+
+static bool start_in_transfer(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+       if (!fsg_is_set(common))
+               return false;
+       start_transfer(common->fsg, common->fsg->bulk_in,
+                      bh->inreq, &bh->inreq_busy, &bh->state);
+       return true;
+}
+
+static bool start_out_transfer(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+       if (!fsg_is_set(common))
+               return false;
+       start_transfer(common->fsg, common->fsg->bulk_out,
+                      bh->outreq, &bh->outreq_busy, &bh->state);
+       return true;
+}
+
+static int sleep_thread(struct fsg_common *common, bool can_freeze)
+{
+       int     rc = 0;
+
+       /* Wait until a signal arrives or we are woken up */
+       for (;;) {
+               if (can_freeze)
+                       try_to_freeze();
+               set_current_state(TASK_INTERRUPTIBLE);
+               if (signal_pending(current)) {
+                       rc = -EINTR;
+                       break;
+               }
+               if (common->thread_wakeup_needed)
+                       break;
+               schedule();
+       }
+       __set_current_state(TASK_RUNNING);
+       common->thread_wakeup_needed = 0;
+       smp_rmb();      /* ensure the latest bh->state is visible */
+       return rc;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_read(struct fsg_common *common)
+{
+       struct fsg_lun          *curlun = common->curlun;
+       u32                     lba;
+       struct fsg_buffhd       *bh;
+       int                     rc;
+       u32                     amount_left;
+       loff_t                  file_offset, file_offset_tmp;
+       unsigned int            amount;
+       ssize_t                 nread;
+
+       /*
+        * Get the starting Logical Block Address and check that it's
+        * not too big.
+        */
+       if (common->cmnd[0] == READ_6)
+               lba = get_unaligned_be24(&common->cmnd[1]);
+       else {
+               lba = get_unaligned_be32(&common->cmnd[2]);
+
+               /*
+                * We allow DPO (Disable Page Out = don't save data in the
+                * cache) and FUA (Force Unit Access = don't read from the
+                * cache), but we don't implement them.
+                */
+               if ((common->cmnd[1] & ~0x18) != 0) {
+                       curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+                       return -EINVAL;
+               }
+       }
+       if (lba >= curlun->num_sectors) {
+               curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+               return -EINVAL;
+       }
+       file_offset = ((loff_t) lba) << curlun->blkbits;
+
+       /* Carry out the file reads */
+       amount_left = common->data_size_from_cmnd;
+       if (unlikely(amount_left == 0))
+               return -EIO;            /* No default reply */
+
+       for (;;) {
+               /*
+                * Figure out how much we need to read:
+                * Try to read the remaining amount.
+                * But don't read more than the buffer size.
+                * And don't try to read past the end of the file.
+                */
+               amount = min(amount_left, FSG_BUFLEN);
+               amount = min((loff_t)amount,
+                            curlun->file_length - file_offset);
+
+               /* Wait for the next buffer to become available */
+               bh = common->next_buffhd_to_fill;
+               while (bh->state != BUF_STATE_EMPTY) {
+                       rc = sleep_thread(common, false);
+                       if (rc)
+                               return rc;
+               }
+
+               /*
+                * If we were asked to read past the end of file,
+                * end with an empty buffer.
+                */
+               if (amount == 0) {
+                       curlun->sense_data =
+                                       SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+                       curlun->sense_data_info =
+                                       file_offset >> curlun->blkbits;
+                       curlun->info_valid = 1;
+                       bh->inreq->length = 0;
+                       bh->state = BUF_STATE_FULL;
+                       break;
+               }
+
+               /* Perform the read */
+               file_offset_tmp = file_offset;
+               nread = vfs_read(curlun->filp,
+                                (char __user *)bh->buf,
+                                amount, &file_offset_tmp);
+               VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,
+                     (unsigned long long)file_offset, (int)nread);
+               if (signal_pending(current))
+                       return -EINTR;
+
+               if (nread < 0) {
+                       LDBG(curlun, "error in file read: %d\n", (int)nread);
+                       nread = 0;
+               } else if (nread < amount) {
+                       LDBG(curlun, "partial file read: %d/%u\n",
+                            (int)nread, amount);
+                       nread = round_down(nread, curlun->blksize);
+               }
+               file_offset  += nread;
+               amount_left  -= nread;
+               common->residue -= nread;
+
+               /*
+                * Except at the end of the transfer, nread will be
+                * equal to the buffer size, which is divisible by the
+                * bulk-in maxpacket size.
+                */
+               bh->inreq->length = nread;
+               bh->state = BUF_STATE_FULL;
+
+               /* If an error occurred, report it and its position */
+               if (nread < amount) {
+                       curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
+                       curlun->sense_data_info =
+                                       file_offset >> curlun->blkbits;
+                       curlun->info_valid = 1;
+                       break;
+               }
+
+               if (amount_left == 0)
+                       break;          /* No more left to read */
+
+               /* Send this buffer and go read some more */
+               bh->inreq->zero = 0;
+               if (!start_in_transfer(common, bh))
+                       /* Don't know what to do if common->fsg is NULL */
+                       return -EIO;
+               common->next_buffhd_to_fill = bh->next;
+       }
+
+       return -EIO;            /* No default reply */
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_write(struct fsg_common *common)
+{
+       struct fsg_lun          *curlun = common->curlun;
+       u32                     lba;
+       struct fsg_buffhd       *bh;
+       int                     get_some_more;
+       u32                     amount_left_to_req, amount_left_to_write;
+       loff_t                  usb_offset, file_offset, file_offset_tmp;
+       unsigned int            amount;
+       ssize_t                 nwritten;
+       int                     rc;
+
+       if (curlun->ro) {
+               curlun->sense_data = SS_WRITE_PROTECTED;
+               return -EINVAL;
+       }
+       spin_lock(&curlun->filp->f_lock);
+       curlun->filp->f_flags &= ~O_SYNC;       /* Default is not to wait */
+       spin_unlock(&curlun->filp->f_lock);
+
+       /*
+        * Get the starting Logical Block Address and check that it's
+        * not too big
+        */
+       if (common->cmnd[0] == WRITE_6)
+               lba = get_unaligned_be24(&common->cmnd[1]);
+       else {
+               lba = get_unaligned_be32(&common->cmnd[2]);
+
+               /*
+                * We allow DPO (Disable Page Out = don't save data in the
+                * cache) and FUA (Force Unit Access = write directly to the
+                * medium).  We don't implement DPO; we implement FUA by
+                * performing synchronous output.
+                */
+               if (common->cmnd[1] & ~0x18) {
+                       curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+                       return -EINVAL;
+               }
+               if (!curlun->nofua && (common->cmnd[1] & 0x08)) { /* FUA */
+                       spin_lock(&curlun->filp->f_lock);
+                       curlun->filp->f_flags |= O_SYNC;
+                       spin_unlock(&curlun->filp->f_lock);
+               }
+       }
+       if (lba >= curlun->num_sectors) {
+               curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+               return -EINVAL;
+       }
+
+       /* Carry out the file writes */
+       get_some_more = 1;
+       file_offset = usb_offset = ((loff_t) lba) << curlun->blkbits;
+       amount_left_to_req = common->data_size_from_cmnd;
+       amount_left_to_write = common->data_size_from_cmnd;
+
+       while (amount_left_to_write > 0) {
+
+               /* Queue a request for more data from the host */
+               bh = common->next_buffhd_to_fill;
+               if (bh->state == BUF_STATE_EMPTY && get_some_more) {
+
+                       /*
+                        * Figure out how much we want to get:
+                        * Try to get the remaining amount,
+                        * but not more than the buffer size.
+                        */
+                       amount = min(amount_left_to_req, FSG_BUFLEN);
+
+                       /* Beyond the end of the backing file? */
+                       if (usb_offset >= curlun->file_length) {
+                               get_some_more = 0;
+                               curlun->sense_data =
+                                       SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+                               curlun->sense_data_info =
+                                       usb_offset >> curlun->blkbits;
+                               curlun->info_valid = 1;
+                               continue;
+                       }
+
+                       /* Get the next buffer */
+                       usb_offset += amount;
+                       common->usb_amount_left -= amount;
+                       amount_left_to_req -= amount;
+                       if (amount_left_to_req == 0)
+                               get_some_more = 0;
+
+                       /*
+                        * Except at the end of the transfer, amount will be
+                        * equal to the buffer size, which is divisible by
+                        * the bulk-out maxpacket size.
+                        */
+                       set_bulk_out_req_length(common, bh, amount);
+                       if (!start_out_transfer(common, bh))
+                               /* Dunno what to do if common->fsg is NULL */
+                               return -EIO;
+                       common->next_buffhd_to_fill = bh->next;
+                       continue;
+               }
+
+               /* Write the received data to the backing file */
+               bh = common->next_buffhd_to_drain;
+               if (bh->state == BUF_STATE_EMPTY && !get_some_more)
+                       break;                  /* We stopped early */
+               if (bh->state == BUF_STATE_FULL) {
+                       smp_rmb();
+                       common->next_buffhd_to_drain = bh->next;
+                       bh->state = BUF_STATE_EMPTY;
+
+                       /* Did something go wrong with the transfer? */
+                       if (bh->outreq->status != 0) {
+                               curlun->sense_data = SS_COMMUNICATION_FAILURE;
+                               curlun->sense_data_info =
+                                       file_offset >> curlun->blkbits;
+                               curlun->info_valid = 1;
+                               break;
+                       }
+
+                       amount = bh->outreq->actual;
+                       if (curlun->file_length - file_offset < amount) {
+                               LERROR(curlun,
+                                      "write %u @ %llu beyond end %llu\n",
+                                      amount, (unsigned long long)file_offset,
+                                      (unsigned long long)curlun->file_length);
+                               amount = curlun->file_length - file_offset;
+                       }
+
+                       /* Don't accept excess data.  The spec doesn't say
+                        * what to do in this case.  We'll ignore the error.
+                        */
+                       amount = min(amount, bh->bulk_out_intended_length);
+
+                       /* Don't write a partial block */
+                       amount = round_down(amount, curlun->blksize);
+                       if (amount == 0)
+                               goto empty_write;
+
+                       /* Perform the write */
+                       file_offset_tmp = file_offset;
+                       nwritten = vfs_write(curlun->filp,
+                                            (char __user *)bh->buf,
+                                            amount, &file_offset_tmp);
+                       VLDBG(curlun, "file write %u @ %llu -> %d\n", amount,
+                             (unsigned long long)file_offset, (int)nwritten);
+                       if (signal_pending(current))
+                               return -EINTR;          /* Interrupted! */
+
+                       if (nwritten < 0) {
+                               LDBG(curlun, "error in file write: %d\n",
+                                    (int)nwritten);
+                               nwritten = 0;
+                       } else if (nwritten < amount) {
+                               LDBG(curlun, "partial file write: %d/%u\n",
+                                    (int)nwritten, amount);
+                               nwritten = round_down(nwritten, curlun->blksize);
+                       }
+                       file_offset += nwritten;
+                       amount_left_to_write -= nwritten;
+                       common->residue -= nwritten;
+
+                       /* If an error occurred, report it and its position */
+                       if (nwritten < amount) {
+                               curlun->sense_data = SS_WRITE_ERROR;
+                               curlun->sense_data_info =
+                                       file_offset >> curlun->blkbits;
+                               curlun->info_valid = 1;
+                               break;
+                       }
+
+ empty_write:
+                       /* Did the host decide to stop early? */
+                       if (bh->outreq->actual < bh->bulk_out_intended_length) {
+                               common->short_packet_received = 1;
+                               break;
+                       }
+                       continue;
+               }
+
+               /* Wait for something to happen */
+               rc = sleep_thread(common, false);
+               if (rc)
+                       return rc;
+       }
+
+       return -EIO;            /* No default reply */
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_synchronize_cache(struct fsg_common *common)
+{
+       struct fsg_lun  *curlun = common->curlun;
+       int             rc;
+
+       /* We ignore the requested LBA and write out all file's
+        * dirty data buffers. */
+       rc = fsg_lun_fsync_sub(curlun);
+       if (rc)
+               curlun->sense_data = SS_WRITE_ERROR;
+       return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static void invalidate_sub(struct fsg_lun *curlun)
+{
+       struct file     *filp = curlun->filp;
+       struct inode    *inode = file_inode(filp);
+       unsigned long   rc;
+
+       rc = invalidate_mapping_pages(inode->i_mapping, 0, -1);
+       VLDBG(curlun, "invalidate_mapping_pages -> %ld\n", rc);
+}
+
+static int do_verify(struct fsg_common *common)
+{
+       struct fsg_lun          *curlun = common->curlun;
+       u32                     lba;
+       u32                     verification_length;
+       struct fsg_buffhd       *bh = common->next_buffhd_to_fill;
+       loff_t                  file_offset, file_offset_tmp;
+       u32                     amount_left;
+       unsigned int            amount;
+       ssize_t                 nread;
+
+       /*
+        * Get the starting Logical Block Address and check that it's
+        * not too big.
+        */
+       lba = get_unaligned_be32(&common->cmnd[2]);
+       if (lba >= curlun->num_sectors) {
+               curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+               return -EINVAL;
+       }
+
+       /*
+        * We allow DPO (Disable Page Out = don't save data in the
+        * cache) but we don't implement it.
+        */
+       if (common->cmnd[1] & ~0x10) {
+               curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+               return -EINVAL;
+       }
+
+       verification_length = get_unaligned_be16(&common->cmnd[7]);
+       if (unlikely(verification_length == 0))
+               return -EIO;            /* No default reply */
+
+       /* Prepare to carry out the file verify */
+       amount_left = verification_length << curlun->blkbits;
+       file_offset = ((loff_t) lba) << curlun->blkbits;
+
+       /* Write out all the dirty buffers before invalidating them */
+       fsg_lun_fsync_sub(curlun);
+       if (signal_pending(current))
+               return -EINTR;
+
+       invalidate_sub(curlun);
+       if (signal_pending(current))
+               return -EINTR;
+
+       /* Just try to read the requested blocks */
+       while (amount_left > 0) {
+               /*
+                * Figure out how much we need to read:
+                * Try to read the remaining amount, but not more than
+                * the buffer size.
+                * And don't try to read past the end of the file.
+                */
+               amount = min(amount_left, FSG_BUFLEN);
+               amount = min((loff_t)amount,
+                            curlun->file_length - file_offset);
+               if (amount == 0) {
+                       curlun->sense_data =
+                                       SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+                       curlun->sense_data_info =
+                               file_offset >> curlun->blkbits;
+                       curlun->info_valid = 1;
+                       break;
+               }
+
+               /* Perform the read */
+               file_offset_tmp = file_offset;
+               nread = vfs_read(curlun->filp,
+                               (char __user *) bh->buf,
+                               amount, &file_offset_tmp);
+               VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,
+                               (unsigned long long) file_offset,
+                               (int) nread);
+               if (signal_pending(current))
+                       return -EINTR;
+
+               if (nread < 0) {
+                       LDBG(curlun, "error in file verify: %d\n", (int)nread);
+                       nread = 0;
+               } else if (nread < amount) {
+                       LDBG(curlun, "partial file verify: %d/%u\n",
+                            (int)nread, amount);
+                       nread = round_down(nread, curlun->blksize);
+               }
+               if (nread == 0) {
+                       curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
+                       curlun->sense_data_info =
+                               file_offset >> curlun->blkbits;
+                       curlun->info_valid = 1;
+                       break;
+               }
+               file_offset += nread;
+               amount_left -= nread;
+       }
+       return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+       struct fsg_lun *curlun = common->curlun;
+       u8      *buf = (u8 *) bh->buf;
+
+       if (!curlun) {          /* Unsupported LUNs are okay */
+               common->bad_lun_okay = 1;
+               memset(buf, 0, 36);
+               buf[0] = 0x7f;          /* Unsupported, no device-type */
+               buf[4] = 31;            /* Additional length */
+               return 36;
+       }
+
+       buf[0] = curlun->cdrom ? TYPE_ROM : TYPE_DISK;
+       buf[1] = curlun->removable ? 0x80 : 0;
+       buf[2] = 2;             /* ANSI SCSI level 2 */
+       buf[3] = 2;             /* SCSI-2 INQUIRY data format */
+       buf[4] = 31;            /* Additional length */
+       buf[5] = 0;             /* No special options */
+       buf[6] = 0;
+       buf[7] = 0;
+       memcpy(buf + 8, common->inquiry_string, sizeof common->inquiry_string);
+       return 36;
+}
+
+static int do_request_sense(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+       struct fsg_lun  *curlun = common->curlun;
+       u8              *buf = (u8 *) bh->buf;
+       u32             sd, sdinfo;
+       int             valid;
+
+       /*
+        * From the SCSI-2 spec., section 7.9 (Unit attention condition):
+        *
+        * If a REQUEST SENSE command is received from an initiator
+        * with a pending unit attention condition (before the target
+        * generates the contingent allegiance condition), then the
+        * target shall either:
+        *   a) report any pending sense data and preserve the unit
+        *      attention condition on the logical unit, or,
+        *   b) report the unit attention condition, may discard any
+        *      pending sense data, and clear the unit attention
+        *      condition on the logical unit for that initiator.
+        *
+        * FSG normally uses option a); enable this code to use option b).
+        */
+#if 0
+       if (curlun && curlun->unit_attention_data != SS_NO_SENSE) {
+               curlun->sense_data = curlun->unit_attention_data;
+               curlun->unit_attention_data = SS_NO_SENSE;
+       }
+#endif
+
+       if (!curlun) {          /* Unsupported LUNs are okay */
+               common->bad_lun_okay = 1;
+               sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
+               sdinfo = 0;
+               valid = 0;
+       } else {
+               sd = curlun->sense_data;
+               sdinfo = curlun->sense_data_info;
+               valid = curlun->info_valid << 7;
+               curlun->sense_data = SS_NO_SENSE;
+               curlun->sense_data_info = 0;
+               curlun->info_valid = 0;
+       }
+
+       memset(buf, 0, 18);
+       buf[0] = valid | 0x70;                  /* Valid, current error */
+       buf[2] = SK(sd);
+       put_unaligned_be32(sdinfo, &buf[3]);    /* Sense information */
+       buf[7] = 18 - 8;                        /* Additional sense length */
+       buf[12] = ASC(sd);
+       buf[13] = ASCQ(sd);
+       return 18;
+}
+
+static int do_read_capacity(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+       struct fsg_lun  *curlun = common->curlun;
+       u32             lba = get_unaligned_be32(&common->cmnd[2]);
+       int             pmi = common->cmnd[8];
+       u8              *buf = (u8 *)bh->buf;
+
+       /* Check the PMI and LBA fields */
+       if (pmi > 1 || (pmi == 0 && lba != 0)) {
+               curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+               return -EINVAL;
+       }
+
+       put_unaligned_be32(curlun->num_sectors - 1, &buf[0]);
+                                               /* Max logical block */
+       put_unaligned_be32(curlun->blksize, &buf[4]);/* Block length */
+       return 8;
+}
+
+static int do_read_header(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+       struct fsg_lun  *curlun = common->curlun;
+       int             msf = common->cmnd[1] & 0x02;
+       u32             lba = get_unaligned_be32(&common->cmnd[2]);
+       u8              *buf = (u8 *)bh->buf;
+
+       if (common->cmnd[1] & ~0x02) {          /* Mask away MSF */
+               curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+               return -EINVAL;
+       }
+       if (lba >= curlun->num_sectors) {
+               curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+               return -EINVAL;
+       }
+
+       memset(buf, 0, 8);
+       buf[0] = 0x01;          /* 2048 bytes of user data, rest is EC */
+       store_cdrom_address(&buf[4], msf, lba);
+       return 8;
+}
+
+static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+       struct fsg_lun  *curlun = common->curlun;
+       int             msf = common->cmnd[1] & 0x02;
+       int             start_track = common->cmnd[6];
+       u8              *buf = (u8 *)bh->buf;
+
+       if ((common->cmnd[1] & ~0x02) != 0 ||   /* Mask away MSF */
+                       start_track > 1) {
+               curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+               return -EINVAL;
+       }
+
+       memset(buf, 0, 20);
+       buf[1] = (20-2);                /* TOC data length */
+       buf[2] = 1;                     /* First track number */
+       buf[3] = 1;                     /* Last track number */
+       buf[5] = 0x16;                  /* Data track, copying allowed */
+       buf[6] = 0x01;                  /* Only track is number 1 */
+       store_cdrom_address(&buf[8], msf, 0);
+
+       buf[13] = 0x16;                 /* Lead-out track is data */
+       buf[14] = 0xAA;                 /* Lead-out track number */
+       store_cdrom_address(&buf[16], msf, curlun->num_sectors);
+       return 20;
+}
+
+static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+       struct fsg_lun  *curlun = common->curlun;
+       int             mscmnd = common->cmnd[0];
+       u8              *buf = (u8 *) bh->buf;
+       u8              *buf0 = buf;
+       int             pc, page_code;
+       int             changeable_values, all_pages;
+       int             valid_page = 0;
+       int             len, limit;
+
+       if ((common->cmnd[1] & ~0x08) != 0) {   /* Mask away DBD */
+               curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+               return -EINVAL;
+       }
+       pc = common->cmnd[2] >> 6;
+       page_code = common->cmnd[2] & 0x3f;
+       if (pc == 3) {
+               curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED;
+               return -EINVAL;
+       }
+       changeable_values = (pc == 1);
+       all_pages = (page_code == 0x3f);
+
+       /*
+        * Write the mode parameter header.  Fixed values are: default
+        * medium type, no cache control (DPOFUA), and no block descriptors.
+        * The only variable value is the WriteProtect bit.  We will fill in
+        * the mode data length later.
+        */
+       memset(buf, 0, 8);
+       if (mscmnd == MODE_SENSE) {
+               buf[2] = (curlun->ro ? 0x80 : 0x00);            /* WP, DPOFUA */
+               buf += 4;
+               limit = 255;
+       } else {                        /* MODE_SENSE_10 */
+               buf[3] = (curlun->ro ? 0x80 : 0x00);            /* WP, DPOFUA */
+               buf += 8;
+               limit = 65535;          /* Should really be FSG_BUFLEN */
+       }
+
+       /* No block descriptors */
+
+       /*
+        * The mode pages, in numerical order.  The only page we support
+        * is the Caching page.
+        */
+       if (page_code == 0x08 || all_pages) {
+               valid_page = 1;
+               buf[0] = 0x08;          /* Page code */
+               buf[1] = 10;            /* Page length */
+               memset(buf+2, 0, 10);   /* None of the fields are changeable */
+
+               if (!changeable_values) {
+                       buf[2] = 0x04;  /* Write cache enable, */
+                                       /* Read cache not disabled */
+                                       /* No cache retention priorities */
+                       put_unaligned_be16(0xffff, &buf[4]);
+                                       /* Don't disable prefetch */
+                                       /* Minimum prefetch = 0 */
+                       put_unaligned_be16(0xffff, &buf[8]);
+                                       /* Maximum prefetch */
+                       put_unaligned_be16(0xffff, &buf[10]);
+                                       /* Maximum prefetch ceiling */
+               }
+               buf += 12;
+       }
+
+       /*
+        * Check that a valid page was requested and the mode data length
+        * isn't too long.
+        */
+       len = buf - buf0;
+       if (!valid_page || len > limit) {
+               curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+               return -EINVAL;
+       }
+
+       /*  Store the mode data length */
+       if (mscmnd == MODE_SENSE)
+               buf0[0] = len - 1;
+       else
+               put_unaligned_be16(len - 2, buf0);
+       return len;
+}
+
+static int do_start_stop(struct fsg_common *common)
+{
+       struct fsg_lun  *curlun = common->curlun;
+       int             loej, start;
+
+       if (!curlun) {
+               return -EINVAL;
+       } else if (!curlun->removable) {
+               curlun->sense_data = SS_INVALID_COMMAND;
+               return -EINVAL;
+       } else if ((common->cmnd[1] & ~0x01) != 0 || /* Mask away Immed */
+                  (common->cmnd[4] & ~0x03) != 0) { /* Mask LoEj, Start */
+               curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+               return -EINVAL;
+       }
+
+       loej  = common->cmnd[4] & 0x02;
+       start = common->cmnd[4] & 0x01;
+
+       /*
+        * Our emulation doesn't support mounting; the medium is
+        * available for use as soon as it is loaded.
+        */
+       if (start) {
+               if (!fsg_lun_is_open(curlun)) {
+                       curlun->sense_data = SS_MEDIUM_NOT_PRESENT;
+                       return -EINVAL;
+               }
+               return 0;
+       }
+
+       /* Are we allowed to unload the media? */
+       if (curlun->prevent_medium_removal) {
+               LDBG(curlun, "unload attempt prevented\n");
+               curlun->sense_data = SS_MEDIUM_REMOVAL_PREVENTED;
+               return -EINVAL;
+       }
+
+       if (!loej)
+               return 0;
+
+       up_read(&common->filesem);
+       down_write(&common->filesem);
+       fsg_lun_close(curlun);
+       up_write(&common->filesem);
+       down_read(&common->filesem);
+
+       return 0;
+}
+
+static int do_prevent_allow(struct fsg_common *common)
+{
+       struct fsg_lun  *curlun = common->curlun;
+       int             prevent;
+
+       if (!common->curlun) {
+               return -EINVAL;
+       } else if (!common->curlun->removable) {
+               common->curlun->sense_data = SS_INVALID_COMMAND;
+               return -EINVAL;
+       }
+
+       prevent = common->cmnd[4] & 0x01;
+       if ((common->cmnd[4] & ~0x01) != 0) {   /* Mask away Prevent */
+               curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+               return -EINVAL;
+       }
+
+       if (curlun->prevent_medium_removal && !prevent)
+               fsg_lun_fsync_sub(curlun);
+       curlun->prevent_medium_removal = prevent;
+       return 0;
+}
+
+static int do_read_format_capacities(struct fsg_common *common,
+                       struct fsg_buffhd *bh)
+{
+       struct fsg_lun  *curlun = common->curlun;
+       u8              *buf = (u8 *) bh->buf;
+
+       buf[0] = buf[1] = buf[2] = 0;
+       buf[3] = 8;     /* Only the Current/Maximum Capacity Descriptor */
+       buf += 4;
+
+       put_unaligned_be32(curlun->num_sectors, &buf[0]);
+                                               /* Number of blocks */
+       put_unaligned_be32(curlun->blksize, &buf[4]);/* Block length */
+       buf[4] = 0x02;                          /* Current capacity */
+       return 12;
+}
+
+static int do_mode_select(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+       struct fsg_lun  *curlun = common->curlun;
+
+       /* We don't support MODE SELECT */
+       if (curlun)
+               curlun->sense_data = SS_INVALID_COMMAND;
+       return -EINVAL;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int halt_bulk_in_endpoint(struct fsg_dev *fsg)
+{
+       int     rc;
+
+       rc = fsg_set_halt(fsg, fsg->bulk_in);
+       if (rc == -EAGAIN)
+               VDBG(fsg, "delayed bulk-in endpoint halt\n");
+       while (rc != 0) {
+               if (rc != -EAGAIN) {
+                       WARNING(fsg, "usb_ep_set_halt -> %d\n", rc);
+                       rc = 0;
+                       break;
+               }
+
+               /* Wait for a short time and then try again */
+               if (msleep_interruptible(100) != 0)
+                       return -EINTR;
+               rc = usb_ep_set_halt(fsg->bulk_in);
+       }
+       return rc;
+}
+
+static int wedge_bulk_in_endpoint(struct fsg_dev *fsg)
+{
+       int     rc;
+
+       DBG(fsg, "bulk-in set wedge\n");
+       rc = usb_ep_set_wedge(fsg->bulk_in);
+       if (rc == -EAGAIN)
+               VDBG(fsg, "delayed bulk-in endpoint wedge\n");
+       while (rc != 0) {
+               if (rc != -EAGAIN) {
+                       WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc);
+                       rc = 0;
+                       break;
+               }
+
+               /* Wait for a short time and then try again */
+               if (msleep_interruptible(100) != 0)
+                       return -EINTR;
+               rc = usb_ep_set_wedge(fsg->bulk_in);
+       }
+       return rc;
+}
+
+static int throw_away_data(struct fsg_common *common)
+{
+       struct fsg_buffhd       *bh;
+       u32                     amount;
+       int                     rc;
+
+       for (bh = common->next_buffhd_to_drain;
+            bh->state != BUF_STATE_EMPTY || common->usb_amount_left > 0;
+            bh = common->next_buffhd_to_drain) {
+
+               /* Throw away the data in a filled buffer */
+               if (bh->state == BUF_STATE_FULL) {
+                       smp_rmb();
+                       bh->state = BUF_STATE_EMPTY;
+                       common->next_buffhd_to_drain = bh->next;
+
+                       /* A short packet or an error ends everything */
+                       if (bh->outreq->actual < bh->bulk_out_intended_length ||
+                           bh->outreq->status != 0) {
+                               raise_exception(common,
+                                               FSG_STATE_ABORT_BULK_OUT);
+                               return -EINTR;
+                       }
+                       continue;
+               }
+
+               /* Try to submit another request if we need one */
+               bh = common->next_buffhd_to_fill;
+               if (bh->state == BUF_STATE_EMPTY
+                && common->usb_amount_left > 0) {
+                       amount = min(common->usb_amount_left, FSG_BUFLEN);
+
+                       /*
+                        * Except at the end of the transfer, amount will be
+                        * equal to the buffer size, which is divisible by
+                        * the bulk-out maxpacket size.
+                        */
+                       set_bulk_out_req_length(common, bh, amount);
+                       if (!start_out_transfer(common, bh))
+                               /* Dunno what to do if common->fsg is NULL */
+                               return -EIO;
+                       common->next_buffhd_to_fill = bh->next;
+                       common->usb_amount_left -= amount;
+                       continue;
+               }
+
+               /* Otherwise wait for something to happen */
+               rc = sleep_thread(common, true);
+               if (rc)
+                       return rc;
+       }
+       return 0;
+}
+
+static int finish_reply(struct fsg_common *common)
+{
+       struct fsg_buffhd       *bh = common->next_buffhd_to_fill;
+       int                     rc = 0;
+
+       switch (common->data_dir) {
+       case DATA_DIR_NONE:
+               break;                  /* Nothing to send */
+
+       /*
+        * If we don't know whether the host wants to read or write,
+        * this must be CB or CBI with an unknown command.  We mustn't
+        * try to send or receive any data.  So stall both bulk pipes
+        * if we can and wait for a reset.
+        */
+       case DATA_DIR_UNKNOWN:
+               if (!common->can_stall) {
+                       /* Nothing */
+               } else if (fsg_is_set(common)) {
+                       fsg_set_halt(common->fsg, common->fsg->bulk_out);
+                       rc = halt_bulk_in_endpoint(common->fsg);
+               } else {
+                       /* Don't know what to do if common->fsg is NULL */
+                       rc = -EIO;
+               }
+               break;
+
+       /* All but the last buffer of data must have already been sent */
+       case DATA_DIR_TO_HOST:
+               if (common->data_size == 0) {
+                       /* Nothing to send */
+
+               /* Don't know what to do if common->fsg is NULL */
+               } else if (!fsg_is_set(common)) {
+                       rc = -EIO;
+
+               /* If there's no residue, simply send the last buffer */
+               } else if (common->residue == 0) {
+                       bh->inreq->zero = 0;
+                       if (!start_in_transfer(common, bh))
+                               return -EIO;
+                       common->next_buffhd_to_fill = bh->next;
+
+               /*
+                * For Bulk-only, mark the end of the data with a short
+                * packet.  If we are allowed to stall, halt the bulk-in
+                * endpoint.  (Note: This violates the Bulk-Only Transport
+                * specification, which requires us to pad the data if we
+                * don't halt the endpoint.  Presumably nobody will mind.)
+                */
+               } else {
+                       bh->inreq->zero = 1;
+                       if (!start_in_transfer(common, bh))
+                               rc = -EIO;
+                       common->next_buffhd_to_fill = bh->next;
+                       if (common->can_stall)
+                               rc = halt_bulk_in_endpoint(common->fsg);
+               }
+               break;
+
+       /*
+        * We have processed all we want from the data the host has sent.
+        * There may still be outstanding bulk-out requests.
+        */
+       case DATA_DIR_FROM_HOST:
+               if (common->residue == 0) {
+                       /* Nothing to receive */
+
+               /* Did the host stop sending unexpectedly early? */
+               } else if (common->short_packet_received) {
+                       raise_exception(common, FSG_STATE_ABORT_BULK_OUT);
+                       rc = -EINTR;
+
+               /*
+                * We haven't processed all the incoming data.  Even though
+                * we may be allowed to stall, doing so would cause a race.
+                * The controller may already have ACK'ed all the remaining
+                * bulk-out packets, in which case the host wouldn't see a
+                * STALL.  Not realizing the endpoint was halted, it wouldn't
+                * clear the halt -- leading to problems later on.
+                */
+#if 0
+               } else if (common->can_stall) {
+                       if (fsg_is_set(common))
+                               fsg_set_halt(common->fsg,
+                                            common->fsg->bulk_out);
+                       raise_exception(common, FSG_STATE_ABORT_BULK_OUT);
+                       rc = -EINTR;
+#endif
+
+               /*
+                * We can't stall.  Read in the excess data and throw it
+                * all away.
+                */
+               } else {
+                       rc = throw_away_data(common);
+               }
+               break;
+       }
+       return rc;
+}
+
+static int send_status(struct fsg_common *common)
+{
+       struct fsg_lun          *curlun = common->curlun;
+       struct fsg_buffhd       *bh;
+       struct bulk_cs_wrap     *csw;
+       int                     rc;
+       u8                      status = US_BULK_STAT_OK;
+       u32                     sd, sdinfo = 0;
+
+       /* Wait for the next buffer to become available */
+       bh = common->next_buffhd_to_fill;
+       while (bh->state != BUF_STATE_EMPTY) {
+               rc = sleep_thread(common, true);
+               if (rc)
+                       return rc;
+       }
+
+       if (curlun) {
+               sd = curlun->sense_data;
+               sdinfo = curlun->sense_data_info;
+       } else if (common->bad_lun_okay)
+               sd = SS_NO_SENSE;
+       else
+               sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
+
+       if (common->phase_error) {
+               DBG(common, "sending phase-error status\n");
+               status = US_BULK_STAT_PHASE;
+               sd = SS_INVALID_COMMAND;
+       } else if (sd != SS_NO_SENSE) {
+               DBG(common, "sending command-failure status\n");
+               status = US_BULK_STAT_FAIL;
+               VDBG(common, "  sense data: SK x%02x, ASC x%02x, ASCQ x%02x;"
+                               "  info x%x\n",
+                               SK(sd), ASC(sd), ASCQ(sd), sdinfo);
+       }
+
+       /* Store and send the Bulk-only CSW */
+       csw = (void *)bh->buf;
+
+       csw->Signature = cpu_to_le32(US_BULK_CS_SIGN);
+       csw->Tag = common->tag;
+       csw->Residue = cpu_to_le32(common->residue);
+       csw->Status = status;
+
+       bh->inreq->length = US_BULK_CS_WRAP_LEN;
+       bh->inreq->zero = 0;
+       if (!start_in_transfer(common, bh))
+               /* Don't know what to do if common->fsg is NULL */
+               return -EIO;
+
+       common->next_buffhd_to_fill = bh->next;
+       return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Check whether the command is properly formed and whether its data size
+ * and direction agree with the values we already have.
+ */
+static int check_command(struct fsg_common *common, int cmnd_size,
+                        enum data_direction data_dir, unsigned int mask,
+                        int needs_medium, const char *name)
+{
+       int                     i;
+       unsigned int            lun = common->cmnd[1] >> 5;
+       static const char       dirletter[4] = {'u', 'o', 'i', 'n'};
+       char                    hdlen[20];
+       struct fsg_lun          *curlun;
+
+       hdlen[0] = 0;
+       if (common->data_dir != DATA_DIR_UNKNOWN)
+               sprintf(hdlen, ", H%c=%u", dirletter[(int) common->data_dir],
+                       common->data_size);
+       VDBG(common, "SCSI command: %s;  Dc=%d, D%c=%u;  Hc=%d%s\n",
+            name, cmnd_size, dirletter[(int) data_dir],
+            common->data_size_from_cmnd, common->cmnd_size, hdlen);
+
+       /*
+        * We can't reply at all until we know the correct data direction
+        * and size.
+        */
+       if (common->data_size_from_cmnd == 0)
+               data_dir = DATA_DIR_NONE;
+       if (common->data_size < common->data_size_from_cmnd) {
+               /*
+                * Host data size < Device data size is a phase error.
+                * Carry out the command, but only transfer as much as
+                * we are allowed.
+                */
+               common->data_size_from_cmnd = common->data_size;
+               common->phase_error = 1;
+       }
+       common->residue = common->data_size;
+       common->usb_amount_left = common->data_size;
+
+       /* Conflicting data directions is a phase error */
+       if (common->data_dir != data_dir && common->data_size_from_cmnd > 0) {
+               common->phase_error = 1;
+               return -EINVAL;
+       }
+
+       /* Verify the length of the command itself */
+       if (cmnd_size != common->cmnd_size) {
+
+               /*
+                * Special case workaround: There are plenty of buggy SCSI
+                * implementations. Many have issues with cbw->Length
+                * field passing a wrong command size. For those cases we
+                * always try to work around the problem by using the length
+                * sent by the host side provided it is at least as large
+                * as the correct command length.
+                * Examples of such cases would be MS-Windows, which issues
+                * REQUEST SENSE with cbw->Length == 12 where it should
+                * be 6, and xbox360 issuing INQUIRY, TEST UNIT READY and
+                * REQUEST SENSE with cbw->Length == 10 where it should
+                * be 6 as well.
+                */
+               if (cmnd_size <= common->cmnd_size) {
+                       DBG(common, "%s is buggy! Expected length %d "
+                           "but we got %d\n", name,
+                           cmnd_size, common->cmnd_size);
+                       cmnd_size = common->cmnd_size;
+               } else {
+                       common->phase_error = 1;
+                       return -EINVAL;
+               }
+       }
+
+       /* Check that the LUN values are consistent */
+       if (common->lun != lun)
+               DBG(common, "using LUN %u from CBW, not LUN %u from CDB\n",
+                   common->lun, lun);
+
+       /* Check the LUN */
+       curlun = common->curlun;
+       if (curlun) {
+               if (common->cmnd[0] != REQUEST_SENSE) {
+                       curlun->sense_data = SS_NO_SENSE;
+                       curlun->sense_data_info = 0;
+                       curlun->info_valid = 0;
+               }
+       } else {
+               common->bad_lun_okay = 0;
+
+               /*
+                * INQUIRY and REQUEST SENSE commands are explicitly allowed
+                * to use unsupported LUNs; all others may not.
+                */
+               if (common->cmnd[0] != INQUIRY &&
+                   common->cmnd[0] != REQUEST_SENSE) {
+                       DBG(common, "unsupported LUN %u\n", common->lun);
+                       return -EINVAL;
+               }
+       }
+
+       /*
+        * If a unit attention condition exists, only INQUIRY and
+        * REQUEST SENSE commands are allowed; anything else must fail.
+        */
+       if (curlun && curlun->unit_attention_data != SS_NO_SENSE &&
+           common->cmnd[0] != INQUIRY &&
+           common->cmnd[0] != REQUEST_SENSE) {
+               curlun->sense_data = curlun->unit_attention_data;
+               curlun->unit_attention_data = SS_NO_SENSE;
+               return -EINVAL;
+       }
+
+       /* Check that only command bytes listed in the mask are non-zero */
+       common->cmnd[1] &= 0x1f;                        /* Mask away the LUN */
+       for (i = 1; i < cmnd_size; ++i) {
+               if (common->cmnd[i] && !(mask & (1 << i))) {
+                       if (curlun)
+                               curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+                       return -EINVAL;
+               }
+       }
+
+       /* If the medium isn't mounted and the command needs to access
+        * it, return an error. */
+       if (curlun && !fsg_lun_is_open(curlun) && needs_medium) {
+               curlun->sense_data = SS_MEDIUM_NOT_PRESENT;
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/* wrapper of check_command for data size in blocks handling */
+static int check_command_size_in_blocks(struct fsg_common *common,
+               int cmnd_size, enum data_direction data_dir,
+               unsigned int mask, int needs_medium, const char *name)
+{
+       if (common->curlun)
+               common->data_size_from_cmnd <<= common->curlun->blkbits;
+       return check_command(common, cmnd_size, data_dir,
+                       mask, needs_medium, name);
+}
+
+static int do_scsi_command(struct fsg_common *common)
+{
+       struct fsg_buffhd       *bh;
+       int                     rc;
+       int                     reply = -EINVAL;
+       int                     i;
+       static char             unknown[16];
+
+       dump_cdb(common);
+
+       /* Wait for the next buffer to become available for data or status */
+       bh = common->next_buffhd_to_fill;
+       common->next_buffhd_to_drain = bh;
+       while (bh->state != BUF_STATE_EMPTY) {
+               rc = sleep_thread(common, true);
+               if (rc)
+                       return rc;
+       }
+       common->phase_error = 0;
+       common->short_packet_received = 0;
+
+       down_read(&common->filesem);    /* We're using the backing file */
+       switch (common->cmnd[0]) {
+
+       case INQUIRY:
+               common->data_size_from_cmnd = common->cmnd[4];
+               reply = check_command(common, 6, DATA_DIR_TO_HOST,
+                                     (1<<4), 0,
+                                     "INQUIRY");
+               if (reply == 0)
+                       reply = do_inquiry(common, bh);
+               break;
+
+       case MODE_SELECT:
+               common->data_size_from_cmnd = common->cmnd[4];
+               reply = check_command(common, 6, DATA_DIR_FROM_HOST,
+                                     (1<<1) | (1<<4), 0,
+                                     "MODE SELECT(6)");
+               if (reply == 0)
+                       reply = do_mode_select(common, bh);
+               break;
+
+       case MODE_SELECT_10:
+               common->data_size_from_cmnd =
+                       get_unaligned_be16(&common->cmnd[7]);
+               reply = check_command(common, 10, DATA_DIR_FROM_HOST,
+                                     (1<<1) | (3<<7), 0,
+                                     "MODE SELECT(10)");
+               if (reply == 0)
+                       reply = do_mode_select(common, bh);
+               break;
+
+       case MODE_SENSE:
+               common->data_size_from_cmnd = common->cmnd[4];
+               reply = check_command(common, 6, DATA_DIR_TO_HOST,
+                                     (1<<1) | (1<<2) | (1<<4), 0,
+                                     "MODE SENSE(6)");
+               if (reply == 0)
+                       reply = do_mode_sense(common, bh);
+               break;
+
+       case MODE_SENSE_10:
+               common->data_size_from_cmnd =
+                       get_unaligned_be16(&common->cmnd[7]);
+               reply = check_command(common, 10, DATA_DIR_TO_HOST,
+                                     (1<<1) | (1<<2) | (3<<7), 0,
+                                     "MODE SENSE(10)");
+               if (reply == 0)
+                       reply = do_mode_sense(common, bh);
+               break;
+
+       case ALLOW_MEDIUM_REMOVAL:
+               common->data_size_from_cmnd = 0;
+               reply = check_command(common, 6, DATA_DIR_NONE,
+                                     (1<<4), 0,
+                                     "PREVENT-ALLOW MEDIUM REMOVAL");
+               if (reply == 0)
+                       reply = do_prevent_allow(common);
+               break;
+
+       case READ_6:
+               i = common->cmnd[4];
+               common->data_size_from_cmnd = (i == 0) ? 256 : i;
+               reply = check_command_size_in_blocks(common, 6,
+                                     DATA_DIR_TO_HOST,
+                                     (7<<1) | (1<<4), 1,
+                                     "READ(6)");
+               if (reply == 0)
+                       reply = do_read(common);
+               break;
+
+       case READ_10:
+               common->data_size_from_cmnd =
+                               get_unaligned_be16(&common->cmnd[7]);
+               reply = check_command_size_in_blocks(common, 10,
+                                     DATA_DIR_TO_HOST,
+                                     (1<<1) | (0xf<<2) | (3<<7), 1,
+                                     "READ(10)");
+               if (reply == 0)
+                       reply = do_read(common);
+               break;
+
+       case READ_12:
+               common->data_size_from_cmnd =
+                               get_unaligned_be32(&common->cmnd[6]);
+               reply = check_command_size_in_blocks(common, 12,
+                                     DATA_DIR_TO_HOST,
+                                     (1<<1) | (0xf<<2) | (0xf<<6), 1,
+                                     "READ(12)");
+               if (reply == 0)
+                       reply = do_read(common);
+               break;
+
+       case READ_CAPACITY:
+               common->data_size_from_cmnd = 8;
+               reply = check_command(common, 10, DATA_DIR_TO_HOST,
+                                     (0xf<<2) | (1<<8), 1,
+                                     "READ CAPACITY");
+               if (reply == 0)
+                       reply = do_read_capacity(common, bh);
+               break;
+
+       case READ_HEADER:
+               if (!common->curlun || !common->curlun->cdrom)
+                       goto unknown_cmnd;
+               common->data_size_from_cmnd =
+                       get_unaligned_be16(&common->cmnd[7]);
+               reply = check_command(common, 10, DATA_DIR_TO_HOST,
+                                     (3<<7) | (0x1f<<1), 1,
+                                     "READ HEADER");
+               if (reply == 0)
+                       reply = do_read_header(common, bh);
+               break;
+
+       case READ_TOC:
+               if (!common->curlun || !common->curlun->cdrom)
+                       goto unknown_cmnd;
+               common->data_size_from_cmnd =
+                       get_unaligned_be16(&common->cmnd[7]);
+               reply = check_command(common, 10, DATA_DIR_TO_HOST,
+                                     (7<<6) | (1<<1), 1,
+                                     "READ TOC");
+               if (reply == 0)
+                       reply = do_read_toc(common, bh);
+               break;
+
+       case READ_FORMAT_CAPACITIES:
+               common->data_size_from_cmnd =
+                       get_unaligned_be16(&common->cmnd[7]);
+               reply = check_command(common, 10, DATA_DIR_TO_HOST,
+                                     (3<<7), 1,
+                                     "READ FORMAT CAPACITIES");
+               if (reply == 0)
+                       reply = do_read_format_capacities(common, bh);
+               break;
+
+       case REQUEST_SENSE:
+               common->data_size_from_cmnd = common->cmnd[4];
+               reply = check_command(common, 6, DATA_DIR_TO_HOST,
+                                     (1<<4), 0,
+                                     "REQUEST SENSE");
+               if (reply == 0)
+                       reply = do_request_sense(common, bh);
+               break;
+
+       case START_STOP:
+               common->data_size_from_cmnd = 0;
+               reply = check_command(common, 6, DATA_DIR_NONE,
+                                     (1<<1) | (1<<4), 0,
+                                     "START-STOP UNIT");
+               if (reply == 0)
+                       reply = do_start_stop(common);
+               break;
+
+       case SYNCHRONIZE_CACHE:
+               common->data_size_from_cmnd = 0;
+               reply = check_command(common, 10, DATA_DIR_NONE,
+                                     (0xf<<2) | (3<<7), 1,
+                                     "SYNCHRONIZE CACHE");
+               if (reply == 0)
+                       reply = do_synchronize_cache(common);
+               break;
+
+       case TEST_UNIT_READY:
+               common->data_size_from_cmnd = 0;
+               reply = check_command(common, 6, DATA_DIR_NONE,
+                               0, 1,
+                               "TEST UNIT READY");
+               break;
+
+       /*
+        * Although optional, this command is used by MS-Windows.  We
+        * support a minimal version: BytChk must be 0.
+        */
+       case VERIFY:
+               common->data_size_from_cmnd = 0;
+               reply = check_command(common, 10, DATA_DIR_NONE,
+                                     (1<<1) | (0xf<<2) | (3<<7), 1,
+                                     "VERIFY");
+               if (reply == 0)
+                       reply = do_verify(common);
+               break;
+
+       case WRITE_6:
+               i = common->cmnd[4];
+               common->data_size_from_cmnd = (i == 0) ? 256 : i;
+               reply = check_command_size_in_blocks(common, 6,
+                                     DATA_DIR_FROM_HOST,
+                                     (7<<1) | (1<<4), 1,
+                                     "WRITE(6)");
+               if (reply == 0)
+                       reply = do_write(common);
+               break;
+
+       case WRITE_10:
+               common->data_size_from_cmnd =
+                               get_unaligned_be16(&common->cmnd[7]);
+               reply = check_command_size_in_blocks(common, 10,
+                                     DATA_DIR_FROM_HOST,
+                                     (1<<1) | (0xf<<2) | (3<<7), 1,
+                                     "WRITE(10)");
+               if (reply == 0)
+                       reply = do_write(common);
+               break;
+
+       case WRITE_12:
+               common->data_size_from_cmnd =
+                               get_unaligned_be32(&common->cmnd[6]);
+               reply = check_command_size_in_blocks(common, 12,
+                                     DATA_DIR_FROM_HOST,
+                                     (1<<1) | (0xf<<2) | (0xf<<6), 1,
+                                     "WRITE(12)");
+               if (reply == 0)
+                       reply = do_write(common);
+               break;
+
+       /*
+        * Some mandatory commands that we recognize but don't implement.
+        * They don't mean much in this setting.  It's left as an exercise
+        * for anyone interested to implement RESERVE and RELEASE in terms
+        * of Posix locks.
+        */
+       case FORMAT_UNIT:
+       case RELEASE:
+       case RESERVE:
+       case SEND_DIAGNOSTIC:
+               /* Fall through */
+
+       default:
+unknown_cmnd:
+               common->data_size_from_cmnd = 0;
+               sprintf(unknown, "Unknown x%02x", common->cmnd[0]);
+               reply = check_command(common, common->cmnd_size,
+                                     DATA_DIR_UNKNOWN, ~0, 0, unknown);
+               if (reply == 0) {
+                       common->curlun->sense_data = SS_INVALID_COMMAND;
+                       reply = -EINVAL;
+               }
+               break;
+       }
+       up_read(&common->filesem);
+
+       if (reply == -EINTR || signal_pending(current))
+               return -EINTR;
+
+       /* Set up the single reply buffer for finish_reply() */
+       if (reply == -EINVAL)
+               reply = 0;              /* Error reply length */
+       if (reply >= 0 && common->data_dir == DATA_DIR_TO_HOST) {
+               reply = min((u32)reply, common->data_size_from_cmnd);
+               bh->inreq->length = reply;
+               bh->state = BUF_STATE_FULL;
+               common->residue -= reply;
+       }                               /* Otherwise it's already set */
+
+       return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
+{
+       struct usb_request      *req = bh->outreq;
+       struct bulk_cb_wrap     *cbw = req->buf;
+       struct fsg_common       *common = fsg->common;
+
+       /* Was this a real packet?  Should it be ignored? */
+       if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags))
+               return -EINVAL;
+
+       /* Is the CBW valid? */
+       if (req->actual != US_BULK_CB_WRAP_LEN ||
+                       cbw->Signature != cpu_to_le32(
+                               US_BULK_CB_SIGN)) {
+               DBG(fsg, "invalid CBW: len %u sig 0x%x\n",
+                               req->actual,
+                               le32_to_cpu(cbw->Signature));
+
+               /*
+                * The Bulk-only spec says we MUST stall the IN endpoint
+                * (6.6.1), so it's unavoidable.  It also says we must
+                * retain this state until the next reset, but there's
+                * no way to tell the controller driver it should ignore
+                * Clear-Feature(HALT) requests.
+                *
+                * We aren't required to halt the OUT endpoint; instead
+                * we can simply accept and discard any data received
+                * until the next reset.
+                */
+               wedge_bulk_in_endpoint(fsg);
+               set_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
+               return -EINVAL;
+       }
+
+       /* Is the CBW meaningful? */
+       if (cbw->Lun >= FSG_MAX_LUNS || cbw->Flags & ~US_BULK_FLAG_IN ||
+                       cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) {
+               DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, "
+                               "cmdlen %u\n",
+                               cbw->Lun, cbw->Flags, cbw->Length);
+
+               /*
+                * We can do anything we want here, so let's stall the
+                * bulk pipes if we are allowed to.
+                */
+               if (common->can_stall) {
+                       fsg_set_halt(fsg, fsg->bulk_out);
+                       halt_bulk_in_endpoint(fsg);
+               }
+               return -EINVAL;
+       }
+
+       /* Save the command for later */
+       common->cmnd_size = cbw->Length;
+       memcpy(common->cmnd, cbw->CDB, common->cmnd_size);
+       if (cbw->Flags & US_BULK_FLAG_IN)
+               common->data_dir = DATA_DIR_TO_HOST;
+       else
+               common->data_dir = DATA_DIR_FROM_HOST;
+       common->data_size = le32_to_cpu(cbw->DataTransferLength);
+       if (common->data_size == 0)
+               common->data_dir = DATA_DIR_NONE;
+       common->lun = cbw->Lun;
+       if (common->lun < common->nluns)
+               common->curlun = common->luns[common->lun];
+       else
+               common->curlun = NULL;
+       common->tag = cbw->Tag;
+       return 0;
+}
+
+static int get_next_command(struct fsg_common *common)
+{
+       struct fsg_buffhd       *bh;
+       int                     rc = 0;
+
+       /* Wait for the next buffer to become available */
+       bh = common->next_buffhd_to_fill;
+       while (bh->state != BUF_STATE_EMPTY) {
+               rc = sleep_thread(common, true);
+               if (rc)
+                       return rc;
+       }
+
+       /* Queue a request to read a Bulk-only CBW */
+       set_bulk_out_req_length(common, bh, US_BULK_CB_WRAP_LEN);
+       if (!start_out_transfer(common, bh))
+               /* Don't know what to do if common->fsg is NULL */
+               return -EIO;
+
+       /*
+        * We will drain the buffer in software, which means we
+        * can reuse it for the next filling.  No need to advance
+        * next_buffhd_to_fill.
+        */
+
+       /* Wait for the CBW to arrive */
+       while (bh->state != BUF_STATE_FULL) {
+               rc = sleep_thread(common, true);
+               if (rc)
+                       return rc;
+       }
+       smp_rmb();
+       rc = fsg_is_set(common) ? received_cbw(common->fsg, bh) : -EIO;
+       bh->state = BUF_STATE_EMPTY;
+
+       return rc;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int alloc_request(struct fsg_common *common, struct usb_ep *ep,
+               struct usb_request **preq)
+{
+       *preq = usb_ep_alloc_request(ep, GFP_ATOMIC);
+       if (*preq)
+               return 0;
+       ERROR(common, "can't allocate request for %s\n", ep->name);
+       return -ENOMEM;
+}
+
+/* Reset interface setting and re-init endpoint state (toggle etc). */
+static int do_set_interface(struct fsg_common *common, struct fsg_dev *new_fsg)
+{
+       struct fsg_dev *fsg;
+       int i, rc = 0;
+
+       if (common->running)
+               DBG(common, "reset interface\n");
+
+reset:
+       /* Deallocate the requests */
+       if (common->fsg) {
+               fsg = common->fsg;
+
+               for (i = 0; i < common->fsg_num_buffers; ++i) {
+                       struct fsg_buffhd *bh = &common->buffhds[i];
+
+                       if (bh->inreq) {
+                               usb_ep_free_request(fsg->bulk_in, bh->inreq);
+                               bh->inreq = NULL;
+                       }
+                       if (bh->outreq) {
+                               usb_ep_free_request(fsg->bulk_out, bh->outreq);
+                               bh->outreq = NULL;
+                       }
+               }
+
+               /* Disable the endpoints */
+               if (fsg->bulk_in_enabled) {
+                       usb_ep_disable(fsg->bulk_in);
+                       fsg->bulk_in->driver_data = NULL;
+                       fsg->bulk_in_enabled = 0;
+               }
+               if (fsg->bulk_out_enabled) {
+                       usb_ep_disable(fsg->bulk_out);
+                       fsg->bulk_out->driver_data = NULL;
+                       fsg->bulk_out_enabled = 0;
+               }
+
+               common->fsg = NULL;
+               wake_up(&common->fsg_wait);
+       }
+
+       common->running = 0;
+       if (!new_fsg || rc)
+               return rc;
+
+       common->fsg = new_fsg;
+       fsg = common->fsg;
+
+       /* Enable the endpoints */
+       rc = config_ep_by_speed(common->gadget, &(fsg->function), fsg->bulk_in);
+       if (rc)
+               goto reset;
+       rc = usb_ep_enable(fsg->bulk_in);
+       if (rc)
+               goto reset;
+       fsg->bulk_in->driver_data = common;
+       fsg->bulk_in_enabled = 1;
+
+       rc = config_ep_by_speed(common->gadget, &(fsg->function),
+                               fsg->bulk_out);
+       if (rc)
+               goto reset;
+       rc = usb_ep_enable(fsg->bulk_out);
+       if (rc)
+               goto reset;
+       fsg->bulk_out->driver_data = common;
+       fsg->bulk_out_enabled = 1;
+       common->bulk_out_maxpacket = usb_endpoint_maxp(fsg->bulk_out->desc);
+       clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
+
+       /* Allocate the requests */
+       for (i = 0; i < common->fsg_num_buffers; ++i) {
+               struct fsg_buffhd       *bh = &common->buffhds[i];
+
+               rc = alloc_request(common, fsg->bulk_in, &bh->inreq);
+               if (rc)
+                       goto reset;
+               rc = alloc_request(common, fsg->bulk_out, &bh->outreq);
+               if (rc)
+                       goto reset;
+               bh->inreq->buf = bh->outreq->buf = bh->buf;
+               bh->inreq->context = bh->outreq->context = bh;
+               bh->inreq->complete = bulk_in_complete;
+               bh->outreq->complete = bulk_out_complete;
+       }
+
+       common->running = 1;
+       for (i = 0; i < common->nluns; ++i)
+               if (common->luns[i])
+                       common->luns[i]->unit_attention_data =
+                               SS_RESET_OCCURRED;
+       return rc;
+}
+
+
+/****************************** ALT CONFIGS ******************************/
+
+static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+       struct fsg_dev *fsg = fsg_from_func(f);
+       fsg->common->new_fsg = fsg;
+       raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
+       return USB_GADGET_DELAYED_STATUS;
+}
+
+static void fsg_disable(struct usb_function *f)
+{
+       struct fsg_dev *fsg = fsg_from_func(f);
+       fsg->common->new_fsg = NULL;
+       raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static void handle_exception(struct fsg_common *common)
+{
+       siginfo_t               info;
+       int                     i;
+       struct fsg_buffhd       *bh;
+       enum fsg_state          old_state;
+       struct fsg_lun          *curlun;
+       unsigned int            exception_req_tag;
+
+       /*
+        * Clear the existing signals.  Anything but SIGUSR1 is converted
+        * into a high-priority EXIT exception.
+        */
+       for (;;) {
+               int sig =
+                       dequeue_signal_lock(current, &current->blocked, &info);
+               if (!sig)
+                       break;
+               if (sig != SIGUSR1) {
+                       if (common->state < FSG_STATE_EXIT)
+                               DBG(common, "Main thread exiting on signal\n");
+                       raise_exception(common, FSG_STATE_EXIT);
+               }
+       }
+
+       /* Cancel all the pending transfers */
+       if (likely(common->fsg)) {
+               for (i = 0; i < common->fsg_num_buffers; ++i) {
+                       bh = &common->buffhds[i];
+                       if (bh->inreq_busy)
+                               usb_ep_dequeue(common->fsg->bulk_in, bh->inreq);
+                       if (bh->outreq_busy)
+                               usb_ep_dequeue(common->fsg->bulk_out,
+                                              bh->outreq);
+               }
+
+               /* Wait until everything is idle */
+               for (;;) {
+                       int num_active = 0;
+                       for (i = 0; i < common->fsg_num_buffers; ++i) {
+                               bh = &common->buffhds[i];
+                               num_active += bh->inreq_busy + bh->outreq_busy;
+                       }
+                       if (num_active == 0)
+                               break;
+                       if (sleep_thread(common, true))
+                               return;
+               }
+
+               /* Clear out the controller's fifos */
+               if (common->fsg->bulk_in_enabled)
+                       usb_ep_fifo_flush(common->fsg->bulk_in);
+               if (common->fsg->bulk_out_enabled)
+                       usb_ep_fifo_flush(common->fsg->bulk_out);
+       }
+
+       /*
+        * Reset the I/O buffer states and pointers, the SCSI
+        * state, and the exception.  Then invoke the handler.
+        */
+       spin_lock_irq(&common->lock);
+
+       for (i = 0; i < common->fsg_num_buffers; ++i) {
+               bh = &common->buffhds[i];
+               bh->state = BUF_STATE_EMPTY;
+       }
+       common->next_buffhd_to_fill = &common->buffhds[0];
+       common->next_buffhd_to_drain = &common->buffhds[0];
+       exception_req_tag = common->exception_req_tag;
+       old_state = common->state;
+
+       if (old_state == FSG_STATE_ABORT_BULK_OUT)
+               common->state = FSG_STATE_STATUS_PHASE;
+       else {
+               for (i = 0; i < common->nluns; ++i) {
+                       curlun = common->luns[i];
+                       if (!curlun)
+                               continue;
+                       curlun->prevent_medium_removal = 0;
+                       curlun->sense_data = SS_NO_SENSE;
+                       curlun->unit_attention_data = SS_NO_SENSE;
+                       curlun->sense_data_info = 0;
+                       curlun->info_valid = 0;
+               }
+               common->state = FSG_STATE_IDLE;
+       }
+       spin_unlock_irq(&common->lock);
+
+       /* Carry out any extra actions required for the exception */
+       switch (old_state) {
+       case FSG_STATE_ABORT_BULK_OUT:
+               send_status(common);
+               spin_lock_irq(&common->lock);
+               if (common->state == FSG_STATE_STATUS_PHASE)
+                       common->state = FSG_STATE_IDLE;
+               spin_unlock_irq(&common->lock);
+               break;
+
+       case FSG_STATE_RESET:
+               /*
+                * In case we were forced against our will to halt a
+                * bulk endpoint, clear the halt now.  (The SuperH UDC
+                * requires this.)
+                */
+               if (!fsg_is_set(common))
+                       break;
+               if (test_and_clear_bit(IGNORE_BULK_OUT,
+                                      &common->fsg->atomic_bitflags))
+                       usb_ep_clear_halt(common->fsg->bulk_in);
+
+               if (common->ep0_req_tag == exception_req_tag)
+                       ep0_queue(common);      /* Complete the status stage */
+
+               /*
+                * Technically this should go here, but it would only be
+                * a waste of time.  Ditto for the INTERFACE_CHANGE and
+                * CONFIG_CHANGE cases.
+                */
+               /* for (i = 0; i < common->nluns; ++i) */
+               /*      if (common->luns[i]) */
+               /*              common->luns[i]->unit_attention_data = */
+               /*                      SS_RESET_OCCURRED;  */
+               break;
+
+       case FSG_STATE_CONFIG_CHANGE:
+               do_set_interface(common, common->new_fsg);
+               if (common->new_fsg)
+                       usb_composite_setup_continue(common->cdev);
+               break;
+
+       case FSG_STATE_EXIT:
+       case FSG_STATE_TERMINATED:
+               do_set_interface(common, NULL);         /* Free resources */
+               spin_lock_irq(&common->lock);
+               common->state = FSG_STATE_TERMINATED;   /* Stop the thread */
+               spin_unlock_irq(&common->lock);
+               break;
+
+       case FSG_STATE_INTERFACE_CHANGE:
+       case FSG_STATE_DISCONNECT:
+       case FSG_STATE_COMMAND_PHASE:
+       case FSG_STATE_DATA_PHASE:
+       case FSG_STATE_STATUS_PHASE:
+       case FSG_STATE_IDLE:
+               break;
+       }
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int fsg_main_thread(void *common_)
+{
+       struct fsg_common       *common = common_;
+
+       /*
+        * Allow the thread to be killed by a signal, but set the signal mask
+        * to block everything but INT, TERM, KILL, and USR1.
+        */
+       allow_signal(SIGINT);
+       allow_signal(SIGTERM);
+       allow_signal(SIGKILL);
+       allow_signal(SIGUSR1);
+
+       /* Allow the thread to be frozen */
+       set_freezable();
+
+       /*
+        * Arrange for userspace references to be interpreted as kernel
+        * pointers.  That way we can pass a kernel pointer to a routine
+        * that expects a __user pointer and it will work okay.
+        */
+       set_fs(get_ds());
+
+       /* The main loop */
+       while (common->state != FSG_STATE_TERMINATED) {
+               if (exception_in_progress(common) || signal_pending(current)) {
+                       handle_exception(common);
+                       continue;
+               }
+
+               if (!common->running) {
+                       sleep_thread(common, true);
+                       continue;
+               }
+
+               if (get_next_command(common))
+                       continue;
+
+               spin_lock_irq(&common->lock);
+               if (!exception_in_progress(common))
+                       common->state = FSG_STATE_DATA_PHASE;
+               spin_unlock_irq(&common->lock);
+
+               if (do_scsi_command(common) || finish_reply(common))
+                       continue;
+
+               spin_lock_irq(&common->lock);
+               if (!exception_in_progress(common))
+                       common->state = FSG_STATE_STATUS_PHASE;
+               spin_unlock_irq(&common->lock);
+
+               if (send_status(common))
+                       continue;
+
+               spin_lock_irq(&common->lock);
+               if (!exception_in_progress(common))
+                       common->state = FSG_STATE_IDLE;
+               spin_unlock_irq(&common->lock);
+       }
+
+       spin_lock_irq(&common->lock);
+       common->thread_task = NULL;
+       spin_unlock_irq(&common->lock);
+
+       if (!common->ops || !common->ops->thread_exits
+        || common->ops->thread_exits(common) < 0) {
+               struct fsg_lun **curlun_it = common->luns;
+               unsigned i = common->nluns;
+
+               down_write(&common->filesem);
+               for (; i--; ++curlun_it) {
+                       struct fsg_lun *curlun = *curlun_it;
+                       if (!curlun || !fsg_lun_is_open(curlun))
+                               continue;
+
+                       fsg_lun_close(curlun);
+                       curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
+               }
+               up_write(&common->filesem);
+       }
+
+       /* Let fsg_unbind() know the thread has exited */
+       complete_and_exit(&common->thread_notifier, 0);
+}
+
+
+/*************************** DEVICE ATTRIBUTES ***************************/
+
+static ssize_t ro_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct fsg_lun          *curlun = fsg_lun_from_dev(dev);
+
+       return fsg_show_ro(curlun, buf);
+}
+
+static ssize_t nofua_show(struct device *dev, struct device_attribute *attr,
+                         char *buf)
+{
+       struct fsg_lun          *curlun = fsg_lun_from_dev(dev);
+
+       return fsg_show_nofua(curlun, buf);
+}
+
+static ssize_t file_show(struct device *dev, struct device_attribute *attr,
+                        char *buf)
+{
+       struct fsg_lun          *curlun = fsg_lun_from_dev(dev);
+       struct rw_semaphore     *filesem = dev_get_drvdata(dev);
+
+       return fsg_show_file(curlun, filesem, buf);
+}
+
+static ssize_t ro_store(struct device *dev, struct device_attribute *attr,
+                       const char *buf, size_t count)
+{
+       struct fsg_lun          *curlun = fsg_lun_from_dev(dev);
+       struct rw_semaphore     *filesem = dev_get_drvdata(dev);
+
+       return fsg_store_ro(curlun, filesem, buf, count);
+}
+
+static ssize_t nofua_store(struct device *dev, struct device_attribute *attr,
+                          const char *buf, size_t count)
+{
+       struct fsg_lun          *curlun = fsg_lun_from_dev(dev);
+
+       return fsg_store_nofua(curlun, buf, count);
+}
+
+static ssize_t file_store(struct device *dev, struct device_attribute *attr,
+                         const char *buf, size_t count)
+{
+       struct fsg_lun          *curlun = fsg_lun_from_dev(dev);
+       struct rw_semaphore     *filesem = dev_get_drvdata(dev);
+
+       return fsg_store_file(curlun, filesem, buf, count);
+}
+
+static DEVICE_ATTR_RW(ro);
+static DEVICE_ATTR_RW(nofua);
+static DEVICE_ATTR_RW(file);
+
+static struct device_attribute dev_attr_ro_cdrom = __ATTR_RO(ro);
+static struct device_attribute dev_attr_file_nonremovable = __ATTR_RO(file);
+
+
+/****************************** FSG COMMON ******************************/
+
+static void fsg_common_release(struct kref *ref);
+
+static void fsg_lun_release(struct device *dev)
+{
+       /* Nothing needs to be done */
+}
+
+void fsg_common_get(struct fsg_common *common)
+{
+       kref_get(&common->ref);
+}
+EXPORT_SYMBOL_GPL(fsg_common_get);
+
+void fsg_common_put(struct fsg_common *common)
+{
+       kref_put(&common->ref, fsg_common_release);
+}
+EXPORT_SYMBOL_GPL(fsg_common_put);
+
+/* check if fsg_num_buffers is within a valid range */
+static inline int fsg_num_buffers_validate(unsigned int fsg_num_buffers)
+{
+       if (fsg_num_buffers >= 2 && fsg_num_buffers <= 4)
+               return 0;
+       pr_err("fsg_num_buffers %u is out of range (%d to %d)\n",
+              fsg_num_buffers, 2, 4);
+       return -EINVAL;
+}
+
+static struct fsg_common *fsg_common_setup(struct fsg_common *common)
+{
+       if (!common) {
+               common = kzalloc(sizeof(*common), GFP_KERNEL);
+               if (!common)
+                       return ERR_PTR(-ENOMEM);
+               common->free_storage_on_release = 1;
+       } else {
+               common->free_storage_on_release = 0;
+       }
+       init_rwsem(&common->filesem);
+       spin_lock_init(&common->lock);
+       kref_init(&common->ref);
+       init_completion(&common->thread_notifier);
+       init_waitqueue_head(&common->fsg_wait);
+       common->state = FSG_STATE_TERMINATED;
+
+       return common;
+}
+
+void fsg_common_set_sysfs(struct fsg_common *common, bool sysfs)
+{
+       common->sysfs = sysfs;
+}
+EXPORT_SYMBOL_GPL(fsg_common_set_sysfs);
+
+static void _fsg_common_free_buffers(struct fsg_buffhd *buffhds, unsigned n)
+{
+       if (buffhds) {
+               struct fsg_buffhd *bh = buffhds;
+               while (n--) {
+                       kfree(bh->buf);
+                       ++bh;
+               }
+               kfree(buffhds);
+       }
+}
+
+int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n)
+{
+       struct fsg_buffhd *bh, *buffhds;
+       int i, rc;
+
+       rc = fsg_num_buffers_validate(n);
+       if (rc != 0)
+               return rc;
+
+       buffhds = kcalloc(n, sizeof(*buffhds), GFP_KERNEL);
+       if (!buffhds)
+               return -ENOMEM;
+
+       /* Data buffers cyclic list */
+       bh = buffhds;
+       i = n;
+       goto buffhds_first_it;
+       do {
+               bh->next = bh + 1;
+               ++bh;
+buffhds_first_it:
+               bh->buf = kmalloc(FSG_BUFLEN, GFP_KERNEL);
+               if (unlikely(!bh->buf))
+                       goto error_release;
+       } while (--i);
+       bh->next = buffhds;
+
+       _fsg_common_free_buffers(common->buffhds, common->fsg_num_buffers);
+       common->fsg_num_buffers = n;
+       common->buffhds = buffhds;
+
+       return 0;
+
+error_release:
+       /*
+        * "buf"s pointed to by heads after n - i are NULL
+        * so releasing them won't hurt
+        */
+       _fsg_common_free_buffers(buffhds, n);
+
+       return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(fsg_common_set_num_buffers);
+
+static inline void fsg_common_remove_sysfs(struct fsg_lun *lun)
+{
+       device_remove_file(&lun->dev, &dev_attr_nofua);
+       /*
+        * device_remove_file() =>
+        *
+        * here the attr (e.g. dev_attr_ro) is only used to be passed to:
+        *
+        *      sysfs_remove_file() =>
+        *
+        *      here e.g. both dev_attr_ro_cdrom and dev_attr_ro are in
+        *      the same namespace and
+        *      from here only attr->name is passed to:
+        *
+        *              sysfs_hash_and_remove()
+        *
+        *              attr->name is the same for dev_attr_ro_cdrom and
+        *              dev_attr_ro
+        *              attr->name is the same for dev_attr_file and
+        *              dev_attr_file_nonremovable
+        *
+        * so we don't differentiate between removing e.g. dev_attr_ro_cdrom
+        * and dev_attr_ro
+        */
+       device_remove_file(&lun->dev, &dev_attr_ro);
+       device_remove_file(&lun->dev, &dev_attr_file);
+}
+
+void fsg_common_remove_lun(struct fsg_lun *lun, bool sysfs)
+{
+       if (sysfs) {
+               fsg_common_remove_sysfs(lun);
+               device_unregister(&lun->dev);
+       }
+       fsg_lun_close(lun);
+       kfree(lun);
+}
+EXPORT_SYMBOL_GPL(fsg_common_remove_lun);
+
+static void _fsg_common_remove_luns(struct fsg_common *common, int n)
+{
+       int i;
+
+       for (i = 0; i < n; ++i)
+               if (common->luns[i]) {
+                       fsg_common_remove_lun(common->luns[i], common->sysfs);
+                       common->luns[i] = NULL;
+               }
+}
+EXPORT_SYMBOL_GPL(fsg_common_remove_luns);
+
+void fsg_common_remove_luns(struct fsg_common *common)
+{
+       _fsg_common_remove_luns(common, common->nluns);
+}
+
+void fsg_common_free_luns(struct fsg_common *common)
+{
+       fsg_common_remove_luns(common);
+       kfree(common->luns);
+       common->luns = NULL;
+}
+EXPORT_SYMBOL_GPL(fsg_common_free_luns);
+
+int fsg_common_set_nluns(struct fsg_common *common, int nluns)
+{
+       struct fsg_lun **curlun;
+
+       /* Find out how many LUNs there should be */
+       if (nluns < 1 || nluns > FSG_MAX_LUNS) {
+               pr_err("invalid number of LUNs: %u\n", nluns);
+               return -EINVAL;
+       }
+
+       curlun = kcalloc(nluns, sizeof(*curlun), GFP_KERNEL);
+       if (unlikely(!curlun))
+               return -ENOMEM;
+
+       if (common->luns)
+               fsg_common_free_luns(common);
+
+       common->luns = curlun;
+       common->nluns = nluns;
+
+       pr_info("Number of LUNs=%d\n", common->nluns);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(fsg_common_set_nluns);
+
+void fsg_common_set_ops(struct fsg_common *common,
+                       const struct fsg_operations *ops)
+{
+       common->ops = ops;
+}
+EXPORT_SYMBOL_GPL(fsg_common_set_ops);
+
+void fsg_common_free_buffers(struct fsg_common *common)
+{
+       _fsg_common_free_buffers(common->buffhds, common->fsg_num_buffers);
+       common->buffhds = NULL;
+}
+EXPORT_SYMBOL_GPL(fsg_common_free_buffers);
+
+int fsg_common_set_cdev(struct fsg_common *common,
+                        struct usb_composite_dev *cdev, bool can_stall)
+{
+       struct usb_string *us;
+
+       common->gadget = cdev->gadget;
+       common->ep0 = cdev->gadget->ep0;
+       common->ep0req = cdev->req;
+       common->cdev = cdev;
+
+       us = usb_gstrings_attach(cdev, fsg_strings_array,
+                                ARRAY_SIZE(fsg_strings));
+       if (IS_ERR(us))
+               return PTR_ERR(us);
+
+       fsg_intf_desc.iInterface = us[FSG_STRING_INTERFACE].id;
+
+       /*
+        * Some peripheral controllers are known not to be able to
+        * halt bulk endpoints correctly.  If one of them is present,
+        * disable stalls.
+        */
+       common->can_stall = can_stall && !(gadget_is_at91(common->gadget));
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(fsg_common_set_cdev);
+
+static inline int fsg_common_add_sysfs(struct fsg_common *common,
+                                      struct fsg_lun *lun)
+{
+       int rc;
+
+       rc = device_register(&lun->dev);
+       if (rc) {
+               put_device(&lun->dev);
+               return rc;
+       }
+
+       rc = device_create_file(&lun->dev,
+                               lun->cdrom
+                             ? &dev_attr_ro_cdrom
+                             : &dev_attr_ro);
+       if (rc)
+               goto error;
+       rc = device_create_file(&lun->dev,
+                               lun->removable
+                             ? &dev_attr_file
+                             : &dev_attr_file_nonremovable);
+       if (rc)
+               goto error;
+       rc = device_create_file(&lun->dev, &dev_attr_nofua);
+       if (rc)
+               goto error;
+
+       return 0;
+
+error:
+       /* removing nonexistent files is a no-op */
+       fsg_common_remove_sysfs(lun);
+       device_unregister(&lun->dev);
+       return rc;
+}
+
+int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg,
+                         unsigned int id, const char *name,
+                         const char **name_pfx)
+{
+       struct fsg_lun *lun;
+       char *pathbuf, *p;
+       int rc = -ENOMEM;
+
+       if (!common->nluns || !common->luns)
+               return -ENODEV;
+
+       if (common->luns[id])
+               return -EBUSY;
+
+       if (!cfg->filename && !cfg->removable) {
+               pr_err("no file given for LUN%d\n", id);
+               return -EINVAL;
+       }
+
+       lun = kzalloc(sizeof(*lun), GFP_KERNEL);
+       if (!lun)
+               return -ENOMEM;
+
+       lun->name_pfx = name_pfx;
+
+       lun->cdrom = !!cfg->cdrom;
+       lun->ro = cfg->cdrom || cfg->ro;
+       lun->initially_ro = lun->ro;
+       lun->removable = !!cfg->removable;
+
+       if (!common->sysfs) {
+               /* we DON'T own the name!*/
+               lun->name = name;
+       } else {
+               lun->dev.release = fsg_lun_release;
+               lun->dev.parent = &common->gadget->dev;
+               dev_set_drvdata(&lun->dev, &common->filesem);
+               dev_set_name(&lun->dev, "%s", name);
+               lun->name = dev_name(&lun->dev);
+
+               rc = fsg_common_add_sysfs(common, lun);
+               if (rc) {
+                       pr_info("failed to register LUN%d: %d\n", id, rc);
+                       goto error_sysfs;
+               }
+       }
+
+       common->luns[id] = lun;
+
+       if (cfg->filename) {
+               rc = fsg_lun_open(lun, cfg->filename);
+               if (rc)
+                       goto error_lun;
+       }
+
+       pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
+       p = "(no medium)";
+       if (fsg_lun_is_open(lun)) {
+               p = "(error)";
+               if (pathbuf) {
+                       p = d_path(&lun->filp->f_path, pathbuf, PATH_MAX);
+                       if (IS_ERR(p))
+                               p = "(error)";
+               }
+       }
+       pr_info("LUN: %s%s%sfile: %s\n",
+             lun->removable ? "removable " : "",
+             lun->ro ? "read only " : "",
+             lun->cdrom ? "CD-ROM " : "",
+             p);
+       kfree(pathbuf);
+
+       return 0;
+
+error_lun:
+       if (common->sysfs) {
+               fsg_common_remove_sysfs(lun);
+               device_unregister(&lun->dev);
+       }
+       fsg_lun_close(lun);
+       common->luns[id] = NULL;
+error_sysfs:
+       kfree(lun);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(fsg_common_create_lun);
+
+int fsg_common_create_luns(struct fsg_common *common, struct fsg_config *cfg)
+{
+       char buf[8]; /* enough for 100000000 different numbers, decimal */
+       int i, rc;
+
+       for (i = 0; i < common->nluns; ++i) {
+               snprintf(buf, sizeof(buf), "lun%d", i);
+               rc = fsg_common_create_lun(common, &cfg->luns[i], i, buf, NULL);
+               if (rc)
+                       goto fail;
+       }
+
+       pr_info("Number of LUNs=%d\n", common->nluns);
+
+       return 0;
+
+fail:
+       _fsg_common_remove_luns(common, i);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(fsg_common_create_luns);
+
+void fsg_common_set_inquiry_string(struct fsg_common *common, const char *vn,
+                                  const char *pn)
+{
+       int i;
+
+       /* Prepare inquiryString */
+       i = get_default_bcdDevice();
+       snprintf(common->inquiry_string, sizeof(common->inquiry_string),
+                "%-8s%-16s%04x", vn ?: "Linux",
+                /* Assume product name dependent on the first LUN */
+                pn ?: ((*common->luns)->cdrom
+                    ? "File-CD Gadget"
+                    : "File-Stor Gadget"),
+                i);
+}
+EXPORT_SYMBOL_GPL(fsg_common_set_inquiry_string);
+
+int fsg_common_run_thread(struct fsg_common *common)
+{
+       common->state = FSG_STATE_IDLE;
+       /* Tell the thread to start working */
+       common->thread_task =
+               kthread_create(fsg_main_thread, common, "file-storage");
+       if (IS_ERR(common->thread_task)) {
+               common->state = FSG_STATE_TERMINATED;
+               return PTR_ERR(common->thread_task);
+       }
+
+       DBG(common, "I/O thread pid: %d\n", task_pid_nr(common->thread_task));
+
+       wake_up_process(common->thread_task);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(fsg_common_run_thread);
+
+static void fsg_common_release(struct kref *ref)
+{
+       struct fsg_common *common = container_of(ref, struct fsg_common, ref);
+
+       /* If the thread isn't already dead, tell it to exit now */
+       if (common->state != FSG_STATE_TERMINATED) {
+               raise_exception(common, FSG_STATE_EXIT);
+               wait_for_completion(&common->thread_notifier);
+       }
+
+       if (likely(common->luns)) {
+               struct fsg_lun **lun_it = common->luns;
+               unsigned i = common->nluns;
+
+               /* In error recovery common->nluns may be zero. */
+               for (; i; --i, ++lun_it) {
+                       struct fsg_lun *lun = *lun_it;
+                       if (!lun)
+                               continue;
+                       if (common->sysfs)
+                               fsg_common_remove_sysfs(lun);
+                       fsg_lun_close(lun);
+                       if (common->sysfs)
+                               device_unregister(&lun->dev);
+                       kfree(lun);
+               }
+
+               kfree(common->luns);
+       }
+
+       _fsg_common_free_buffers(common->buffhds, common->fsg_num_buffers);
+       if (common->free_storage_on_release)
+               kfree(common);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct fsg_dev          *fsg = fsg_from_func(f);
+       struct usb_gadget       *gadget = c->cdev->gadget;
+       int                     i;
+       struct usb_ep           *ep;
+       unsigned                max_burst;
+       int                     ret;
+       struct fsg_opts         *opts;
+
+       opts = fsg_opts_from_func_inst(f->fi);
+       if (!opts->no_configfs) {
+               ret = fsg_common_set_cdev(fsg->common, c->cdev,
+                                         fsg->common->can_stall);
+               if (ret)
+                       return ret;
+               fsg_common_set_inquiry_string(fsg->common, NULL, NULL);
+               ret = fsg_common_run_thread(fsg->common);
+               if (ret)
+                       return ret;
+       }
+
+       fsg->gadget = gadget;
+
+       /* New interface */
+       i = usb_interface_id(c, f);
+       if (i < 0)
+               return i;
+       fsg_intf_desc.bInterfaceNumber = i;
+       fsg->interface_number = i;
+
+       /* Find all the endpoints we will use */
+       ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc);
+       if (!ep)
+               goto autoconf_fail;
+       ep->driver_data = fsg->common;  /* claim the endpoint */
+       fsg->bulk_in = ep;
+
+       ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc);
+       if (!ep)
+               goto autoconf_fail;
+       ep->driver_data = fsg->common;  /* claim the endpoint */
+       fsg->bulk_out = ep;
+
+       /* Assume endpoint addresses are the same for both speeds */
+       fsg_hs_bulk_in_desc.bEndpointAddress =
+               fsg_fs_bulk_in_desc.bEndpointAddress;
+       fsg_hs_bulk_out_desc.bEndpointAddress =
+               fsg_fs_bulk_out_desc.bEndpointAddress;
+
+       /* Calculate bMaxBurst, we know packet size is 1024 */
+       max_burst = min_t(unsigned, FSG_BUFLEN / 1024, 15);
+
+       fsg_ss_bulk_in_desc.bEndpointAddress =
+               fsg_fs_bulk_in_desc.bEndpointAddress;
+       fsg_ss_bulk_in_comp_desc.bMaxBurst = max_burst;
+
+       fsg_ss_bulk_out_desc.bEndpointAddress =
+               fsg_fs_bulk_out_desc.bEndpointAddress;
+       fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst;
+
+       ret = usb_assign_descriptors(f, fsg_fs_function, fsg_hs_function,
+                       fsg_ss_function);
+       if (ret)
+               goto autoconf_fail;
+
+       return 0;
+
+autoconf_fail:
+       ERROR(fsg, "unable to autoconfigure all endpoints\n");
+       return -ENOTSUPP;
+}
+
+/****************************** ALLOCATE FUNCTION *************************/
+
+static void fsg_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct fsg_dev          *fsg = fsg_from_func(f);
+       struct fsg_common       *common = fsg->common;
+
+       DBG(fsg, "unbind\n");
+       if (fsg->common->fsg == fsg) {
+               fsg->common->new_fsg = NULL;
+               raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
+               /* FIXME: make interruptible or killable somehow? */
+               wait_event(common->fsg_wait, common->fsg != fsg);
+       }
+
+       usb_free_all_descriptors(&fsg->function);
+}
+
+static inline struct fsg_lun_opts *to_fsg_lun_opts(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct fsg_lun_opts, group);
+}
+
+static inline struct fsg_opts *to_fsg_opts(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct fsg_opts,
+                           func_inst.group);
+}
+
+CONFIGFS_ATTR_STRUCT(fsg_lun_opts);
+CONFIGFS_ATTR_OPS(fsg_lun_opts);
+
+static void fsg_lun_attr_release(struct config_item *item)
+{
+       struct fsg_lun_opts *lun_opts;
+
+       lun_opts = to_fsg_lun_opts(item);
+       kfree(lun_opts);
+}
+
+static struct configfs_item_operations fsg_lun_item_ops = {
+       .release                = fsg_lun_attr_release,
+       .show_attribute         = fsg_lun_opts_attr_show,
+       .store_attribute        = fsg_lun_opts_attr_store,
+};
+
+static ssize_t fsg_lun_opts_file_show(struct fsg_lun_opts *opts, char *page)
+{
+       struct fsg_opts *fsg_opts;
+
+       fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent);
+
+       return fsg_show_file(opts->lun, &fsg_opts->common->filesem, page);
+}
+
+static ssize_t fsg_lun_opts_file_store(struct fsg_lun_opts *opts,
+                                      const char *page, size_t len)
+{
+       struct fsg_opts *fsg_opts;
+
+       fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent);
+
+       return fsg_store_file(opts->lun, &fsg_opts->common->filesem, page, len);
+}
+
+static struct fsg_lun_opts_attribute fsg_lun_opts_file =
+       __CONFIGFS_ATTR(file, S_IRUGO | S_IWUSR, fsg_lun_opts_file_show,
+                       fsg_lun_opts_file_store);
+
+static ssize_t fsg_lun_opts_ro_show(struct fsg_lun_opts *opts, char *page)
+{
+       return fsg_show_ro(opts->lun, page);
+}
+
+static ssize_t fsg_lun_opts_ro_store(struct fsg_lun_opts *opts,
+                                      const char *page, size_t len)
+{
+       struct fsg_opts *fsg_opts;
+
+       fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent);
+
+       return fsg_store_ro(opts->lun, &fsg_opts->common->filesem, page, len);
+}
+
+static struct fsg_lun_opts_attribute fsg_lun_opts_ro =
+       __CONFIGFS_ATTR(ro, S_IRUGO | S_IWUSR, fsg_lun_opts_ro_show,
+                       fsg_lun_opts_ro_store);
+
+static ssize_t fsg_lun_opts_removable_show(struct fsg_lun_opts *opts,
+                                          char *page)
+{
+       return fsg_show_removable(opts->lun, page);
+}
+
+static ssize_t fsg_lun_opts_removable_store(struct fsg_lun_opts *opts,
+                                      const char *page, size_t len)
+{
+       return fsg_store_removable(opts->lun, page, len);
+}
+
+static struct fsg_lun_opts_attribute fsg_lun_opts_removable =
+       __CONFIGFS_ATTR(removable, S_IRUGO | S_IWUSR,
+                       fsg_lun_opts_removable_show,
+                       fsg_lun_opts_removable_store);
+
+static ssize_t fsg_lun_opts_cdrom_show(struct fsg_lun_opts *opts, char *page)
+{
+       return fsg_show_cdrom(opts->lun, page);
+}
+
+static ssize_t fsg_lun_opts_cdrom_store(struct fsg_lun_opts *opts,
+                                      const char *page, size_t len)
+{
+       struct fsg_opts *fsg_opts;
+
+       fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent);
+
+       return fsg_store_cdrom(opts->lun, &fsg_opts->common->filesem, page,
+                              len);
+}
+
+static struct fsg_lun_opts_attribute fsg_lun_opts_cdrom =
+       __CONFIGFS_ATTR(cdrom, S_IRUGO | S_IWUSR, fsg_lun_opts_cdrom_show,
+                       fsg_lun_opts_cdrom_store);
+
+static ssize_t fsg_lun_opts_nofua_show(struct fsg_lun_opts *opts, char *page)
+{
+       return fsg_show_nofua(opts->lun, page);
+}
+
+static ssize_t fsg_lun_opts_nofua_store(struct fsg_lun_opts *opts,
+                                      const char *page, size_t len)
+{
+       return fsg_store_nofua(opts->lun, page, len);
+}
+
+static struct fsg_lun_opts_attribute fsg_lun_opts_nofua =
+       __CONFIGFS_ATTR(nofua, S_IRUGO | S_IWUSR, fsg_lun_opts_nofua_show,
+                       fsg_lun_opts_nofua_store);
+
+static struct configfs_attribute *fsg_lun_attrs[] = {
+       &fsg_lun_opts_file.attr,
+       &fsg_lun_opts_ro.attr,
+       &fsg_lun_opts_removable.attr,
+       &fsg_lun_opts_cdrom.attr,
+       &fsg_lun_opts_nofua.attr,
+       NULL,
+};
+
+static struct config_item_type fsg_lun_type = {
+       .ct_item_ops    = &fsg_lun_item_ops,
+       .ct_attrs       = fsg_lun_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+static struct config_group *fsg_lun_make(struct config_group *group,
+                                        const char *name)
+{
+       struct fsg_lun_opts *opts;
+       struct fsg_opts *fsg_opts;
+       struct fsg_lun_config config;
+       char *num_str;
+       u8 num;
+       int ret;
+
+       num_str = strchr(name, '.');
+       if (!num_str) {
+               pr_err("Unable to locate . in LUN.NUMBER\n");
+               return ERR_PTR(-EINVAL);
+       }
+       num_str++;
+
+       ret = kstrtou8(num_str, 0, &num);
+       if (ret)
+               return ERR_PTR(ret);
+
+       fsg_opts = to_fsg_opts(&group->cg_item);
+       if (num >= FSG_MAX_LUNS)
+               return ERR_PTR(-ERANGE);
+
+       mutex_lock(&fsg_opts->lock);
+       if (fsg_opts->refcnt || fsg_opts->common->luns[num]) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+       if (!opts) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       memset(&config, 0, sizeof(config));
+       config.removable = true;
+
+       ret = fsg_common_create_lun(fsg_opts->common, &config, num, name,
+                                   (const char **)&group->cg_item.ci_name);
+       if (ret) {
+               kfree(opts);
+               goto out;
+       }
+       opts->lun = fsg_opts->common->luns[num];
+       opts->lun_id = num;
+       mutex_unlock(&fsg_opts->lock);
+
+       config_group_init_type_name(&opts->group, name, &fsg_lun_type);
+
+       return &opts->group;
+out:
+       mutex_unlock(&fsg_opts->lock);
+       return ERR_PTR(ret);
+}
+
+static void fsg_lun_drop(struct config_group *group, struct config_item *item)
+{
+       struct fsg_lun_opts *lun_opts;
+       struct fsg_opts *fsg_opts;
+
+       lun_opts = to_fsg_lun_opts(item);
+       fsg_opts = to_fsg_opts(&group->cg_item);
+
+       mutex_lock(&fsg_opts->lock);
+       if (fsg_opts->refcnt) {
+               struct config_item *gadget;
+
+               gadget = group->cg_item.ci_parent->ci_parent;
+               unregister_gadget_item(gadget);
+       }
+
+       fsg_common_remove_lun(lun_opts->lun, fsg_opts->common->sysfs);
+       fsg_opts->common->luns[lun_opts->lun_id] = NULL;
+       lun_opts->lun_id = 0;
+       mutex_unlock(&fsg_opts->lock);
+
+       config_item_put(item);
+}
+
+CONFIGFS_ATTR_STRUCT(fsg_opts);
+CONFIGFS_ATTR_OPS(fsg_opts);
+
+static void fsg_attr_release(struct config_item *item)
+{
+       struct fsg_opts *opts = to_fsg_opts(item);
+
+       usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations fsg_item_ops = {
+       .release                = fsg_attr_release,
+       .show_attribute         = fsg_opts_attr_show,
+       .store_attribute        = fsg_opts_attr_store,
+};
+
+static ssize_t fsg_opts_stall_show(struct fsg_opts *opts, char *page)
+{
+       int result;
+
+       mutex_lock(&opts->lock);
+       result = sprintf(page, "%d", opts->common->can_stall);
+       mutex_unlock(&opts->lock);
+
+       return result;
+}
+
+static ssize_t fsg_opts_stall_store(struct fsg_opts *opts, const char *page,
+                                   size_t len)
+{
+       int ret;
+       bool stall;
+
+       mutex_lock(&opts->lock);
+
+       if (opts->refcnt) {
+               mutex_unlock(&opts->lock);
+               return -EBUSY;
+       }
+
+       ret = strtobool(page, &stall);
+       if (!ret) {
+               opts->common->can_stall = stall;
+               ret = len;
+       }
+
+       mutex_unlock(&opts->lock);
+
+       return ret;
+}
+
+static struct fsg_opts_attribute fsg_opts_stall =
+       __CONFIGFS_ATTR(stall, S_IRUGO | S_IWUSR, fsg_opts_stall_show,
+                       fsg_opts_stall_store);
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+static ssize_t fsg_opts_num_buffers_show(struct fsg_opts *opts, char *page)
+{
+       int result;
+
+       mutex_lock(&opts->lock);
+       result = sprintf(page, "%d", opts->common->fsg_num_buffers);
+       mutex_unlock(&opts->lock);
+
+       return result;
+}
+
+static ssize_t fsg_opts_num_buffers_store(struct fsg_opts *opts,
+                                         const char *page, size_t len)
+{
+       int ret;
+       u8 num;
+
+       mutex_lock(&opts->lock);
+       if (opts->refcnt) {
+               ret = -EBUSY;
+               goto end;
+       }
+       ret = kstrtou8(page, 0, &num);
+       if (ret)
+               goto end;
+
+       ret = fsg_num_buffers_validate(num);
+       if (ret)
+               goto end;
+
+       fsg_common_set_num_buffers(opts->common, num);
+       ret = len;
+
+end:
+       mutex_unlock(&opts->lock);
+       return ret;
+}
+
+static struct fsg_opts_attribute fsg_opts_num_buffers =
+       __CONFIGFS_ATTR(num_buffers, S_IRUGO | S_IWUSR,
+                       fsg_opts_num_buffers_show,
+                       fsg_opts_num_buffers_store);
+
+#endif
+
+static struct configfs_attribute *fsg_attrs[] = {
+       &fsg_opts_stall.attr,
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+       &fsg_opts_num_buffers.attr,
+#endif
+       NULL,
+};
+
+static struct configfs_group_operations fsg_group_ops = {
+       .make_group     = fsg_lun_make,
+       .drop_item      = fsg_lun_drop,
+};
+
+static struct config_item_type fsg_func_type = {
+       .ct_item_ops    = &fsg_item_ops,
+       .ct_group_ops   = &fsg_group_ops,
+       .ct_attrs       = fsg_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+static void fsg_free_inst(struct usb_function_instance *fi)
+{
+       struct fsg_opts *opts;
+
+       opts = fsg_opts_from_func_inst(fi);
+       fsg_common_put(opts->common);
+       kfree(opts);
+}
+
+static struct usb_function_instance *fsg_alloc_inst(void)
+{
+       struct fsg_opts *opts;
+       struct fsg_lun_config config;
+       int rc;
+
+       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+       if (!opts)
+               return ERR_PTR(-ENOMEM);
+       mutex_init(&opts->lock);
+       opts->func_inst.free_func_inst = fsg_free_inst;
+       opts->common = fsg_common_setup(opts->common);
+       if (IS_ERR(opts->common)) {
+               rc = PTR_ERR(opts->common);
+               goto release_opts;
+       }
+       rc = fsg_common_set_nluns(opts->common, FSG_MAX_LUNS);
+       if (rc)
+               goto release_opts;
+
+       rc = fsg_common_set_num_buffers(opts->common,
+                                       CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS);
+       if (rc)
+               goto release_luns;
+
+       pr_info(FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n");
+
+       memset(&config, 0, sizeof(config));
+       config.removable = true;
+       rc = fsg_common_create_lun(opts->common, &config, 0, "lun.0",
+                       (const char **)&opts->func_inst.group.cg_item.ci_name);
+       opts->lun0.lun = opts->common->luns[0];
+       opts->lun0.lun_id = 0;
+       config_group_init_type_name(&opts->lun0.group, "lun.0", &fsg_lun_type);
+       opts->default_groups[0] = &opts->lun0.group;
+       opts->func_inst.group.default_groups = opts->default_groups;
+
+       config_group_init_type_name(&opts->func_inst.group, "", &fsg_func_type);
+
+       return &opts->func_inst;
+
+release_luns:
+       kfree(opts->common->luns);
+release_opts:
+       kfree(opts);
+       return ERR_PTR(rc);
+}
+
+static void fsg_free(struct usb_function *f)
+{
+       struct fsg_dev *fsg;
+       struct fsg_opts *opts;
+
+       fsg = container_of(f, struct fsg_dev, function);
+       opts = container_of(f->fi, struct fsg_opts, func_inst);
+
+       mutex_lock(&opts->lock);
+       opts->refcnt--;
+       mutex_unlock(&opts->lock);
+
+       kfree(fsg);
+}
+
+static struct usb_function *fsg_alloc(struct usb_function_instance *fi)
+{
+       struct fsg_opts *opts = fsg_opts_from_func_inst(fi);
+       struct fsg_common *common = opts->common;
+       struct fsg_dev *fsg;
+
+       fsg = kzalloc(sizeof(*fsg), GFP_KERNEL);
+       if (unlikely(!fsg))
+               return ERR_PTR(-ENOMEM);
+
+       mutex_lock(&opts->lock);
+       opts->refcnt++;
+       mutex_unlock(&opts->lock);
+       fsg->function.name      = FSG_DRIVER_DESC;
+       fsg->function.bind      = fsg_bind;
+       fsg->function.unbind    = fsg_unbind;
+       fsg->function.setup     = fsg_setup;
+       fsg->function.set_alt   = fsg_set_alt;
+       fsg->function.disable   = fsg_disable;
+       fsg->function.free_func = fsg_free;
+
+       fsg->common               = common;
+
+       return &fsg->function;
+}
+
+DECLARE_USB_FUNCTION_INIT(mass_storage, fsg_alloc_inst, fsg_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michal Nazarewicz");
+
+/************************* Module parameters *************************/
+
+
+void fsg_config_from_params(struct fsg_config *cfg,
+                      const struct fsg_module_parameters *params,
+                      unsigned int fsg_num_buffers)
+{
+       struct fsg_lun_config *lun;
+       unsigned i;
+
+       /* Configure LUNs */
+       cfg->nluns =
+               min(params->luns ?: (params->file_count ?: 1u),
+                   (unsigned)FSG_MAX_LUNS);
+       for (i = 0, lun = cfg->luns; i < cfg->nluns; ++i, ++lun) {
+               lun->ro = !!params->ro[i];
+               lun->cdrom = !!params->cdrom[i];
+               lun->removable = !!params->removable[i];
+               lun->filename =
+                       params->file_count > i && params->file[i][0]
+                       ? params->file[i]
+                       : NULL;
+       }
+
+       /* Let MSF use defaults */
+       cfg->vendor_name = NULL;
+       cfg->product_name = NULL;
+
+       cfg->ops = NULL;
+       cfg->private_data = NULL;
+
+       /* Finalise */
+       cfg->can_stall = params->stall;
+       cfg->fsg_num_buffers = fsg_num_buffers;
+}
+EXPORT_SYMBOL_GPL(fsg_config_from_params);
+
diff --git a/drivers/usb/gadget/function/f_mass_storage.h b/drivers/usb/gadget/function/f_mass_storage.h
new file mode 100644 (file)
index 0000000..b4866fc
--- /dev/null
@@ -0,0 +1,166 @@
+#ifndef USB_F_MASS_STORAGE_H
+#define USB_F_MASS_STORAGE_H
+
+#include <linux/usb/composite.h>
+#include "storage_common.h"
+
+struct fsg_module_parameters {
+       char            *file[FSG_MAX_LUNS];
+       bool            ro[FSG_MAX_LUNS];
+       bool            removable[FSG_MAX_LUNS];
+       bool            cdrom[FSG_MAX_LUNS];
+       bool            nofua[FSG_MAX_LUNS];
+
+       unsigned int    file_count, ro_count, removable_count, cdrom_count;
+       unsigned int    nofua_count;
+       unsigned int    luns;   /* nluns */
+       bool            stall;  /* can_stall */
+};
+
+#define _FSG_MODULE_PARAM_ARRAY(prefix, params, name, type, desc)      \
+       module_param_array_named(prefix ## name, params.name, type,     \
+                                &prefix ## params.name ## _count,      \
+                                S_IRUGO);                              \
+       MODULE_PARM_DESC(prefix ## name, desc)
+
+#define _FSG_MODULE_PARAM(prefix, params, name, type, desc)            \
+       module_param_named(prefix ## name, params.name, type,           \
+                          S_IRUGO);                                    \
+       MODULE_PARM_DESC(prefix ## name, desc)
+
+#define __FSG_MODULE_PARAMETERS(prefix, params)                                \
+       _FSG_MODULE_PARAM_ARRAY(prefix, params, file, charp,            \
+                               "names of backing files or devices");   \
+       _FSG_MODULE_PARAM_ARRAY(prefix, params, ro, bool,               \
+                               "true to force read-only");             \
+       _FSG_MODULE_PARAM_ARRAY(prefix, params, removable, bool,        \
+                               "true to simulate removable media");    \
+       _FSG_MODULE_PARAM_ARRAY(prefix, params, cdrom, bool,            \
+                               "true to simulate CD-ROM instead of disk"); \
+       _FSG_MODULE_PARAM_ARRAY(prefix, params, nofua, bool,            \
+                               "true to ignore SCSI WRITE(10,12) FUA bit"); \
+       _FSG_MODULE_PARAM(prefix, params, luns, uint,                   \
+                         "number of LUNs");                            \
+       _FSG_MODULE_PARAM(prefix, params, stall, bool,                  \
+                         "false to prevent bulk stalls")
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+#define FSG_MODULE_PARAMETERS(prefix, params)                          \
+       __FSG_MODULE_PARAMETERS(prefix, params);                        \
+       module_param_named(num_buffers, fsg_num_buffers, uint, S_IRUGO);\
+       MODULE_PARM_DESC(num_buffers, "Number of pipeline buffers")
+#else
+
+#define FSG_MODULE_PARAMETERS(prefix, params)                          \
+       __FSG_MODULE_PARAMETERS(prefix, params)
+
+#endif
+
+struct fsg_common;
+
+/* FSF callback functions */
+struct fsg_operations {
+       /*
+        * Callback function to call when thread exits.  If no
+        * callback is set or it returns value lower then zero MSF
+        * will force eject all LUNs it operates on (including those
+        * marked as non-removable or with prevent_medium_removal flag
+        * set).
+        */
+       int (*thread_exits)(struct fsg_common *common);
+};
+
+struct fsg_lun_opts {
+       struct config_group group;
+       struct fsg_lun *lun;
+       int lun_id;
+};
+
+struct fsg_opts {
+       struct fsg_common *common;
+       struct usb_function_instance func_inst;
+       struct fsg_lun_opts lun0;
+       struct config_group *default_groups[2];
+       bool no_configfs; /* for legacy gadgets */
+
+       /*
+        * Read/write access to configfs attributes is handled by configfs.
+        *
+        * This is to protect the data from concurrent access by read/write
+        * and create symlink/remove symlink.
+        */
+       struct mutex                    lock;
+       int                             refcnt;
+};
+
+struct fsg_lun_config {
+       const char *filename;
+       char ro;
+       char removable;
+       char cdrom;
+       char nofua;
+};
+
+struct fsg_config {
+       unsigned nluns;
+       struct fsg_lun_config luns[FSG_MAX_LUNS];
+
+       /* Callback functions. */
+       const struct fsg_operations     *ops;
+       /* Gadget's private data. */
+       void                    *private_data;
+
+       const char *vendor_name;                /*  8 characters or less */
+       const char *product_name;               /* 16 characters or less */
+
+       char                    can_stall;
+       unsigned int            fsg_num_buffers;
+};
+
+static inline struct fsg_opts *
+fsg_opts_from_func_inst(const struct usb_function_instance *fi)
+{
+       return container_of(fi, struct fsg_opts, func_inst);
+}
+
+void fsg_common_get(struct fsg_common *common);
+
+void fsg_common_put(struct fsg_common *common);
+
+void fsg_common_set_sysfs(struct fsg_common *common, bool sysfs);
+
+int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n);
+
+void fsg_common_free_buffers(struct fsg_common *common);
+
+int fsg_common_set_cdev(struct fsg_common *common,
+                       struct usb_composite_dev *cdev, bool can_stall);
+
+void fsg_common_remove_lun(struct fsg_lun *lun, bool sysfs);
+
+void fsg_common_remove_luns(struct fsg_common *common);
+
+void fsg_common_free_luns(struct fsg_common *common);
+
+int fsg_common_set_nluns(struct fsg_common *common, int nluns);
+
+void fsg_common_set_ops(struct fsg_common *common,
+                       const struct fsg_operations *ops);
+
+int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg,
+                         unsigned int id, const char *name,
+                         const char **name_pfx);
+
+int fsg_common_create_luns(struct fsg_common *common, struct fsg_config *cfg);
+
+void fsg_common_set_inquiry_string(struct fsg_common *common, const char *vn,
+                                  const char *pn);
+
+int fsg_common_run_thread(struct fsg_common *common);
+
+void fsg_config_from_params(struct fsg_config *cfg,
+                           const struct fsg_module_parameters *params,
+                           unsigned int fsg_num_buffers);
+
+#endif /* USB_F_MASS_STORAGE_H */
diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
new file mode 100644 (file)
index 0000000..807b31c
--- /dev/null
@@ -0,0 +1,986 @@
+/*
+ * f_midi.c -- USB MIDI class function driver
+ *
+ * Copyright (C) 2006 Thumtronics Pty Ltd.
+ * Developed for Thumtronics by Grey Innovation
+ * Ben Williamson <ben.williamson@greyinnovation.com>
+ *
+ * Rewritten for the composite framework
+ *   Copyright (C) 2011 Daniel Mack <zonque@gmail.com>
+ *
+ * Based on drivers/usb/gadget/f_audio.c,
+ *   Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
+ *   Copyright (C) 2008 Analog Devices, Inc
+ *
+ * and drivers/usb/gadget/midi.c,
+ *   Copyright (C) 2006 Thumtronics Pty Ltd.
+ *   Ben Williamson <ben.williamson@greyinnovation.com>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/rawmidi.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/midi.h>
+
+#include "u_f.h"
+
+MODULE_AUTHOR("Ben Williamson");
+MODULE_LICENSE("GPL v2");
+
+static const char f_midi_shortname[] = "f_midi";
+static const char f_midi_longname[] = "MIDI Gadget";
+
+/*
+ * We can only handle 16 cables on one single endpoint, as cable numbers are
+ * stored in 4-bit fields. And as the interface currently only holds one
+ * single endpoint, this is the maximum number of ports we can allow.
+ */
+#define MAX_PORTS 16
+
+/*
+ * This is a gadget, and the IN/OUT naming is from the host's perspective.
+ * USB -> OUT endpoint -> rawmidi
+ * USB <- IN endpoint  <- rawmidi
+ */
+struct gmidi_in_port {
+       struct f_midi *midi;
+       int active;
+       uint8_t cable;
+       uint8_t state;
+#define STATE_UNKNOWN  0
+#define STATE_1PARAM   1
+#define STATE_2PARAM_1 2
+#define STATE_2PARAM_2 3
+#define STATE_SYSEX_0  4
+#define STATE_SYSEX_1  5
+#define STATE_SYSEX_2  6
+       uint8_t data[2];
+};
+
+struct f_midi {
+       struct usb_function     func;
+       struct usb_gadget       *gadget;
+       struct usb_ep           *in_ep, *out_ep;
+       struct snd_card         *card;
+       struct snd_rawmidi      *rmidi;
+
+       struct snd_rawmidi_substream *in_substream[MAX_PORTS];
+       struct snd_rawmidi_substream *out_substream[MAX_PORTS];
+       struct gmidi_in_port    *in_port[MAX_PORTS];
+
+       unsigned long           out_triggered;
+       struct tasklet_struct   tasklet;
+       unsigned int in_ports;
+       unsigned int out_ports;
+       int index;
+       char *id;
+       unsigned int buflen, qlen;
+};
+
+static inline struct f_midi *func_to_midi(struct usb_function *f)
+{
+       return container_of(f, struct f_midi, func);
+}
+
+static void f_midi_transmit(struct f_midi *midi, struct usb_request *req);
+
+DECLARE_UAC_AC_HEADER_DESCRIPTOR(1);
+DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
+DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(16);
+
+/* B.3.1  Standard AC Interface Descriptor */
+static struct usb_interface_descriptor ac_interface_desc __initdata = {
+       .bLength =              USB_DT_INTERFACE_SIZE,
+       .bDescriptorType =      USB_DT_INTERFACE,
+       /* .bInterfaceNumber =  DYNAMIC */
+       /* .bNumEndpoints =     DYNAMIC */
+       .bInterfaceClass =      USB_CLASS_AUDIO,
+       .bInterfaceSubClass =   USB_SUBCLASS_AUDIOCONTROL,
+       /* .iInterface =        DYNAMIC */
+};
+
+/* B.3.2  Class-Specific AC Interface Descriptor */
+static struct uac1_ac_header_descriptor_1 ac_header_desc __initdata = {
+       .bLength =              UAC_DT_AC_HEADER_SIZE(1),
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype =   USB_MS_HEADER,
+       .bcdADC =               cpu_to_le16(0x0100),
+       .wTotalLength =         cpu_to_le16(UAC_DT_AC_HEADER_SIZE(1)),
+       .bInCollection =        1,
+       /* .baInterfaceNr =     DYNAMIC */
+};
+
+/* B.4.1  Standard MS Interface Descriptor */
+static struct usb_interface_descriptor ms_interface_desc __initdata = {
+       .bLength =              USB_DT_INTERFACE_SIZE,
+       .bDescriptorType =      USB_DT_INTERFACE,
+       /* .bInterfaceNumber =  DYNAMIC */
+       .bNumEndpoints =        2,
+       .bInterfaceClass =      USB_CLASS_AUDIO,
+       .bInterfaceSubClass =   USB_SUBCLASS_MIDISTREAMING,
+       /* .iInterface =        DYNAMIC */
+};
+
+/* B.4.2  Class-Specific MS Interface Descriptor */
+static struct usb_ms_header_descriptor ms_header_desc __initdata = {
+       .bLength =              USB_DT_MS_HEADER_SIZE,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype =   USB_MS_HEADER,
+       .bcdMSC =               cpu_to_le16(0x0100),
+       /* .wTotalLength =      DYNAMIC */
+};
+
+/* B.5.1  Standard Bulk OUT Endpoint Descriptor */
+static struct usb_endpoint_descriptor bulk_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_AUDIO_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+};
+
+/* B.5.2  Class-specific MS Bulk OUT Endpoint Descriptor */
+static struct usb_ms_endpoint_descriptor_16 ms_out_desc = {
+       /* .bLength =           DYNAMIC */
+       .bDescriptorType =      USB_DT_CS_ENDPOINT,
+       .bDescriptorSubtype =   USB_MS_GENERAL,
+       /* .bNumEmbMIDIJack =   DYNAMIC */
+       /* .baAssocJackID =     DYNAMIC */
+};
+
+/* B.6.1  Standard Bulk IN Endpoint Descriptor */
+static struct usb_endpoint_descriptor bulk_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_AUDIO_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+};
+
+/* B.6.2  Class-specific MS Bulk IN Endpoint Descriptor */
+static struct usb_ms_endpoint_descriptor_16 ms_in_desc = {
+       /* .bLength =           DYNAMIC */
+       .bDescriptorType =      USB_DT_CS_ENDPOINT,
+       .bDescriptorSubtype =   USB_MS_GENERAL,
+       /* .bNumEmbMIDIJack =   DYNAMIC */
+       /* .baAssocJackID =     DYNAMIC */
+};
+
+/* string IDs are assigned dynamically */
+
+#define STRING_FUNC_IDX                        0
+
+static struct usb_string midi_string_defs[] = {
+       [STRING_FUNC_IDX].s = "MIDI function",
+       {  } /* end of list */
+};
+
+static struct usb_gadget_strings midi_stringtab = {
+       .language       = 0x0409,       /* en-us */
+       .strings        = midi_string_defs,
+};
+
+static struct usb_gadget_strings *midi_strings[] = {
+       &midi_stringtab,
+       NULL,
+};
+
+static inline struct usb_request *midi_alloc_ep_req(struct usb_ep *ep,
+                                                   unsigned length)
+{
+       return alloc_ep_req(ep, length, length);
+}
+
+static void free_ep_req(struct usb_ep *ep, struct usb_request *req)
+{
+       kfree(req->buf);
+       usb_ep_free_request(ep, req);
+}
+
+static const uint8_t f_midi_cin_length[] = {
+       0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1
+};
+
+/*
+ * Receives a chunk of MIDI data.
+ */
+static void f_midi_read_data(struct usb_ep *ep, int cable,
+                            uint8_t *data, int length)
+{
+       struct f_midi *midi = ep->driver_data;
+       struct snd_rawmidi_substream *substream = midi->out_substream[cable];
+
+       if (!substream)
+               /* Nobody is listening - throw it on the floor. */
+               return;
+
+       if (!test_bit(cable, &midi->out_triggered))
+               return;
+
+       snd_rawmidi_receive(substream, data, length);
+}
+
+static void f_midi_handle_out_data(struct usb_ep *ep, struct usb_request *req)
+{
+       unsigned int i;
+       u8 *buf = req->buf;
+
+       for (i = 0; i + 3 < req->actual; i += 4)
+               if (buf[i] != 0) {
+                       int cable = buf[i] >> 4;
+                       int length = f_midi_cin_length[buf[i] & 0x0f];
+                       f_midi_read_data(ep, cable, &buf[i + 1], length);
+               }
+}
+
+static void
+f_midi_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct f_midi *midi = ep->driver_data;
+       struct usb_composite_dev *cdev = midi->func.config->cdev;
+       int status = req->status;
+
+       switch (status) {
+       case 0:                  /* normal completion */
+               if (ep == midi->out_ep) {
+                       /* We received stuff. req is queued again, below */
+                       f_midi_handle_out_data(ep, req);
+               } else if (ep == midi->in_ep) {
+                       /* Our transmit completed. See if there's more to go.
+                        * f_midi_transmit eats req, don't queue it again. */
+                       f_midi_transmit(midi, req);
+                       return;
+               }
+               break;
+
+       /* this endpoint is normally active while we're configured */
+       case -ECONNABORTED:     /* hardware forced ep reset */
+       case -ECONNRESET:       /* request dequeued */
+       case -ESHUTDOWN:        /* disconnect from host */
+               VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status,
+                               req->actual, req->length);
+               if (ep == midi->out_ep)
+                       f_midi_handle_out_data(ep, req);
+
+               free_ep_req(ep, req);
+               return;
+
+       case -EOVERFLOW:        /* buffer overrun on read means that
+                                * we didn't provide a big enough buffer.
+                                */
+       default:
+               DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name,
+                               status, req->actual, req->length);
+               break;
+       case -EREMOTEIO:        /* short read */
+               break;
+       }
+
+       status = usb_ep_queue(ep, req, GFP_ATOMIC);
+       if (status) {
+               ERROR(cdev, "kill %s:  resubmit %d bytes --> %d\n",
+                               ep->name, req->length, status);
+               usb_ep_set_halt(ep);
+               /* FIXME recover later ... somehow */
+       }
+}
+
+static int f_midi_start_ep(struct f_midi *midi,
+                          struct usb_function *f,
+                          struct usb_ep *ep)
+{
+       int err;
+       struct usb_composite_dev *cdev = f->config->cdev;
+
+       if (ep->driver_data)
+               usb_ep_disable(ep);
+
+       err = config_ep_by_speed(midi->gadget, f, ep);
+       if (err) {
+               ERROR(cdev, "can't configure %s: %d\n", ep->name, err);
+               return err;
+       }
+
+       err = usb_ep_enable(ep);
+       if (err) {
+               ERROR(cdev, "can't start %s: %d\n", ep->name, err);
+               return err;
+       }
+
+       ep->driver_data = midi;
+
+       return 0;
+}
+
+static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+       struct f_midi *midi = func_to_midi(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+       unsigned i;
+       int err;
+
+       err = f_midi_start_ep(midi, f, midi->in_ep);
+       if (err)
+               return err;
+
+       err = f_midi_start_ep(midi, f, midi->out_ep);
+       if (err)
+               return err;
+
+       if (midi->out_ep->driver_data)
+               usb_ep_disable(midi->out_ep);
+
+       err = config_ep_by_speed(midi->gadget, f, midi->out_ep);
+       if (err) {
+               ERROR(cdev, "can't configure %s: %d\n",
+                     midi->out_ep->name, err);
+               return err;
+       }
+
+       err = usb_ep_enable(midi->out_ep);
+       if (err) {
+               ERROR(cdev, "can't start %s: %d\n",
+                     midi->out_ep->name, err);
+               return err;
+       }
+
+       midi->out_ep->driver_data = midi;
+
+       /* allocate a bunch of read buffers and queue them all at once. */
+       for (i = 0; i < midi->qlen && err == 0; i++) {
+               struct usb_request *req =
+                       midi_alloc_ep_req(midi->out_ep, midi->buflen);
+               if (req == NULL)
+                       return -ENOMEM;
+
+               req->complete = f_midi_complete;
+               err = usb_ep_queue(midi->out_ep, req, GFP_ATOMIC);
+               if (err) {
+                       ERROR(midi, "%s queue req: %d\n",
+                                   midi->out_ep->name, err);
+               }
+       }
+
+       return 0;
+}
+
+static void f_midi_disable(struct usb_function *f)
+{
+       struct f_midi *midi = func_to_midi(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+
+       DBG(cdev, "disable\n");
+
+       /*
+        * just disable endpoints, forcing completion of pending i/o.
+        * all our completion handlers free their requests in this case.
+        */
+       usb_ep_disable(midi->in_ep);
+       usb_ep_disable(midi->out_ep);
+}
+
+static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct usb_composite_dev *cdev = f->config->cdev;
+       struct f_midi *midi = func_to_midi(f);
+       struct snd_card *card;
+
+       DBG(cdev, "unbind\n");
+
+       /* just to be sure */
+       f_midi_disable(f);
+
+       card = midi->card;
+       midi->card = NULL;
+       if (card)
+               snd_card_free(card);
+
+       kfree(midi->id);
+       midi->id = NULL;
+
+       usb_free_all_descriptors(f);
+       kfree(midi);
+}
+
+static int f_midi_snd_free(struct snd_device *device)
+{
+       return 0;
+}
+
+static void f_midi_transmit_packet(struct usb_request *req, uint8_t p0,
+                                       uint8_t p1, uint8_t p2, uint8_t p3)
+{
+       unsigned length = req->length;
+       u8 *buf = (u8 *)req->buf + length;
+
+       buf[0] = p0;
+       buf[1] = p1;
+       buf[2] = p2;
+       buf[3] = p3;
+       req->length = length + 4;
+}
+
+/*
+ * Converts MIDI commands to USB MIDI packets.
+ */
+static void f_midi_transmit_byte(struct usb_request *req,
+                                struct gmidi_in_port *port, uint8_t b)
+{
+       uint8_t p0 = port->cable << 4;
+
+       if (b >= 0xf8) {
+               f_midi_transmit_packet(req, p0 | 0x0f, b, 0, 0);
+       } else if (b >= 0xf0) {
+               switch (b) {
+               case 0xf0:
+                       port->data[0] = b;
+                       port->state = STATE_SYSEX_1;
+                       break;
+               case 0xf1:
+               case 0xf3:
+                       port->data[0] = b;
+                       port->state = STATE_1PARAM;
+                       break;
+               case 0xf2:
+                       port->data[0] = b;
+                       port->state = STATE_2PARAM_1;
+                       break;
+               case 0xf4:
+               case 0xf5:
+                       port->state = STATE_UNKNOWN;
+                       break;
+               case 0xf6:
+                       f_midi_transmit_packet(req, p0 | 0x05, 0xf6, 0, 0);
+                       port->state = STATE_UNKNOWN;
+                       break;
+               case 0xf7:
+                       switch (port->state) {
+                       case STATE_SYSEX_0:
+                               f_midi_transmit_packet(req,
+                                       p0 | 0x05, 0xf7, 0, 0);
+                               break;
+                       case STATE_SYSEX_1:
+                               f_midi_transmit_packet(req,
+                                       p0 | 0x06, port->data[0], 0xf7, 0);
+                               break;
+                       case STATE_SYSEX_2:
+                               f_midi_transmit_packet(req,
+                                       p0 | 0x07, port->data[0],
+                                       port->data[1], 0xf7);
+                               break;
+                       }
+                       port->state = STATE_UNKNOWN;
+                       break;
+               }
+       } else if (b >= 0x80) {
+               port->data[0] = b;
+               if (b >= 0xc0 && b <= 0xdf)
+                       port->state = STATE_1PARAM;
+               else
+                       port->state = STATE_2PARAM_1;
+       } else { /* b < 0x80 */
+               switch (port->state) {
+               case STATE_1PARAM:
+                       if (port->data[0] < 0xf0) {
+                               p0 |= port->data[0] >> 4;
+                       } else {
+                               p0 |= 0x02;
+                               port->state = STATE_UNKNOWN;
+                       }
+                       f_midi_transmit_packet(req, p0, port->data[0], b, 0);
+                       break;
+               case STATE_2PARAM_1:
+                       port->data[1] = b;
+                       port->state = STATE_2PARAM_2;
+                       break;
+               case STATE_2PARAM_2:
+                       if (port->data[0] < 0xf0) {
+                               p0 |= port->data[0] >> 4;
+                               port->state = STATE_2PARAM_1;
+                       } else {
+                               p0 |= 0x03;
+                               port->state = STATE_UNKNOWN;
+                       }
+                       f_midi_transmit_packet(req,
+                               p0, port->data[0], port->data[1], b);
+                       break;
+               case STATE_SYSEX_0:
+                       port->data[0] = b;
+                       port->state = STATE_SYSEX_1;
+                       break;
+               case STATE_SYSEX_1:
+                       port->data[1] = b;
+                       port->state = STATE_SYSEX_2;
+                       break;
+               case STATE_SYSEX_2:
+                       f_midi_transmit_packet(req,
+                               p0 | 0x04, port->data[0], port->data[1], b);
+                       port->state = STATE_SYSEX_0;
+                       break;
+               }
+       }
+}
+
+static void f_midi_transmit(struct f_midi *midi, struct usb_request *req)
+{
+       struct usb_ep *ep = midi->in_ep;
+       int i;
+
+       if (!ep)
+               return;
+
+       if (!req)
+               req = midi_alloc_ep_req(ep, midi->buflen);
+
+       if (!req) {
+               ERROR(midi, "gmidi_transmit: alloc_ep_request failed\n");
+               return;
+       }
+       req->length = 0;
+       req->complete = f_midi_complete;
+
+       for (i = 0; i < MAX_PORTS; i++) {
+               struct gmidi_in_port *port = midi->in_port[i];
+               struct snd_rawmidi_substream *substream = midi->in_substream[i];
+
+               if (!port || !port->active || !substream)
+                       continue;
+
+               while (req->length + 3 < midi->buflen) {
+                       uint8_t b;
+                       if (snd_rawmidi_transmit(substream, &b, 1) != 1) {
+                               port->active = 0;
+                               break;
+                       }
+                       f_midi_transmit_byte(req, port, b);
+               }
+       }
+
+       if (req->length > 0)
+               usb_ep_queue(ep, req, GFP_ATOMIC);
+       else
+               free_ep_req(ep, req);
+}
+
+static void f_midi_in_tasklet(unsigned long data)
+{
+       struct f_midi *midi = (struct f_midi *) data;
+       f_midi_transmit(midi, NULL);
+}
+
+static int f_midi_in_open(struct snd_rawmidi_substream *substream)
+{
+       struct f_midi *midi = substream->rmidi->private_data;
+
+       if (!midi->in_port[substream->number])
+               return -EINVAL;
+
+       VDBG(midi, "%s()\n", __func__);
+       midi->in_substream[substream->number] = substream;
+       midi->in_port[substream->number]->state = STATE_UNKNOWN;
+       return 0;
+}
+
+static int f_midi_in_close(struct snd_rawmidi_substream *substream)
+{
+       struct f_midi *midi = substream->rmidi->private_data;
+
+       VDBG(midi, "%s()\n", __func__);
+       return 0;
+}
+
+static void f_midi_in_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+       struct f_midi *midi = substream->rmidi->private_data;
+
+       if (!midi->in_port[substream->number])
+               return;
+
+       VDBG(midi, "%s() %d\n", __func__, up);
+       midi->in_port[substream->number]->active = up;
+       if (up)
+               tasklet_hi_schedule(&midi->tasklet);
+}
+
+static int f_midi_out_open(struct snd_rawmidi_substream *substream)
+{
+       struct f_midi *midi = substream->rmidi->private_data;
+
+       if (substream->number >= MAX_PORTS)
+               return -EINVAL;
+
+       VDBG(midi, "%s()\n", __func__);
+       midi->out_substream[substream->number] = substream;
+       return 0;
+}
+
+static int f_midi_out_close(struct snd_rawmidi_substream *substream)
+{
+       struct f_midi *midi = substream->rmidi->private_data;
+
+       VDBG(midi, "%s()\n", __func__);
+       return 0;
+}
+
+static void f_midi_out_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+       struct f_midi *midi = substream->rmidi->private_data;
+
+       VDBG(midi, "%s()\n", __func__);
+
+       if (up)
+               set_bit(substream->number, &midi->out_triggered);
+       else
+               clear_bit(substream->number, &midi->out_triggered);
+}
+
+static struct snd_rawmidi_ops gmidi_in_ops = {
+       .open = f_midi_in_open,
+       .close = f_midi_in_close,
+       .trigger = f_midi_in_trigger,
+};
+
+static struct snd_rawmidi_ops gmidi_out_ops = {
+       .open = f_midi_out_open,
+       .close = f_midi_out_close,
+       .trigger = f_midi_out_trigger
+};
+
+/* register as a sound "card" */
+static int f_midi_register_card(struct f_midi *midi)
+{
+       struct snd_card *card;
+       struct snd_rawmidi *rmidi;
+       int err;
+       static struct snd_device_ops ops = {
+               .dev_free = f_midi_snd_free,
+       };
+
+       err = snd_card_new(&midi->gadget->dev, midi->index, midi->id,
+                          THIS_MODULE, 0, &card);
+       if (err < 0) {
+               ERROR(midi, "snd_card_new() failed\n");
+               goto fail;
+       }
+       midi->card = card;
+
+       err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, midi, &ops);
+       if (err < 0) {
+               ERROR(midi, "snd_device_new() failed: error %d\n", err);
+               goto fail;
+       }
+
+       strcpy(card->driver, f_midi_longname);
+       strcpy(card->longname, f_midi_longname);
+       strcpy(card->shortname, f_midi_shortname);
+
+       /* Set up rawmidi */
+       snd_component_add(card, "MIDI");
+       err = snd_rawmidi_new(card, card->longname, 0,
+                             midi->out_ports, midi->in_ports, &rmidi);
+       if (err < 0) {
+               ERROR(midi, "snd_rawmidi_new() failed: error %d\n", err);
+               goto fail;
+       }
+       midi->rmidi = rmidi;
+       strcpy(rmidi->name, card->shortname);
+       rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
+                           SNDRV_RAWMIDI_INFO_INPUT |
+                           SNDRV_RAWMIDI_INFO_DUPLEX;
+       rmidi->private_data = midi;
+
+       /*
+        * Yes, rawmidi OUTPUT = USB IN, and rawmidi INPUT = USB OUT.
+        * It's an upside-down world being a gadget.
+        */
+       snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &gmidi_in_ops);
+       snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &gmidi_out_ops);
+
+       /* register it - we're ready to go */
+       err = snd_card_register(card);
+       if (err < 0) {
+               ERROR(midi, "snd_card_register() failed\n");
+               goto fail;
+       }
+
+       VDBG(midi, "%s() finished ok\n", __func__);
+       return 0;
+
+fail:
+       if (midi->card) {
+               snd_card_free(midi->card);
+               midi->card = NULL;
+       }
+       return err;
+}
+
+/* MIDI function driver setup/binding */
+
+static int __init
+f_midi_bind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct usb_descriptor_header **midi_function;
+       struct usb_midi_in_jack_descriptor jack_in_ext_desc[MAX_PORTS];
+       struct usb_midi_in_jack_descriptor jack_in_emb_desc[MAX_PORTS];
+       struct usb_midi_out_jack_descriptor_1 jack_out_ext_desc[MAX_PORTS];
+       struct usb_midi_out_jack_descriptor_1 jack_out_emb_desc[MAX_PORTS];
+       struct usb_composite_dev *cdev = c->cdev;
+       struct f_midi *midi = func_to_midi(f);
+       int status, n, jack = 1, i = 0;
+
+       /* maybe allocate device-global string ID */
+       if (midi_string_defs[0].id == 0) {
+               status = usb_string_id(c->cdev);
+               if (status < 0)
+                       goto fail;
+               midi_string_defs[0].id = status;
+       }
+
+       /* We have two interfaces, AudioControl and MIDIStreaming */
+       status = usb_interface_id(c, f);
+       if (status < 0)
+               goto fail;
+       ac_interface_desc.bInterfaceNumber = status;
+
+       status = usb_interface_id(c, f);
+       if (status < 0)
+               goto fail;
+       ms_interface_desc.bInterfaceNumber = status;
+       ac_header_desc.baInterfaceNr[0] = status;
+
+       status = -ENODEV;
+
+       /* allocate instance-specific endpoints */
+       midi->in_ep = usb_ep_autoconfig(cdev->gadget, &bulk_in_desc);
+       if (!midi->in_ep)
+               goto fail;
+       midi->in_ep->driver_data = cdev;        /* claim */
+
+       midi->out_ep = usb_ep_autoconfig(cdev->gadget, &bulk_out_desc);
+       if (!midi->out_ep)
+               goto fail;
+       midi->out_ep->driver_data = cdev;       /* claim */
+
+       /* allocate temporary function list */
+       midi_function = kcalloc((MAX_PORTS * 4) + 9, sizeof(*midi_function),
+                               GFP_KERNEL);
+       if (!midi_function) {
+               status = -ENOMEM;
+               goto fail;
+       }
+
+       /*
+        * construct the function's descriptor set. As the number of
+        * input and output MIDI ports is configurable, we have to do
+        * it that way.
+        */
+
+       /* add the headers - these are always the same */
+       midi_function[i++] = (struct usb_descriptor_header *) &ac_interface_desc;
+       midi_function[i++] = (struct usb_descriptor_header *) &ac_header_desc;
+       midi_function[i++] = (struct usb_descriptor_header *) &ms_interface_desc;
+
+       /* calculate the header's wTotalLength */
+       n = USB_DT_MS_HEADER_SIZE
+               + (midi->in_ports + midi->out_ports) *
+                       (USB_DT_MIDI_IN_SIZE + USB_DT_MIDI_OUT_SIZE(1));
+       ms_header_desc.wTotalLength = cpu_to_le16(n);
+
+       midi_function[i++] = (struct usb_descriptor_header *) &ms_header_desc;
+
+       /* configure the external IN jacks, each linked to an embedded OUT jack */
+       for (n = 0; n < midi->in_ports; n++) {
+               struct usb_midi_in_jack_descriptor *in_ext = &jack_in_ext_desc[n];
+               struct usb_midi_out_jack_descriptor_1 *out_emb = &jack_out_emb_desc[n];
+
+               in_ext->bLength                 = USB_DT_MIDI_IN_SIZE;
+               in_ext->bDescriptorType         = USB_DT_CS_INTERFACE;
+               in_ext->bDescriptorSubtype      = USB_MS_MIDI_IN_JACK;
+               in_ext->bJackType               = USB_MS_EXTERNAL;
+               in_ext->bJackID                 = jack++;
+               in_ext->iJack                   = 0;
+               midi_function[i++] = (struct usb_descriptor_header *) in_ext;
+
+               out_emb->bLength                = USB_DT_MIDI_OUT_SIZE(1);
+               out_emb->bDescriptorType        = USB_DT_CS_INTERFACE;
+               out_emb->bDescriptorSubtype     = USB_MS_MIDI_OUT_JACK;
+               out_emb->bJackType              = USB_MS_EMBEDDED;
+               out_emb->bJackID                = jack++;
+               out_emb->bNrInputPins           = 1;
+               out_emb->pins[0].baSourcePin    = 1;
+               out_emb->pins[0].baSourceID     = in_ext->bJackID;
+               out_emb->iJack                  = 0;
+               midi_function[i++] = (struct usb_descriptor_header *) out_emb;
+
+               /* link it to the endpoint */
+               ms_in_desc.baAssocJackID[n] = out_emb->bJackID;
+       }
+
+       /* configure the external OUT jacks, each linked to an embedded IN jack */
+       for (n = 0; n < midi->out_ports; n++) {
+               struct usb_midi_in_jack_descriptor *in_emb = &jack_in_emb_desc[n];
+               struct usb_midi_out_jack_descriptor_1 *out_ext = &jack_out_ext_desc[n];
+
+               in_emb->bLength                 = USB_DT_MIDI_IN_SIZE;
+               in_emb->bDescriptorType         = USB_DT_CS_INTERFACE;
+               in_emb->bDescriptorSubtype      = USB_MS_MIDI_IN_JACK;
+               in_emb->bJackType               = USB_MS_EMBEDDED;
+               in_emb->bJackID                 = jack++;
+               in_emb->iJack                   = 0;
+               midi_function[i++] = (struct usb_descriptor_header *) in_emb;
+
+               out_ext->bLength =              USB_DT_MIDI_OUT_SIZE(1);
+               out_ext->bDescriptorType =      USB_DT_CS_INTERFACE;
+               out_ext->bDescriptorSubtype =   USB_MS_MIDI_OUT_JACK;
+               out_ext->bJackType =            USB_MS_EXTERNAL;
+               out_ext->bJackID =              jack++;
+               out_ext->bNrInputPins =         1;
+               out_ext->iJack =                0;
+               out_ext->pins[0].baSourceID =   in_emb->bJackID;
+               out_ext->pins[0].baSourcePin =  1;
+               midi_function[i++] = (struct usb_descriptor_header *) out_ext;
+
+               /* link it to the endpoint */
+               ms_out_desc.baAssocJackID[n] = in_emb->bJackID;
+       }
+
+       /* configure the endpoint descriptors ... */
+       ms_out_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->in_ports);
+       ms_out_desc.bNumEmbMIDIJack = midi->in_ports;
+
+       ms_in_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->out_ports);
+       ms_in_desc.bNumEmbMIDIJack = midi->out_ports;
+
+       /* ... and add them to the list */
+       midi_function[i++] = (struct usb_descriptor_header *) &bulk_out_desc;
+       midi_function[i++] = (struct usb_descriptor_header *) &ms_out_desc;
+       midi_function[i++] = (struct usb_descriptor_header *) &bulk_in_desc;
+       midi_function[i++] = (struct usb_descriptor_header *) &ms_in_desc;
+       midi_function[i++] = NULL;
+
+       /*
+        * support all relevant hardware speeds... we expect that when
+        * hardware is dual speed, all bulk-capable endpoints work at
+        * both speeds
+        */
+       /* copy descriptors, and track endpoint copies */
+       f->fs_descriptors = usb_copy_descriptors(midi_function);
+       if (!f->fs_descriptors)
+               goto fail_f_midi;
+
+       if (gadget_is_dualspeed(c->cdev->gadget)) {
+               bulk_in_desc.wMaxPacketSize = cpu_to_le16(512);
+               bulk_out_desc.wMaxPacketSize = cpu_to_le16(512);
+               f->hs_descriptors = usb_copy_descriptors(midi_function);
+               if (!f->hs_descriptors)
+                       goto fail_f_midi;
+       }
+
+       kfree(midi_function);
+
+       return 0;
+
+fail_f_midi:
+       kfree(midi_function);
+       usb_free_descriptors(f->hs_descriptors);
+fail:
+       /* we might as well release our claims on endpoints */
+       if (midi->out_ep)
+               midi->out_ep->driver_data = NULL;
+       if (midi->in_ep)
+               midi->in_ep->driver_data = NULL;
+
+       ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
+
+       return status;
+}
+
+/**
+ * f_midi_bind_config - add USB MIDI function to a configuration
+ * @c: the configuration to supcard the USB audio function
+ * @index: the soundcard index to use for the ALSA device creation
+ * @id: the soundcard id to use for the ALSA device creation
+ * @buflen: the buffer length to use
+ * @qlen the number of read requests to pre-allocate
+ * Context: single threaded during gadget setup
+ *
+ * Returns zero on success, else negative errno.
+ */
+int __init f_midi_bind_config(struct usb_configuration *c,
+                             int index, char *id,
+                             unsigned int in_ports,
+                             unsigned int out_ports,
+                             unsigned int buflen,
+                             unsigned int qlen)
+{
+       struct f_midi *midi;
+       int status, i;
+
+       /* sanity check */
+       if (in_ports > MAX_PORTS || out_ports > MAX_PORTS)
+               return -EINVAL;
+
+       /* allocate and initialize one new instance */
+       midi = kzalloc(sizeof *midi, GFP_KERNEL);
+       if (!midi) {
+               status = -ENOMEM;
+               goto fail;
+       }
+
+       for (i = 0; i < in_ports; i++) {
+               struct gmidi_in_port *port = kzalloc(sizeof(*port), GFP_KERNEL);
+               if (!port) {
+                       status = -ENOMEM;
+                       goto setup_fail;
+               }
+
+               port->midi = midi;
+               port->active = 0;
+               port->cable = i;
+               midi->in_port[i] = port;
+       }
+
+       midi->gadget = c->cdev->gadget;
+       tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi);
+
+       /* set up ALSA midi devices */
+       midi->in_ports = in_ports;
+       midi->out_ports = out_ports;
+       status = f_midi_register_card(midi);
+       if (status < 0)
+               goto setup_fail;
+
+       midi->func.name        = "gmidi function";
+       midi->func.strings     = midi_strings;
+       midi->func.bind        = f_midi_bind;
+       midi->func.unbind      = f_midi_unbind;
+       midi->func.set_alt     = f_midi_set_alt;
+       midi->func.disable     = f_midi_disable;
+
+       midi->id = kstrdup(id, GFP_KERNEL);
+       midi->index = index;
+       midi->buflen = buflen;
+       midi->qlen = qlen;
+
+       status = usb_add_function(c, &midi->func);
+       if (status)
+               goto setup_fail;
+
+       return 0;
+
+setup_fail:
+       for (--i; i >= 0; i--)
+               kfree(midi->in_port[i]);
+       kfree(midi);
+fail:
+       return status;
+}
+
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
new file mode 100644 (file)
index 0000000..bcdc882
--- /dev/null
@@ -0,0 +1,1622 @@
+/*
+ * f_ncm.c -- USB CDC Network (NCM) link function driver
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Contact: Yauheni Kaliuta <yauheni.kaliuta@nokia.com>
+ *
+ * The driver borrows from f_ecm.c which is:
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/etherdevice.h>
+#include <linux/crc32.h>
+
+#include <linux/usb/cdc.h>
+
+#include "u_ether.h"
+#include "u_ether_configfs.h"
+#include "u_ncm.h"
+
+/*
+ * This function is a "CDC Network Control Model" (CDC NCM) Ethernet link.
+ * NCM is intended to be used with high-speed network attachments.
+ *
+ * Note that NCM requires the use of "alternate settings" for its data
+ * interface.  This means that the set_alt() method has real work to do,
+ * and also means that a get_alt() method is required.
+ */
+
+/* to trigger crc/non-crc ndp signature */
+
+#define NCM_NDP_HDR_CRC_MASK   0x01000000
+#define NCM_NDP_HDR_CRC                0x01000000
+#define NCM_NDP_HDR_NOCRC      0x00000000
+
+enum ncm_notify_state {
+       NCM_NOTIFY_NONE,                /* don't notify */
+       NCM_NOTIFY_CONNECT,             /* issue CONNECT next */
+       NCM_NOTIFY_SPEED,               /* issue SPEED_CHANGE next */
+};
+
+struct f_ncm {
+       struct gether                   port;
+       u8                              ctrl_id, data_id;
+
+       char                            ethaddr[14];
+
+       struct usb_ep                   *notify;
+       struct usb_request              *notify_req;
+       u8                              notify_state;
+       bool                            is_open;
+
+       const struct ndp_parser_opts    *parser_opts;
+       bool                            is_crc;
+       u32                             ndp_sign;
+
+       /*
+        * for notification, it is accessed from both
+        * callback and ethernet open/close
+        */
+       spinlock_t                      lock;
+
+       struct net_device               *netdev;
+
+       /* For multi-frame NDP TX */
+       struct sk_buff                  *skb_tx_data;
+       struct sk_buff                  *skb_tx_ndp;
+       u16                             ndp_dgram_count;
+       bool                            timer_force_tx;
+       struct tasklet_struct           tx_tasklet;
+       struct hrtimer                  task_timer;
+
+       bool                            timer_stopping;
+};
+
+static inline struct f_ncm *func_to_ncm(struct usb_function *f)
+{
+       return container_of(f, struct f_ncm, port.func);
+}
+
+/* peak (theoretical) bulk transfer rate in bits-per-second */
+static inline unsigned ncm_bitrate(struct usb_gadget *g)
+{
+       if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+               return 13 * 512 * 8 * 1000 * 8;
+       else
+               return 19 *  64 * 1 * 1000 * 8;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * We cannot group frames so use just the minimal size which ok to put
+ * one max-size ethernet frame.
+ * If the host can group frames, allow it to do that, 16K is selected,
+ * because it's used by default by the current linux host driver
+ */
+#define NTB_DEFAULT_IN_SIZE    16384
+#define NTB_OUT_SIZE           16384
+
+/* Allocation for storing the NDP, 32 should suffice for a
+ * 16k packet. This allows a maximum of 32 * 507 Byte packets to
+ * be transmitted in a single 16kB skb, though when sending full size
+ * packets this limit will be plenty.
+ * Smaller packets are not likely to be trying to maximize the
+ * throughput and will be mstly sending smaller infrequent frames.
+ */
+#define TX_MAX_NUM_DPE         32
+
+/* Delay for the transmit to wait before sending an unfilled NTB frame. */
+#define TX_TIMEOUT_NSECS       300000
+
+#define FORMATS_SUPPORTED      (USB_CDC_NCM_NTB16_SUPPORTED |  \
+                                USB_CDC_NCM_NTB32_SUPPORTED)
+
+static struct usb_cdc_ncm_ntb_parameters ntb_parameters = {
+       .wLength = cpu_to_le16(sizeof(ntb_parameters)),
+       .bmNtbFormatsSupported = cpu_to_le16(FORMATS_SUPPORTED),
+       .dwNtbInMaxSize = cpu_to_le32(NTB_DEFAULT_IN_SIZE),
+       .wNdpInDivisor = cpu_to_le16(4),
+       .wNdpInPayloadRemainder = cpu_to_le16(0),
+       .wNdpInAlignment = cpu_to_le16(4),
+
+       .dwNtbOutMaxSize = cpu_to_le32(NTB_OUT_SIZE),
+       .wNdpOutDivisor = cpu_to_le16(4),
+       .wNdpOutPayloadRemainder = cpu_to_le16(0),
+       .wNdpOutAlignment = cpu_to_le16(4),
+};
+
+/*
+ * Use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one
+ * packet, to simplify cancellation; and a big transfer interval, to
+ * waste less bandwidth.
+ */
+
+#define NCM_STATUS_INTERVAL_MS         32
+#define NCM_STATUS_BYTECOUNT           16      /* 8 byte header + data */
+
+static struct usb_interface_assoc_descriptor ncm_iad_desc = {
+       .bLength =              sizeof ncm_iad_desc,
+       .bDescriptorType =      USB_DT_INTERFACE_ASSOCIATION,
+
+       /* .bFirstInterface =   DYNAMIC, */
+       .bInterfaceCount =      2,      /* control + data */
+       .bFunctionClass =       USB_CLASS_COMM,
+       .bFunctionSubClass =    USB_CDC_SUBCLASS_NCM,
+       .bFunctionProtocol =    USB_CDC_PROTO_NONE,
+       /* .iFunction =         DYNAMIC */
+};
+
+/* interface descriptor: */
+
+static struct usb_interface_descriptor ncm_control_intf = {
+       .bLength =              sizeof ncm_control_intf,
+       .bDescriptorType =      USB_DT_INTERFACE,
+
+       /* .bInterfaceNumber = DYNAMIC */
+       .bNumEndpoints =        1,
+       .bInterfaceClass =      USB_CLASS_COMM,
+       .bInterfaceSubClass =   USB_CDC_SUBCLASS_NCM,
+       .bInterfaceProtocol =   USB_CDC_PROTO_NONE,
+       /* .iInterface = DYNAMIC */
+};
+
+static struct usb_cdc_header_desc ncm_header_desc = {
+       .bLength =              sizeof ncm_header_desc,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubType =   USB_CDC_HEADER_TYPE,
+
+       .bcdCDC =               cpu_to_le16(0x0110),
+};
+
+static struct usb_cdc_union_desc ncm_union_desc = {
+       .bLength =              sizeof(ncm_union_desc),
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubType =   USB_CDC_UNION_TYPE,
+       /* .bMasterInterface0 = DYNAMIC */
+       /* .bSlaveInterface0 =  DYNAMIC */
+};
+
+static struct usb_cdc_ether_desc ecm_desc = {
+       .bLength =              sizeof ecm_desc,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubType =   USB_CDC_ETHERNET_TYPE,
+
+       /* this descriptor actually adds value, surprise! */
+       /* .iMACAddress = DYNAMIC */
+       .bmEthernetStatistics = cpu_to_le32(0), /* no statistics */
+       .wMaxSegmentSize =      cpu_to_le16(ETH_FRAME_LEN),
+       .wNumberMCFilters =     cpu_to_le16(0),
+       .bNumberPowerFilters =  0,
+};
+
+#define NCAPS  (USB_CDC_NCM_NCAP_ETH_FILTER | USB_CDC_NCM_NCAP_CRC_MODE)
+
+static struct usb_cdc_ncm_desc ncm_desc = {
+       .bLength =              sizeof ncm_desc,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubType =   USB_CDC_NCM_TYPE,
+
+       .bcdNcmVersion =        cpu_to_le16(0x0100),
+       /* can process SetEthernetPacketFilter */
+       .bmNetworkCapabilities = NCAPS,
+};
+
+/* the default data interface has no endpoints ... */
+
+static struct usb_interface_descriptor ncm_data_nop_intf = {
+       .bLength =              sizeof ncm_data_nop_intf,
+       .bDescriptorType =      USB_DT_INTERFACE,
+
+       .bInterfaceNumber =     1,
+       .bAlternateSetting =    0,
+       .bNumEndpoints =        0,
+       .bInterfaceClass =      USB_CLASS_CDC_DATA,
+       .bInterfaceSubClass =   0,
+       .bInterfaceProtocol =   USB_CDC_NCM_PROTO_NTB,
+       /* .iInterface = DYNAMIC */
+};
+
+/* ... but the "real" data interface has two bulk endpoints */
+
+static struct usb_interface_descriptor ncm_data_intf = {
+       .bLength =              sizeof ncm_data_intf,
+       .bDescriptorType =      USB_DT_INTERFACE,
+
+       .bInterfaceNumber =     1,
+       .bAlternateSetting =    1,
+       .bNumEndpoints =        2,
+       .bInterfaceClass =      USB_CLASS_CDC_DATA,
+       .bInterfaceSubClass =   0,
+       .bInterfaceProtocol =   USB_CDC_NCM_PROTO_NTB,
+       /* .iInterface = DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor fs_ncm_notify_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_INT,
+       .wMaxPacketSize =       cpu_to_le16(NCM_STATUS_BYTECOUNT),
+       .bInterval =            NCM_STATUS_INTERVAL_MS,
+};
+
+static struct usb_endpoint_descriptor fs_ncm_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor fs_ncm_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *ncm_fs_function[] = {
+       (struct usb_descriptor_header *) &ncm_iad_desc,
+       /* CDC NCM control descriptors */
+       (struct usb_descriptor_header *) &ncm_control_intf,
+       (struct usb_descriptor_header *) &ncm_header_desc,
+       (struct usb_descriptor_header *) &ncm_union_desc,
+       (struct usb_descriptor_header *) &ecm_desc,
+       (struct usb_descriptor_header *) &ncm_desc,
+       (struct usb_descriptor_header *) &fs_ncm_notify_desc,
+       /* data interface, altsettings 0 and 1 */
+       (struct usb_descriptor_header *) &ncm_data_nop_intf,
+       (struct usb_descriptor_header *) &ncm_data_intf,
+       (struct usb_descriptor_header *) &fs_ncm_in_desc,
+       (struct usb_descriptor_header *) &fs_ncm_out_desc,
+       NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor hs_ncm_notify_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_INT,
+       .wMaxPacketSize =       cpu_to_le16(NCM_STATUS_BYTECOUNT),
+       .bInterval =            USB_MS_TO_HS_INTERVAL(NCM_STATUS_INTERVAL_MS),
+};
+static struct usb_endpoint_descriptor hs_ncm_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor hs_ncm_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *ncm_hs_function[] = {
+       (struct usb_descriptor_header *) &ncm_iad_desc,
+       /* CDC NCM control descriptors */
+       (struct usb_descriptor_header *) &ncm_control_intf,
+       (struct usb_descriptor_header *) &ncm_header_desc,
+       (struct usb_descriptor_header *) &ncm_union_desc,
+       (struct usb_descriptor_header *) &ecm_desc,
+       (struct usb_descriptor_header *) &ncm_desc,
+       (struct usb_descriptor_header *) &hs_ncm_notify_desc,
+       /* data interface, altsettings 0 and 1 */
+       (struct usb_descriptor_header *) &ncm_data_nop_intf,
+       (struct usb_descriptor_header *) &ncm_data_intf,
+       (struct usb_descriptor_header *) &hs_ncm_in_desc,
+       (struct usb_descriptor_header *) &hs_ncm_out_desc,
+       NULL,
+};
+
+/* string descriptors: */
+
+#define STRING_CTRL_IDX        0
+#define STRING_MAC_IDX 1
+#define STRING_DATA_IDX        2
+#define STRING_IAD_IDX 3
+
+static struct usb_string ncm_string_defs[] = {
+       [STRING_CTRL_IDX].s = "CDC Network Control Model (NCM)",
+       [STRING_MAC_IDX].s = "",
+       [STRING_DATA_IDX].s = "CDC Network Data",
+       [STRING_IAD_IDX].s = "CDC NCM",
+       {  } /* end of list */
+};
+
+static struct usb_gadget_strings ncm_string_table = {
+       .language =             0x0409, /* en-us */
+       .strings =              ncm_string_defs,
+};
+
+static struct usb_gadget_strings *ncm_strings[] = {
+       &ncm_string_table,
+       NULL,
+};
+
+/*
+ * Here are options for NCM Datagram Pointer table (NDP) parser.
+ * There are 2 different formats: NDP16 and NDP32 in the spec (ch. 3),
+ * in NDP16 offsets and sizes fields are 1 16bit word wide,
+ * in NDP32 -- 2 16bit words wide. Also signatures are different.
+ * To make the parser code the same, put the differences in the structure,
+ * and switch pointers to the structures when the format is changed.
+ */
+
+struct ndp_parser_opts {
+       u32             nth_sign;
+       u32             ndp_sign;
+       unsigned        nth_size;
+       unsigned        ndp_size;
+       unsigned        dpe_size;
+       unsigned        ndplen_align;
+       /* sizes in u16 units */
+       unsigned        dgram_item_len; /* index or length */
+       unsigned        block_length;
+       unsigned        ndp_index;
+       unsigned        reserved1;
+       unsigned        reserved2;
+       unsigned        next_ndp_index;
+};
+
+#define INIT_NDP16_OPTS {                                      \
+               .nth_sign = USB_CDC_NCM_NTH16_SIGN,             \
+               .ndp_sign = USB_CDC_NCM_NDP16_NOCRC_SIGN,       \
+               .nth_size = sizeof(struct usb_cdc_ncm_nth16),   \
+               .ndp_size = sizeof(struct usb_cdc_ncm_ndp16),   \
+               .dpe_size = sizeof(struct usb_cdc_ncm_dpe16),   \
+               .ndplen_align = 4,                              \
+               .dgram_item_len = 1,                            \
+               .block_length = 1,                              \
+               .ndp_index = 1,                                 \
+               .reserved1 = 0,                                 \
+               .reserved2 = 0,                                 \
+               .next_ndp_index = 1,                            \
+       }
+
+
+#define INIT_NDP32_OPTS {                                      \
+               .nth_sign = USB_CDC_NCM_NTH32_SIGN,             \
+               .ndp_sign = USB_CDC_NCM_NDP32_NOCRC_SIGN,       \
+               .nth_size = sizeof(struct usb_cdc_ncm_nth32),   \
+               .ndp_size = sizeof(struct usb_cdc_ncm_ndp32),   \
+               .dpe_size = sizeof(struct usb_cdc_ncm_dpe32),   \
+               .ndplen_align = 8,                              \
+               .dgram_item_len = 2,                            \
+               .block_length = 2,                              \
+               .ndp_index = 2,                                 \
+               .reserved1 = 1,                                 \
+               .reserved2 = 2,                                 \
+               .next_ndp_index = 2,                            \
+       }
+
+static const struct ndp_parser_opts ndp16_opts = INIT_NDP16_OPTS;
+static const struct ndp_parser_opts ndp32_opts = INIT_NDP32_OPTS;
+
+static inline void put_ncm(__le16 **p, unsigned size, unsigned val)
+{
+       switch (size) {
+       case 1:
+               put_unaligned_le16((u16)val, *p);
+               break;
+       case 2:
+               put_unaligned_le32((u32)val, *p);
+
+               break;
+       default:
+               BUG();
+       }
+
+       *p += size;
+}
+
+static inline unsigned get_ncm(__le16 **p, unsigned size)
+{
+       unsigned tmp;
+
+       switch (size) {
+       case 1:
+               tmp = get_unaligned_le16(*p);
+               break;
+       case 2:
+               tmp = get_unaligned_le32(*p);
+               break;
+       default:
+               BUG();
+       }
+
+       *p += size;
+       return tmp;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline void ncm_reset_values(struct f_ncm *ncm)
+{
+       ncm->parser_opts = &ndp16_opts;
+       ncm->is_crc = false;
+       ncm->port.cdc_filter = DEFAULT_FILTER;
+
+       /* doesn't make sense for ncm, fixed size used */
+       ncm->port.header_len = 0;
+
+       ncm->port.fixed_out_len = le32_to_cpu(ntb_parameters.dwNtbOutMaxSize);
+       ncm->port.fixed_in_len = NTB_DEFAULT_IN_SIZE;
+}
+
+/*
+ * Context: ncm->lock held
+ */
+static void ncm_do_notify(struct f_ncm *ncm)
+{
+       struct usb_request              *req = ncm->notify_req;
+       struct usb_cdc_notification     *event;
+       struct usb_composite_dev        *cdev = ncm->port.func.config->cdev;
+       __le32                          *data;
+       int                             status;
+
+       /* notification already in flight? */
+       if (!req)
+               return;
+
+       event = req->buf;
+       switch (ncm->notify_state) {
+       case NCM_NOTIFY_NONE:
+               return;
+
+       case NCM_NOTIFY_CONNECT:
+               event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
+               if (ncm->is_open)
+                       event->wValue = cpu_to_le16(1);
+               else
+                       event->wValue = cpu_to_le16(0);
+               event->wLength = 0;
+               req->length = sizeof *event;
+
+               DBG(cdev, "notify connect %s\n",
+                               ncm->is_open ? "true" : "false");
+               ncm->notify_state = NCM_NOTIFY_NONE;
+               break;
+
+       case NCM_NOTIFY_SPEED:
+               event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE;
+               event->wValue = cpu_to_le16(0);
+               event->wLength = cpu_to_le16(8);
+               req->length = NCM_STATUS_BYTECOUNT;
+
+               /* SPEED_CHANGE data is up/down speeds in bits/sec */
+               data = req->buf + sizeof *event;
+               data[0] = cpu_to_le32(ncm_bitrate(cdev->gadget));
+               data[1] = data[0];
+
+               DBG(cdev, "notify speed %d\n", ncm_bitrate(cdev->gadget));
+               ncm->notify_state = NCM_NOTIFY_CONNECT;
+               break;
+       }
+       event->bmRequestType = 0xA1;
+       event->wIndex = cpu_to_le16(ncm->ctrl_id);
+
+       ncm->notify_req = NULL;
+       /*
+        * In double buffering if there is a space in FIFO,
+        * completion callback can be called right after the call,
+        * so unlocking
+        */
+       spin_unlock(&ncm->lock);
+       status = usb_ep_queue(ncm->notify, req, GFP_ATOMIC);
+       spin_lock(&ncm->lock);
+       if (status < 0) {
+               ncm->notify_req = req;
+               DBG(cdev, "notify --> %d\n", status);
+       }
+}
+
+/*
+ * Context: ncm->lock held
+ */
+static void ncm_notify(struct f_ncm *ncm)
+{
+       /*
+        * NOTE on most versions of Linux, host side cdc-ethernet
+        * won't listen for notifications until its netdevice opens.
+        * The first notification then sits in the FIFO for a long
+        * time, and the second one is queued.
+        *
+        * If ncm_notify() is called before the second (CONNECT)
+        * notification is sent, then it will reset to send the SPEED
+        * notificaion again (and again, and again), but it's not a problem
+        */
+       ncm->notify_state = NCM_NOTIFY_SPEED;
+       ncm_do_notify(ncm);
+}
+
+static void ncm_notify_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct f_ncm                    *ncm = req->context;
+       struct usb_composite_dev        *cdev = ncm->port.func.config->cdev;
+       struct usb_cdc_notification     *event = req->buf;
+
+       spin_lock(&ncm->lock);
+       switch (req->status) {
+       case 0:
+               VDBG(cdev, "Notification %02x sent\n",
+                    event->bNotificationType);
+               break;
+       case -ECONNRESET:
+       case -ESHUTDOWN:
+               ncm->notify_state = NCM_NOTIFY_NONE;
+               break;
+       default:
+               DBG(cdev, "event %02x --> %d\n",
+                       event->bNotificationType, req->status);
+               break;
+       }
+       ncm->notify_req = req;
+       ncm_do_notify(ncm);
+       spin_unlock(&ncm->lock);
+}
+
+static void ncm_ep0out_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       /* now for SET_NTB_INPUT_SIZE only */
+       unsigned                in_size;
+       struct usb_function     *f = req->context;
+       struct f_ncm            *ncm = func_to_ncm(f);
+       struct usb_composite_dev *cdev = ep->driver_data;
+
+       req->context = NULL;
+       if (req->status || req->actual != req->length) {
+               DBG(cdev, "Bad control-OUT transfer\n");
+               goto invalid;
+       }
+
+       in_size = get_unaligned_le32(req->buf);
+       if (in_size < USB_CDC_NCM_NTB_MIN_IN_SIZE ||
+           in_size > le32_to_cpu(ntb_parameters.dwNtbInMaxSize)) {
+               DBG(cdev, "Got wrong INPUT SIZE (%d) from host\n", in_size);
+               goto invalid;
+       }
+
+       ncm->port.fixed_in_len = in_size;
+       VDBG(cdev, "Set NTB INPUT SIZE %d\n", in_size);
+       return;
+
+invalid:
+       usb_ep_set_halt(ep);
+       return;
+}
+
+static int ncm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+       struct f_ncm            *ncm = func_to_ncm(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+       struct usb_request      *req = cdev->req;
+       int                     value = -EOPNOTSUPP;
+       u16                     w_index = le16_to_cpu(ctrl->wIndex);
+       u16                     w_value = le16_to_cpu(ctrl->wValue);
+       u16                     w_length = le16_to_cpu(ctrl->wLength);
+
+       /*
+        * composite driver infrastructure handles everything except
+        * CDC class messages; interface activation uses set_alt().
+        */
+       switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+       case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+                       | USB_CDC_SET_ETHERNET_PACKET_FILTER:
+               /*
+                * see 6.2.30: no data, wIndex = interface,
+                * wValue = packet filter bitmap
+                */
+               if (w_length != 0 || w_index != ncm->ctrl_id)
+                       goto invalid;
+               DBG(cdev, "packet filter %02x\n", w_value);
+               /*
+                * REVISIT locking of cdc_filter.  This assumes the UDC
+                * driver won't have a concurrent packet TX irq running on
+                * another CPU; or that if it does, this write is atomic...
+                */
+               ncm->port.cdc_filter = w_value;
+               value = 0;
+               break;
+       /*
+        * and optionally:
+        * case USB_CDC_SEND_ENCAPSULATED_COMMAND:
+        * case USB_CDC_GET_ENCAPSULATED_RESPONSE:
+        * case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS:
+        * case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER:
+        * case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER:
+        * case USB_CDC_GET_ETHERNET_STATISTIC:
+        */
+
+       case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+               | USB_CDC_GET_NTB_PARAMETERS:
+
+               if (w_length == 0 || w_value != 0 || w_index != ncm->ctrl_id)
+                       goto invalid;
+               value = w_length > sizeof ntb_parameters ?
+                       sizeof ntb_parameters : w_length;
+               memcpy(req->buf, &ntb_parameters, value);
+               VDBG(cdev, "Host asked NTB parameters\n");
+               break;
+
+       case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+               | USB_CDC_GET_NTB_INPUT_SIZE:
+
+               if (w_length < 4 || w_value != 0 || w_index != ncm->ctrl_id)
+                       goto invalid;
+               put_unaligned_le32(ncm->port.fixed_in_len, req->buf);
+               value = 4;
+               VDBG(cdev, "Host asked INPUT SIZE, sending %d\n",
+                    ncm->port.fixed_in_len);
+               break;
+
+       case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+               | USB_CDC_SET_NTB_INPUT_SIZE:
+       {
+               if (w_length != 4 || w_value != 0 || w_index != ncm->ctrl_id)
+                       goto invalid;
+               req->complete = ncm_ep0out_complete;
+               req->length = w_length;
+               req->context = f;
+
+               value = req->length;
+               break;
+       }
+
+       case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+               | USB_CDC_GET_NTB_FORMAT:
+       {
+               uint16_t format;
+
+               if (w_length < 2 || w_value != 0 || w_index != ncm->ctrl_id)
+                       goto invalid;
+               format = (ncm->parser_opts == &ndp16_opts) ? 0x0000 : 0x0001;
+               put_unaligned_le16(format, req->buf);
+               value = 2;
+               VDBG(cdev, "Host asked NTB FORMAT, sending %d\n", format);
+               break;
+       }
+
+       case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+               | USB_CDC_SET_NTB_FORMAT:
+       {
+               if (w_length != 0 || w_index != ncm->ctrl_id)
+                       goto invalid;
+               switch (w_value) {
+               case 0x0000:
+                       ncm->parser_opts = &ndp16_opts;
+                       DBG(cdev, "NCM16 selected\n");
+                       break;
+               case 0x0001:
+                       ncm->parser_opts = &ndp32_opts;
+                       DBG(cdev, "NCM32 selected\n");
+                       break;
+               default:
+                       goto invalid;
+               }
+               value = 0;
+               break;
+       }
+       case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+               | USB_CDC_GET_CRC_MODE:
+       {
+               uint16_t is_crc;
+
+               if (w_length < 2 || w_value != 0 || w_index != ncm->ctrl_id)
+                       goto invalid;
+               is_crc = ncm->is_crc ? 0x0001 : 0x0000;
+               put_unaligned_le16(is_crc, req->buf);
+               value = 2;
+               VDBG(cdev, "Host asked CRC MODE, sending %d\n", is_crc);
+               break;
+       }
+
+       case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+               | USB_CDC_SET_CRC_MODE:
+       {
+               int ndp_hdr_crc = 0;
+
+               if (w_length != 0 || w_index != ncm->ctrl_id)
+                       goto invalid;
+               switch (w_value) {
+               case 0x0000:
+                       ncm->is_crc = false;
+                       ndp_hdr_crc = NCM_NDP_HDR_NOCRC;
+                       DBG(cdev, "non-CRC mode selected\n");
+                       break;
+               case 0x0001:
+                       ncm->is_crc = true;
+                       ndp_hdr_crc = NCM_NDP_HDR_CRC;
+                       DBG(cdev, "CRC mode selected\n");
+                       break;
+               default:
+                       goto invalid;
+               }
+               ncm->ndp_sign = ncm->parser_opts->ndp_sign | ndp_hdr_crc;
+               value = 0;
+               break;
+       }
+
+       /* and disabled in ncm descriptor: */
+       /* case USB_CDC_GET_NET_ADDRESS: */
+       /* case USB_CDC_SET_NET_ADDRESS: */
+       /* case USB_CDC_GET_MAX_DATAGRAM_SIZE: */
+       /* case USB_CDC_SET_MAX_DATAGRAM_SIZE: */
+
+       default:
+invalid:
+               DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+                       ctrl->bRequestType, ctrl->bRequest,
+                       w_value, w_index, w_length);
+       }
+
+       /* respond with data transfer or status phase? */
+       if (value >= 0) {
+               DBG(cdev, "ncm req%02x.%02x v%04x i%04x l%d\n",
+                       ctrl->bRequestType, ctrl->bRequest,
+                       w_value, w_index, w_length);
+               req->zero = 0;
+               req->length = value;
+               value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+               if (value < 0)
+                       ERROR(cdev, "ncm req %02x.%02x response err %d\n",
+                                       ctrl->bRequestType, ctrl->bRequest,
+                                       value);
+       }
+
+       /* device either stalls (value < 0) or reports success */
+       return value;
+}
+
+
+static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+       struct f_ncm            *ncm = func_to_ncm(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+
+       /* Control interface has only altsetting 0 */
+       if (intf == ncm->ctrl_id) {
+               if (alt != 0)
+                       goto fail;
+
+               if (ncm->notify->driver_data) {
+                       DBG(cdev, "reset ncm control %d\n", intf);
+                       usb_ep_disable(ncm->notify);
+               }
+
+               if (!(ncm->notify->desc)) {
+                       DBG(cdev, "init ncm ctrl %d\n", intf);
+                       if (config_ep_by_speed(cdev->gadget, f, ncm->notify))
+                               goto fail;
+               }
+               usb_ep_enable(ncm->notify);
+               ncm->notify->driver_data = ncm;
+
+       /* Data interface has two altsettings, 0 and 1 */
+       } else if (intf == ncm->data_id) {
+               if (alt > 1)
+                       goto fail;
+
+               if (ncm->port.in_ep->driver_data) {
+                       DBG(cdev, "reset ncm\n");
+                       ncm->timer_stopping = true;
+                       ncm->netdev = NULL;
+                       gether_disconnect(&ncm->port);
+                       ncm_reset_values(ncm);
+               }
+
+               /*
+                * CDC Network only sends data in non-default altsettings.
+                * Changing altsettings resets filters, statistics, etc.
+                */
+               if (alt == 1) {
+                       struct net_device       *net;
+
+                       if (!ncm->port.in_ep->desc ||
+                           !ncm->port.out_ep->desc) {
+                               DBG(cdev, "init ncm\n");
+                               if (config_ep_by_speed(cdev->gadget, f,
+                                                      ncm->port.in_ep) ||
+                                   config_ep_by_speed(cdev->gadget, f,
+                                                      ncm->port.out_ep)) {
+                                       ncm->port.in_ep->desc = NULL;
+                                       ncm->port.out_ep->desc = NULL;
+                                       goto fail;
+                               }
+                       }
+
+                       /* TODO */
+                       /* Enable zlps by default for NCM conformance;
+                        * override for musb_hdrc (avoids txdma ovhead)
+                        */
+                       ncm->port.is_zlp_ok = !(
+                               gadget_is_musbhdrc(cdev->gadget)
+                               );
+                       ncm->port.cdc_filter = DEFAULT_FILTER;
+                       DBG(cdev, "activate ncm\n");
+                       net = gether_connect(&ncm->port);
+                       if (IS_ERR(net))
+                               return PTR_ERR(net);
+                       ncm->netdev = net;
+                       ncm->timer_stopping = false;
+               }
+
+               spin_lock(&ncm->lock);
+               ncm_notify(ncm);
+               spin_unlock(&ncm->lock);
+       } else
+               goto fail;
+
+       return 0;
+fail:
+       return -EINVAL;
+}
+
+/*
+ * Because the data interface supports multiple altsettings,
+ * this NCM function *MUST* implement a get_alt() method.
+ */
+static int ncm_get_alt(struct usb_function *f, unsigned intf)
+{
+       struct f_ncm            *ncm = func_to_ncm(f);
+
+       if (intf == ncm->ctrl_id)
+               return 0;
+       return ncm->port.in_ep->driver_data ? 1 : 0;
+}
+
+static struct sk_buff *package_for_tx(struct f_ncm *ncm)
+{
+       __le16          *ntb_iter;
+       struct sk_buff  *skb2 = NULL;
+       unsigned        ndp_pad;
+       unsigned        ndp_index;
+       unsigned        new_len;
+
+       const struct ndp_parser_opts *opts = ncm->parser_opts;
+       const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment);
+       const int dgram_idx_len = 2 * 2 * opts->dgram_item_len;
+
+       /* Stop the timer */
+       hrtimer_try_to_cancel(&ncm->task_timer);
+
+       ndp_pad = ALIGN(ncm->skb_tx_data->len, ndp_align) -
+                       ncm->skb_tx_data->len;
+       ndp_index = ncm->skb_tx_data->len + ndp_pad;
+       new_len = ndp_index + dgram_idx_len + ncm->skb_tx_ndp->len;
+
+       /* Set the final BlockLength and wNdpIndex */
+       ntb_iter = (void *) ncm->skb_tx_data->data;
+       /* Increment pointer to BlockLength */
+       ntb_iter += 2 + 1 + 1;
+       put_ncm(&ntb_iter, opts->block_length, new_len);
+       put_ncm(&ntb_iter, opts->ndp_index, ndp_index);
+
+       /* Set the final NDP wLength */
+       new_len = opts->ndp_size +
+                       (ncm->ndp_dgram_count * dgram_idx_len);
+       ncm->ndp_dgram_count = 0;
+       /* Increment from start to wLength */
+       ntb_iter = (void *) ncm->skb_tx_ndp->data;
+       ntb_iter += 2;
+       put_unaligned_le16(new_len, ntb_iter);
+
+       /* Merge the skbs */
+       swap(skb2, ncm->skb_tx_data);
+       if (ncm->skb_tx_data) {
+               dev_kfree_skb_any(ncm->skb_tx_data);
+               ncm->skb_tx_data = NULL;
+       }
+
+       /* Insert NDP alignment. */
+       ntb_iter = (void *) skb_put(skb2, ndp_pad);
+       memset(ntb_iter, 0, ndp_pad);
+
+       /* Copy NTB across. */
+       ntb_iter = (void *) skb_put(skb2, ncm->skb_tx_ndp->len);
+       memcpy(ntb_iter, ncm->skb_tx_ndp->data, ncm->skb_tx_ndp->len);
+       dev_kfree_skb_any(ncm->skb_tx_ndp);
+       ncm->skb_tx_ndp = NULL;
+
+       /* Insert zero'd datagram. */
+       ntb_iter = (void *) skb_put(skb2, dgram_idx_len);
+       memset(ntb_iter, 0, dgram_idx_len);
+
+       return skb2;
+}
+
+static struct sk_buff *ncm_wrap_ntb(struct gether *port,
+                                   struct sk_buff *skb)
+{
+       struct f_ncm    *ncm = func_to_ncm(&port->func);
+       struct sk_buff  *skb2 = NULL;
+       int             ncb_len = 0;
+       __le16          *ntb_data;
+       __le16          *ntb_ndp;
+       int             dgram_pad;
+
+       unsigned        max_size = ncm->port.fixed_in_len;
+       const struct ndp_parser_opts *opts = ncm->parser_opts;
+       const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment);
+       const int div = le16_to_cpu(ntb_parameters.wNdpInDivisor);
+       const int rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder);
+       const int dgram_idx_len = 2 * 2 * opts->dgram_item_len;
+
+       if (!skb && !ncm->skb_tx_data)
+               return NULL;
+
+       if (skb) {
+               /* Add the CRC if required up front */
+               if (ncm->is_crc) {
+                       uint32_t        crc;
+                       __le16          *crc_pos;
+
+                       crc = ~crc32_le(~0,
+                                       skb->data,
+                                       skb->len);
+                       crc_pos = (void *) skb_put(skb, sizeof(uint32_t));
+                       put_unaligned_le32(crc, crc_pos);
+               }
+
+               /* If the new skb is too big for the current NCM NTB then
+                * set the current stored skb to be sent now and clear it
+                * ready for new data.
+                * NOTE: Assume maximum align for speed of calculation.
+                */
+               if (ncm->skb_tx_data
+                   && (ncm->ndp_dgram_count >= TX_MAX_NUM_DPE
+                   || (ncm->skb_tx_data->len +
+                   div + rem + skb->len +
+                   ncm->skb_tx_ndp->len + ndp_align + (2 * dgram_idx_len))
+                   > max_size)) {
+                       skb2 = package_for_tx(ncm);
+                       if (!skb2)
+                               goto err;
+               }
+
+               if (!ncm->skb_tx_data) {
+                       ncb_len = opts->nth_size;
+                       dgram_pad = ALIGN(ncb_len, div) + rem - ncb_len;
+                       ncb_len += dgram_pad;
+
+                       /* Create a new skb for the NTH and datagrams. */
+                       ncm->skb_tx_data = alloc_skb(max_size, GFP_ATOMIC);
+                       if (!ncm->skb_tx_data)
+                               goto err;
+
+                       ntb_data = (void *) skb_put(ncm->skb_tx_data, ncb_len);
+                       memset(ntb_data, 0, ncb_len);
+                       /* dwSignature */
+                       put_unaligned_le32(opts->nth_sign, ntb_data);
+                       ntb_data += 2;
+                       /* wHeaderLength */
+                       put_unaligned_le16(opts->nth_size, ntb_data++);
+
+                       /* Allocate an skb for storing the NDP,
+                        * TX_MAX_NUM_DPE should easily suffice for a
+                        * 16k packet.
+                        */
+                       ncm->skb_tx_ndp = alloc_skb((int)(opts->ndp_size
+                                                   + opts->dpe_size
+                                                   * TX_MAX_NUM_DPE),
+                                                   GFP_ATOMIC);
+                       if (!ncm->skb_tx_ndp)
+                               goto err;
+                       ntb_ndp = (void *) skb_put(ncm->skb_tx_ndp,
+                                                   opts->ndp_size);
+                       memset(ntb_ndp, 0, ncb_len);
+                       /* dwSignature */
+                       put_unaligned_le32(ncm->ndp_sign, ntb_ndp);
+                       ntb_ndp += 2;
+
+                       /* There is always a zeroed entry */
+                       ncm->ndp_dgram_count = 1;
+
+                       /* Note: we skip opts->next_ndp_index */
+               }
+
+               /* Delay the timer. */
+               hrtimer_start(&ncm->task_timer,
+                             ktime_set(0, TX_TIMEOUT_NSECS),
+                             HRTIMER_MODE_REL);
+
+               /* Add the datagram position entries */
+               ntb_ndp = (void *) skb_put(ncm->skb_tx_ndp, dgram_idx_len);
+               memset(ntb_ndp, 0, dgram_idx_len);
+
+               ncb_len = ncm->skb_tx_data->len;
+               dgram_pad = ALIGN(ncb_len, div) + rem - ncb_len;
+               ncb_len += dgram_pad;
+
+               /* (d)wDatagramIndex */
+               put_ncm(&ntb_ndp, opts->dgram_item_len, ncb_len);
+               /* (d)wDatagramLength */
+               put_ncm(&ntb_ndp, opts->dgram_item_len, skb->len);
+               ncm->ndp_dgram_count++;
+
+               /* Add the new data to the skb */
+               ntb_data = (void *) skb_put(ncm->skb_tx_data, dgram_pad);
+               memset(ntb_data, 0, dgram_pad);
+               ntb_data = (void *) skb_put(ncm->skb_tx_data, skb->len);
+               memcpy(ntb_data, skb->data, skb->len);
+               dev_kfree_skb_any(skb);
+               skb = NULL;
+
+       } else if (ncm->skb_tx_data && ncm->timer_force_tx) {
+               /* If the tx was requested because of a timeout then send */
+               skb2 = package_for_tx(ncm);
+               if (!skb2)
+                       goto err;
+       }
+
+       return skb2;
+
+err:
+       ncm->netdev->stats.tx_dropped++;
+
+       if (skb)
+               dev_kfree_skb_any(skb);
+       if (ncm->skb_tx_data)
+               dev_kfree_skb_any(ncm->skb_tx_data);
+       if (ncm->skb_tx_ndp)
+               dev_kfree_skb_any(ncm->skb_tx_ndp);
+
+       return NULL;
+}
+
+/*
+ * This transmits the NTB if there are frames waiting.
+ */
+static void ncm_tx_tasklet(unsigned long data)
+{
+       struct f_ncm    *ncm = (void *)data;
+
+       if (ncm->timer_stopping)
+               return;
+
+       /* Only send if data is available. */
+       if (ncm->skb_tx_data) {
+               ncm->timer_force_tx = true;
+               ncm->netdev->netdev_ops->ndo_start_xmit(NULL, ncm->netdev);
+               ncm->timer_force_tx = false;
+       }
+}
+
+/*
+ * The transmit should only be run if no skb data has been sent
+ * for a certain duration.
+ */
+static enum hrtimer_restart ncm_tx_timeout(struct hrtimer *data)
+{
+       struct f_ncm *ncm = container_of(data, struct f_ncm, task_timer);
+       tasklet_schedule(&ncm->tx_tasklet);
+       return HRTIMER_NORESTART;
+}
+
+static int ncm_unwrap_ntb(struct gether *port,
+                         struct sk_buff *skb,
+                         struct sk_buff_head *list)
+{
+       struct f_ncm    *ncm = func_to_ncm(&port->func);
+       __le16          *tmp = (void *) skb->data;
+       unsigned        index, index2;
+       int             ndp_index;
+       unsigned        dg_len, dg_len2;
+       unsigned        ndp_len;
+       struct sk_buff  *skb2;
+       int             ret = -EINVAL;
+       unsigned        max_size = le32_to_cpu(ntb_parameters.dwNtbOutMaxSize);
+       const struct ndp_parser_opts *opts = ncm->parser_opts;
+       unsigned        crc_len = ncm->is_crc ? sizeof(uint32_t) : 0;
+       int             dgram_counter;
+
+       /* dwSignature */
+       if (get_unaligned_le32(tmp) != opts->nth_sign) {
+               INFO(port->func.config->cdev, "Wrong NTH SIGN, skblen %d\n",
+                       skb->len);
+               print_hex_dump(KERN_INFO, "HEAD:", DUMP_PREFIX_ADDRESS, 32, 1,
+                              skb->data, 32, false);
+
+               goto err;
+       }
+       tmp += 2;
+       /* wHeaderLength */
+       if (get_unaligned_le16(tmp++) != opts->nth_size) {
+               INFO(port->func.config->cdev, "Wrong NTB headersize\n");
+               goto err;
+       }
+       tmp++; /* skip wSequence */
+
+       /* (d)wBlockLength */
+       if (get_ncm(&tmp, opts->block_length) > max_size) {
+               INFO(port->func.config->cdev, "OUT size exceeded\n");
+               goto err;
+       }
+
+       ndp_index = get_ncm(&tmp, opts->ndp_index);
+
+       /* Run through all the NDP's in the NTB */
+       do {
+               /* NCM 3.2 */
+               if (((ndp_index % 4) != 0) &&
+                               (ndp_index < opts->nth_size)) {
+                       INFO(port->func.config->cdev, "Bad index: %#X\n",
+                            ndp_index);
+                       goto err;
+               }
+
+               /* walk through NDP */
+               tmp = (void *)(skb->data + ndp_index);
+               if (get_unaligned_le32(tmp) != ncm->ndp_sign) {
+                       INFO(port->func.config->cdev, "Wrong NDP SIGN\n");
+                       goto err;
+               }
+               tmp += 2;
+
+               ndp_len = get_unaligned_le16(tmp++);
+               /*
+                * NCM 3.3.1
+                * entry is 2 items
+                * item size is 16/32 bits, opts->dgram_item_len * 2 bytes
+                * minimal: struct usb_cdc_ncm_ndpX + normal entry + zero entry
+                * Each entry is a dgram index and a dgram length.
+                */
+               if ((ndp_len < opts->ndp_size
+                               + 2 * 2 * (opts->dgram_item_len * 2))
+                               || (ndp_len % opts->ndplen_align != 0)) {
+                       INFO(port->func.config->cdev, "Bad NDP length: %#X\n",
+                            ndp_len);
+                       goto err;
+               }
+               tmp += opts->reserved1;
+               /* Check for another NDP (d)wNextNdpIndex */
+               ndp_index = get_ncm(&tmp, opts->next_ndp_index);
+               tmp += opts->reserved2;
+
+               ndp_len -= opts->ndp_size;
+               index2 = get_ncm(&tmp, opts->dgram_item_len);
+               dg_len2 = get_ncm(&tmp, opts->dgram_item_len);
+               dgram_counter = 0;
+
+               do {
+                       index = index2;
+                       dg_len = dg_len2;
+                       if (dg_len < 14 + crc_len) { /* ethernet hdr + crc */
+                               INFO(port->func.config->cdev,
+                                    "Bad dgram length: %#X\n", dg_len);
+                               goto err;
+                       }
+                       if (ncm->is_crc) {
+                               uint32_t crc, crc2;
+
+                               crc = get_unaligned_le32(skb->data +
+                                                        index + dg_len -
+                                                        crc_len);
+                               crc2 = ~crc32_le(~0,
+                                                skb->data + index,
+                                                dg_len - crc_len);
+                               if (crc != crc2) {
+                                       INFO(port->func.config->cdev,
+                                            "Bad CRC\n");
+                                       goto err;
+                               }
+                       }
+
+                       index2 = get_ncm(&tmp, opts->dgram_item_len);
+                       dg_len2 = get_ncm(&tmp, opts->dgram_item_len);
+
+                       /*
+                        * Copy the data into a new skb.
+                        * This ensures the truesize is correct
+                        */
+                       skb2 = netdev_alloc_skb_ip_align(ncm->netdev,
+                                                        dg_len - crc_len);
+                       if (skb2 == NULL)
+                               goto err;
+                       memcpy(skb_put(skb2, dg_len - crc_len),
+                              skb->data + index, dg_len - crc_len);
+
+                       skb_queue_tail(list, skb2);
+
+                       ndp_len -= 2 * (opts->dgram_item_len * 2);
+
+                       dgram_counter++;
+
+                       if (index2 == 0 || dg_len2 == 0)
+                               break;
+               } while (ndp_len > 2 * (opts->dgram_item_len * 2));
+       } while (ndp_index);
+
+       dev_kfree_skb_any(skb);
+
+       VDBG(port->func.config->cdev,
+            "Parsed NTB with %d frames\n", dgram_counter);
+       return 0;
+err:
+       skb_queue_purge(list);
+       dev_kfree_skb_any(skb);
+       return ret;
+}
+
+static void ncm_disable(struct usb_function *f)
+{
+       struct f_ncm            *ncm = func_to_ncm(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+
+       DBG(cdev, "ncm deactivated\n");
+
+       if (ncm->port.in_ep->driver_data) {
+               ncm->timer_stopping = true;
+               ncm->netdev = NULL;
+               gether_disconnect(&ncm->port);
+       }
+
+       if (ncm->notify->driver_data) {
+               usb_ep_disable(ncm->notify);
+               ncm->notify->driver_data = NULL;
+               ncm->notify->desc = NULL;
+       }
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Callbacks let us notify the host about connect/disconnect when the
+ * net device is opened or closed.
+ *
+ * For testing, note that link states on this side include both opened
+ * and closed variants of:
+ *
+ *   - disconnected/unconfigured
+ *   - configured but inactive (data alt 0)
+ *   - configured and active (data alt 1)
+ *
+ * Each needs to be tested with unplug, rmmod, SET_CONFIGURATION, and
+ * SET_INTERFACE (altsetting).  Remember also that "configured" doesn't
+ * imply the host is actually polling the notification endpoint, and
+ * likewise that "active" doesn't imply it's actually using the data
+ * endpoints for traffic.
+ */
+
+static void ncm_open(struct gether *geth)
+{
+       struct f_ncm            *ncm = func_to_ncm(&geth->func);
+
+       DBG(ncm->port.func.config->cdev, "%s\n", __func__);
+
+       spin_lock(&ncm->lock);
+       ncm->is_open = true;
+       ncm_notify(ncm);
+       spin_unlock(&ncm->lock);
+}
+
+static void ncm_close(struct gether *geth)
+{
+       struct f_ncm            *ncm = func_to_ncm(&geth->func);
+
+       DBG(ncm->port.func.config->cdev, "%s\n", __func__);
+
+       spin_lock(&ncm->lock);
+       ncm->is_open = false;
+       ncm_notify(ncm);
+       spin_unlock(&ncm->lock);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* ethernet function driver setup/binding */
+
+static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct usb_composite_dev *cdev = c->cdev;
+       struct f_ncm            *ncm = func_to_ncm(f);
+       struct usb_string       *us;
+       int                     status;
+       struct usb_ep           *ep;
+       struct f_ncm_opts       *ncm_opts;
+
+       if (!can_support_ecm(cdev->gadget))
+               return -EINVAL;
+
+       ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst);
+       /*
+        * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
+        * configurations are bound in sequence with list_for_each_entry,
+        * in each configuration its functions are bound in sequence
+        * with list_for_each_entry, so we assume no race condition
+        * with regard to ncm_opts->bound access
+        */
+       if (!ncm_opts->bound) {
+               mutex_lock(&ncm_opts->lock);
+               gether_set_gadget(ncm_opts->net, cdev->gadget);
+               status = gether_register_netdev(ncm_opts->net);
+               mutex_unlock(&ncm_opts->lock);
+               if (status)
+                       return status;
+               ncm_opts->bound = true;
+       }
+       us = usb_gstrings_attach(cdev, ncm_strings,
+                                ARRAY_SIZE(ncm_string_defs));
+       if (IS_ERR(us))
+               return PTR_ERR(us);
+       ncm_control_intf.iInterface = us[STRING_CTRL_IDX].id;
+       ncm_data_nop_intf.iInterface = us[STRING_DATA_IDX].id;
+       ncm_data_intf.iInterface = us[STRING_DATA_IDX].id;
+       ecm_desc.iMACAddress = us[STRING_MAC_IDX].id;
+       ncm_iad_desc.iFunction = us[STRING_IAD_IDX].id;
+
+       /* allocate instance-specific interface IDs */
+       status = usb_interface_id(c, f);
+       if (status < 0)
+               goto fail;
+       ncm->ctrl_id = status;
+       ncm_iad_desc.bFirstInterface = status;
+
+       ncm_control_intf.bInterfaceNumber = status;
+       ncm_union_desc.bMasterInterface0 = status;
+
+       status = usb_interface_id(c, f);
+       if (status < 0)
+               goto fail;
+       ncm->data_id = status;
+
+       ncm_data_nop_intf.bInterfaceNumber = status;
+       ncm_data_intf.bInterfaceNumber = status;
+       ncm_union_desc.bSlaveInterface0 = status;
+
+       status = -ENODEV;
+
+       /* allocate instance-specific endpoints */
+       ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_in_desc);
+       if (!ep)
+               goto fail;
+       ncm->port.in_ep = ep;
+       ep->driver_data = cdev; /* claim */
+
+       ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_out_desc);
+       if (!ep)
+               goto fail;
+       ncm->port.out_ep = ep;
+       ep->driver_data = cdev; /* claim */
+
+       ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_notify_desc);
+       if (!ep)
+               goto fail;
+       ncm->notify = ep;
+       ep->driver_data = cdev; /* claim */
+
+       status = -ENOMEM;
+
+       /* allocate notification request and buffer */
+       ncm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
+       if (!ncm->notify_req)
+               goto fail;
+       ncm->notify_req->buf = kmalloc(NCM_STATUS_BYTECOUNT, GFP_KERNEL);
+       if (!ncm->notify_req->buf)
+               goto fail;
+       ncm->notify_req->context = ncm;
+       ncm->notify_req->complete = ncm_notify_complete;
+
+       /*
+        * support all relevant hardware speeds... we expect that when
+        * hardware is dual speed, all bulk-capable endpoints work at
+        * both speeds
+        */
+       hs_ncm_in_desc.bEndpointAddress = fs_ncm_in_desc.bEndpointAddress;
+       hs_ncm_out_desc.bEndpointAddress = fs_ncm_out_desc.bEndpointAddress;
+       hs_ncm_notify_desc.bEndpointAddress =
+               fs_ncm_notify_desc.bEndpointAddress;
+
+       status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function,
+                       NULL);
+       /*
+        * NOTE:  all that is done without knowing or caring about
+        * the network link ... which is unavailable to this code
+        * until we're activated via set_alt().
+        */
+
+       ncm->port.open = ncm_open;
+       ncm->port.close = ncm_close;
+
+       tasklet_init(&ncm->tx_tasklet, ncm_tx_tasklet, (unsigned long) ncm);
+       hrtimer_init(&ncm->task_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+       ncm->task_timer.function = ncm_tx_timeout;
+
+       DBG(cdev, "CDC Network: %s speed IN/%s OUT/%s NOTIFY/%s\n",
+                       gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+                       ncm->port.in_ep->name, ncm->port.out_ep->name,
+                       ncm->notify->name);
+       return 0;
+
+fail:
+       usb_free_all_descriptors(f);
+       if (ncm->notify_req) {
+               kfree(ncm->notify_req->buf);
+               usb_ep_free_request(ncm->notify, ncm->notify_req);
+       }
+
+       /* we might as well release our claims on endpoints */
+       if (ncm->notify)
+               ncm->notify->driver_data = NULL;
+       if (ncm->port.out_ep)
+               ncm->port.out_ep->driver_data = NULL;
+       if (ncm->port.in_ep)
+               ncm->port.in_ep->driver_data = NULL;
+
+       ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
+
+       return status;
+}
+
+static inline struct f_ncm_opts *to_f_ncm_opts(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct f_ncm_opts,
+                           func_inst.group);
+}
+
+/* f_ncm_item_ops */
+USB_ETHERNET_CONFIGFS_ITEM(ncm);
+
+/* f_ncm_opts_dev_addr */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ncm);
+
+/* f_ncm_opts_host_addr */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ncm);
+
+/* f_ncm_opts_qmult */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ncm);
+
+/* f_ncm_opts_ifname */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ncm);
+
+static struct configfs_attribute *ncm_attrs[] = {
+       &f_ncm_opts_dev_addr.attr,
+       &f_ncm_opts_host_addr.attr,
+       &f_ncm_opts_qmult.attr,
+       &f_ncm_opts_ifname.attr,
+       NULL,
+};
+
+static struct config_item_type ncm_func_type = {
+       .ct_item_ops    = &ncm_item_ops,
+       .ct_attrs       = ncm_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+static void ncm_free_inst(struct usb_function_instance *f)
+{
+       struct f_ncm_opts *opts;
+
+       opts = container_of(f, struct f_ncm_opts, func_inst);
+       if (opts->bound)
+               gether_cleanup(netdev_priv(opts->net));
+       else
+               free_netdev(opts->net);
+       kfree(opts);
+}
+
+static struct usb_function_instance *ncm_alloc_inst(void)
+{
+       struct f_ncm_opts *opts;
+
+       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+       if (!opts)
+               return ERR_PTR(-ENOMEM);
+       mutex_init(&opts->lock);
+       opts->func_inst.free_func_inst = ncm_free_inst;
+       opts->net = gether_setup_default();
+       if (IS_ERR(opts->net)) {
+               struct net_device *net = opts->net;
+               kfree(opts);
+               return ERR_CAST(net);
+       }
+
+       config_group_init_type_name(&opts->func_inst.group, "", &ncm_func_type);
+
+       return &opts->func_inst;
+}
+
+static void ncm_free(struct usb_function *f)
+{
+       struct f_ncm *ncm;
+       struct f_ncm_opts *opts;
+
+       ncm = func_to_ncm(f);
+       opts = container_of(f->fi, struct f_ncm_opts, func_inst);
+       kfree(ncm);
+       mutex_lock(&opts->lock);
+       opts->refcnt--;
+       mutex_unlock(&opts->lock);
+}
+
+static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct f_ncm *ncm = func_to_ncm(f);
+
+       DBG(c->cdev, "ncm unbind\n");
+
+       hrtimer_cancel(&ncm->task_timer);
+       tasklet_kill(&ncm->tx_tasklet);
+
+       ncm_string_defs[0].id = 0;
+       usb_free_all_descriptors(f);
+
+       kfree(ncm->notify_req->buf);
+       usb_ep_free_request(ncm->notify, ncm->notify_req);
+}
+
+static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
+{
+       struct f_ncm            *ncm;
+       struct f_ncm_opts       *opts;
+       int status;
+
+       /* allocate and initialize one new instance */
+       ncm = kzalloc(sizeof(*ncm), GFP_KERNEL);
+       if (!ncm)
+               return ERR_PTR(-ENOMEM);
+
+       opts = container_of(fi, struct f_ncm_opts, func_inst);
+       mutex_lock(&opts->lock);
+       opts->refcnt++;
+
+       /* export host's Ethernet address in CDC format */
+       status = gether_get_host_addr_cdc(opts->net, ncm->ethaddr,
+                                     sizeof(ncm->ethaddr));
+       if (status < 12) { /* strlen("01234567890a") */
+               kfree(ncm);
+               mutex_unlock(&opts->lock);
+               return ERR_PTR(-EINVAL);
+       }
+       ncm_string_defs[STRING_MAC_IDX].s = ncm->ethaddr;
+
+       spin_lock_init(&ncm->lock);
+       ncm_reset_values(ncm);
+       ncm->port.ioport = netdev_priv(opts->net);
+       mutex_unlock(&opts->lock);
+       ncm->port.is_fixed = true;
+       ncm->port.supports_multi_frame = true;
+
+       ncm->port.func.name = "cdc_network";
+       /* descriptors are per-instance copies */
+       ncm->port.func.bind = ncm_bind;
+       ncm->port.func.unbind = ncm_unbind;
+       ncm->port.func.set_alt = ncm_set_alt;
+       ncm->port.func.get_alt = ncm_get_alt;
+       ncm->port.func.setup = ncm_setup;
+       ncm->port.func.disable = ncm_disable;
+       ncm->port.func.free_func = ncm_free;
+
+       ncm->port.wrap = ncm_wrap_ntb;
+       ncm->port.unwrap = ncm_unwrap_ntb;
+
+       return &ncm->port.func;
+}
+
+DECLARE_USB_FUNCTION_INIT(ncm, ncm_alloc_inst, ncm_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Yauheni Kaliuta");
diff --git a/drivers/usb/gadget/function/f_obex.c b/drivers/usb/gadget/function/f_obex.c
new file mode 100644 (file)
index 0000000..aebae18
--- /dev/null
@@ -0,0 +1,533 @@
+/*
+ * f_obex.c -- USB CDC OBEX function driver
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Contact: Felipe Balbi <felipe.balbi@nokia.com>
+ *
+ * Based on f_acm.c by Al Borchers and 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.
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+
+#include "u_serial.h"
+#include "gadget_chips.h"
+
+
+/*
+ * This CDC OBEX function support just packages a TTY-ish byte stream.
+ * A user mode server will put it into "raw" mode and handle all the
+ * relevant protocol details ... this is just a kernel passthrough.
+ * When possible, we prevent gadget enumeration until that server is
+ * ready to handle the commands.
+ */
+
+struct f_obex {
+       struct gserial                  port;
+       u8                              ctrl_id;
+       u8                              data_id;
+       u8                              port_num;
+       u8                              can_activate;
+};
+
+static inline struct f_obex *func_to_obex(struct usb_function *f)
+{
+       return container_of(f, struct f_obex, port.func);
+}
+
+static inline struct f_obex *port_to_obex(struct gserial *p)
+{
+       return container_of(p, struct f_obex, port);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define OBEX_CTRL_IDX  0
+#define OBEX_DATA_IDX  1
+
+static struct usb_string obex_string_defs[] = {
+       [OBEX_CTRL_IDX].s       = "CDC Object Exchange (OBEX)",
+       [OBEX_DATA_IDX].s       = "CDC OBEX Data",
+       {  },   /* end of list */
+};
+
+static struct usb_gadget_strings obex_string_table = {
+       .language               = 0x0409,       /* en-US */
+       .strings                = obex_string_defs,
+};
+
+static struct usb_gadget_strings *obex_strings[] = {
+       &obex_string_table,
+       NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_interface_descriptor obex_control_intf = {
+       .bLength                = sizeof(obex_control_intf),
+       .bDescriptorType        = USB_DT_INTERFACE,
+       .bInterfaceNumber       = 0,
+
+       .bAlternateSetting      = 0,
+       .bNumEndpoints          = 0,
+       .bInterfaceClass        = USB_CLASS_COMM,
+       .bInterfaceSubClass     = USB_CDC_SUBCLASS_OBEX,
+};
+
+static struct usb_interface_descriptor obex_data_nop_intf = {
+       .bLength                = sizeof(obex_data_nop_intf),
+       .bDescriptorType        = USB_DT_INTERFACE,
+       .bInterfaceNumber       = 1,
+
+       .bAlternateSetting      = 0,
+       .bNumEndpoints          = 0,
+       .bInterfaceClass        = USB_CLASS_CDC_DATA,
+};
+
+static struct usb_interface_descriptor obex_data_intf = {
+       .bLength                = sizeof(obex_data_intf),
+       .bDescriptorType        = USB_DT_INTERFACE,
+       .bInterfaceNumber       = 2,
+
+       .bAlternateSetting      = 1,
+       .bNumEndpoints          = 2,
+       .bInterfaceClass        = USB_CLASS_CDC_DATA,
+};
+
+static struct usb_cdc_header_desc obex_cdc_header_desc = {
+       .bLength                = sizeof(obex_cdc_header_desc),
+       .bDescriptorType        = USB_DT_CS_INTERFACE,
+       .bDescriptorSubType     = USB_CDC_HEADER_TYPE,
+       .bcdCDC                 = cpu_to_le16(0x0120),
+};
+
+static struct usb_cdc_union_desc obex_cdc_union_desc = {
+       .bLength                = sizeof(obex_cdc_union_desc),
+       .bDescriptorType        = USB_DT_CS_INTERFACE,
+       .bDescriptorSubType     = USB_CDC_UNION_TYPE,
+       .bMasterInterface0      = 1,
+       .bSlaveInterface0       = 2,
+};
+
+static struct usb_cdc_obex_desc obex_desc = {
+       .bLength                = sizeof(obex_desc),
+       .bDescriptorType        = USB_DT_CS_INTERFACE,
+       .bDescriptorSubType     = USB_CDC_OBEX_TYPE,
+       .bcdVersion             = cpu_to_le16(0x0100),
+};
+
+/* High-Speed Support */
+
+static struct usb_endpoint_descriptor obex_hs_ep_out_desc = {
+       .bLength                = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType        = USB_DT_ENDPOINT,
+
+       .bEndpointAddress       = USB_DIR_OUT,
+       .bmAttributes           = USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize         = cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor obex_hs_ep_in_desc = {
+       .bLength                = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType        = USB_DT_ENDPOINT,
+
+       .bEndpointAddress       = USB_DIR_IN,
+       .bmAttributes           = USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize         = cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *hs_function[] = {
+       (struct usb_descriptor_header *) &obex_control_intf,
+       (struct usb_descriptor_header *) &obex_cdc_header_desc,
+       (struct usb_descriptor_header *) &obex_desc,
+       (struct usb_descriptor_header *) &obex_cdc_union_desc,
+
+       (struct usb_descriptor_header *) &obex_data_nop_intf,
+       (struct usb_descriptor_header *) &obex_data_intf,
+       (struct usb_descriptor_header *) &obex_hs_ep_in_desc,
+       (struct usb_descriptor_header *) &obex_hs_ep_out_desc,
+       NULL,
+};
+
+/* Full-Speed Support */
+
+static struct usb_endpoint_descriptor obex_fs_ep_in_desc = {
+       .bLength                = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType        = USB_DT_ENDPOINT,
+
+       .bEndpointAddress       = USB_DIR_IN,
+       .bmAttributes           = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor obex_fs_ep_out_desc = {
+       .bLength                = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType        = USB_DT_ENDPOINT,
+
+       .bEndpointAddress       = USB_DIR_OUT,
+       .bmAttributes           = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *fs_function[] = {
+       (struct usb_descriptor_header *) &obex_control_intf,
+       (struct usb_descriptor_header *) &obex_cdc_header_desc,
+       (struct usb_descriptor_header *) &obex_desc,
+       (struct usb_descriptor_header *) &obex_cdc_union_desc,
+
+       (struct usb_descriptor_header *) &obex_data_nop_intf,
+       (struct usb_descriptor_header *) &obex_data_intf,
+       (struct usb_descriptor_header *) &obex_fs_ep_in_desc,
+       (struct usb_descriptor_header *) &obex_fs_ep_out_desc,
+       NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int obex_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+       struct f_obex           *obex = func_to_obex(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+
+       if (intf == obex->ctrl_id) {
+               if (alt != 0)
+                       goto fail;
+               /* NOP */
+               DBG(cdev, "reset obex ttyGS%d control\n", obex->port_num);
+
+       } else if (intf == obex->data_id) {
+               if (alt > 1)
+                       goto fail;
+
+               if (obex->port.in->driver_data) {
+                       DBG(cdev, "reset obex ttyGS%d\n", obex->port_num);
+                       gserial_disconnect(&obex->port);
+               }
+
+               if (!obex->port.in->desc || !obex->port.out->desc) {
+                       DBG(cdev, "init obex ttyGS%d\n", obex->port_num);
+                       if (config_ep_by_speed(cdev->gadget, f,
+                                              obex->port.in) ||
+                           config_ep_by_speed(cdev->gadget, f,
+                                              obex->port.out)) {
+                               obex->port.out->desc = NULL;
+                               obex->port.in->desc = NULL;
+                               goto fail;
+                       }
+               }
+
+               if (alt == 1) {
+                       DBG(cdev, "activate obex ttyGS%d\n", obex->port_num);
+                       gserial_connect(&obex->port, obex->port_num);
+               }
+
+       } else
+               goto fail;
+
+       return 0;
+
+fail:
+       return -EINVAL;
+}
+
+static int obex_get_alt(struct usb_function *f, unsigned intf)
+{
+       struct f_obex           *obex = func_to_obex(f);
+
+       if (intf == obex->ctrl_id)
+               return 0;
+
+       return obex->port.in->driver_data ? 1 : 0;
+}
+
+static void obex_disable(struct usb_function *f)
+{
+       struct f_obex   *obex = func_to_obex(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+
+       DBG(cdev, "obex ttyGS%d disable\n", obex->port_num);
+       gserial_disconnect(&obex->port);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void obex_connect(struct gserial *g)
+{
+       struct f_obex           *obex = port_to_obex(g);
+       struct usb_composite_dev *cdev = g->func.config->cdev;
+       int                     status;
+
+       if (!obex->can_activate)
+               return;
+
+       status = usb_function_activate(&g->func);
+       if (status)
+               DBG(cdev, "obex ttyGS%d function activate --> %d\n",
+                       obex->port_num, status);
+}
+
+static void obex_disconnect(struct gserial *g)
+{
+       struct f_obex           *obex = port_to_obex(g);
+       struct usb_composite_dev *cdev = g->func.config->cdev;
+       int                     status;
+
+       if (!obex->can_activate)
+               return;
+
+       status = usb_function_deactivate(&g->func);
+       if (status)
+               DBG(cdev, "obex ttyGS%d function deactivate --> %d\n",
+                       obex->port_num, status);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Some controllers can't support CDC OBEX ... */
+static inline bool can_support_obex(struct usb_configuration *c)
+{
+       /* Since the first interface is a NOP, we can ignore the
+        * issue of multi-interface support on most controllers.
+        *
+        * Altsettings are mandatory, however...
+        */
+       if (!gadget_supports_altsettings(c->cdev->gadget))
+               return false;
+
+       /* everything else is *probably* fine ... */
+       return true;
+}
+
+static int obex_bind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct usb_composite_dev *cdev = c->cdev;
+       struct f_obex           *obex = func_to_obex(f);
+       struct usb_string       *us;
+       int                     status;
+       struct usb_ep           *ep;
+
+       if (!can_support_obex(c))
+               return -EINVAL;
+
+       us = usb_gstrings_attach(cdev, obex_strings,
+                                ARRAY_SIZE(obex_string_defs));
+       if (IS_ERR(us))
+               return PTR_ERR(us);
+       obex_control_intf.iInterface = us[OBEX_CTRL_IDX].id;
+       obex_data_nop_intf.iInterface = us[OBEX_DATA_IDX].id;
+       obex_data_intf.iInterface = us[OBEX_DATA_IDX].id;
+
+       /* allocate instance-specific interface IDs, and patch descriptors */
+
+       status = usb_interface_id(c, f);
+       if (status < 0)
+               goto fail;
+       obex->ctrl_id = status;
+
+       obex_control_intf.bInterfaceNumber = status;
+       obex_cdc_union_desc.bMasterInterface0 = status;
+
+       status = usb_interface_id(c, f);
+       if (status < 0)
+               goto fail;
+       obex->data_id = status;
+
+       obex_data_nop_intf.bInterfaceNumber = status;
+       obex_data_intf.bInterfaceNumber = status;
+       obex_cdc_union_desc.bSlaveInterface0 = status;
+
+       /* allocate instance-specific endpoints */
+
+       status = -ENODEV;
+       ep = usb_ep_autoconfig(cdev->gadget, &obex_fs_ep_in_desc);
+       if (!ep)
+               goto fail;
+       obex->port.in = ep;
+       ep->driver_data = cdev; /* claim */
+
+       ep = usb_ep_autoconfig(cdev->gadget, &obex_fs_ep_out_desc);
+       if (!ep)
+               goto fail;
+       obex->port.out = ep;
+       ep->driver_data = cdev; /* claim */
+
+       /* support all relevant hardware speeds... we expect that when
+        * hardware is dual speed, all bulk-capable endpoints work at
+        * both speeds
+        */
+
+       obex_hs_ep_in_desc.bEndpointAddress =
+               obex_fs_ep_in_desc.bEndpointAddress;
+       obex_hs_ep_out_desc.bEndpointAddress =
+               obex_fs_ep_out_desc.bEndpointAddress;
+
+       status = usb_assign_descriptors(f, fs_function, hs_function, NULL);
+       if (status)
+               goto fail;
+
+       /* Avoid letting this gadget enumerate until the userspace
+        * OBEX server is active.
+        */
+       status = usb_function_deactivate(f);
+       if (status < 0)
+               WARNING(cdev, "obex ttyGS%d: can't prevent enumeration, %d\n",
+                       obex->port_num, status);
+       else
+               obex->can_activate = true;
+
+
+       DBG(cdev, "obex ttyGS%d: %s speed IN/%s OUT/%s\n",
+                       obex->port_num,
+                       gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+                       obex->port.in->name, obex->port.out->name);
+
+       return 0;
+
+fail:
+       usb_free_all_descriptors(f);
+       /* we might as well release our claims on endpoints */
+       if (obex->port.out)
+               obex->port.out->driver_data = NULL;
+       if (obex->port.in)
+               obex->port.in->driver_data = NULL;
+
+       ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status);
+
+       return status;
+}
+
+static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct f_serial_opts,
+                           func_inst.group);
+}
+
+CONFIGFS_ATTR_STRUCT(f_serial_opts);
+static ssize_t f_obex_attr_show(struct config_item *item,
+                               struct configfs_attribute *attr,
+                               char *page)
+{
+       struct f_serial_opts *opts = to_f_serial_opts(item);
+       struct f_serial_opts_attribute *f_serial_opts_attr =
+               container_of(attr, struct f_serial_opts_attribute, attr);
+       ssize_t ret = 0;
+
+       if (f_serial_opts_attr->show)
+               ret = f_serial_opts_attr->show(opts, page);
+
+       return ret;
+}
+
+static void obex_attr_release(struct config_item *item)
+{
+       struct f_serial_opts *opts = to_f_serial_opts(item);
+
+       usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations obex_item_ops = {
+       .release        = obex_attr_release,
+       .show_attribute = f_obex_attr_show,
+};
+
+static ssize_t f_obex_port_num_show(struct f_serial_opts *opts, char *page)
+{
+       return sprintf(page, "%u\n", opts->port_num);
+}
+
+static struct f_serial_opts_attribute f_obex_port_num =
+       __CONFIGFS_ATTR_RO(port_num, f_obex_port_num_show);
+
+static struct configfs_attribute *acm_attrs[] = {
+       &f_obex_port_num.attr,
+       NULL,
+};
+
+static struct config_item_type obex_func_type = {
+       .ct_item_ops    = &obex_item_ops,
+       .ct_attrs       = acm_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+static void obex_free_inst(struct usb_function_instance *f)
+{
+       struct f_serial_opts *opts;
+
+       opts = container_of(f, struct f_serial_opts, func_inst);
+       gserial_free_line(opts->port_num);
+       kfree(opts);
+}
+
+static struct usb_function_instance *obex_alloc_inst(void)
+{
+       struct f_serial_opts *opts;
+       int ret;
+
+       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+       if (!opts)
+               return ERR_PTR(-ENOMEM);
+
+       opts->func_inst.free_func_inst = obex_free_inst;
+       ret = gserial_alloc_line(&opts->port_num);
+       if (ret) {
+               kfree(opts);
+               return ERR_PTR(ret);
+       }
+       config_group_init_type_name(&opts->func_inst.group, "",
+                                   &obex_func_type);
+
+       return &opts->func_inst;
+}
+
+static void obex_free(struct usb_function *f)
+{
+       struct f_obex *obex;
+
+       obex = func_to_obex(f);
+       kfree(obex);
+}
+
+static void obex_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+       usb_free_all_descriptors(f);
+}
+
+static struct usb_function *obex_alloc(struct usb_function_instance *fi)
+{
+       struct f_obex   *obex;
+       struct f_serial_opts *opts;
+
+       /* allocate and initialize one new instance */
+       obex = kzalloc(sizeof(*obex), GFP_KERNEL);
+       if (!obex)
+               return ERR_PTR(-ENOMEM);
+
+       opts = container_of(fi, struct f_serial_opts, func_inst);
+
+       obex->port_num = opts->port_num;
+
+       obex->port.connect = obex_connect;
+       obex->port.disconnect = obex_disconnect;
+
+       obex->port.func.name = "obex";
+       /* descriptors are per-instance copies */
+       obex->port.func.bind = obex_bind;
+       obex->port.func.unbind = obex_unbind;
+       obex->port.func.set_alt = obex_set_alt;
+       obex->port.func.get_alt = obex_get_alt;
+       obex->port.func.disable = obex_disable;
+       obex->port.func.free_func = obex_free;
+
+       return &obex->port.func;
+}
+
+DECLARE_USB_FUNCTION_INIT(obex, obex_alloc_inst, obex_alloc);
+MODULE_AUTHOR("Felipe Balbi");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c
new file mode 100644 (file)
index 0000000..f2b7817
--- /dev/null
@@ -0,0 +1,758 @@
+/*
+ * f_phonet.c -- USB CDC Phonet function
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
+ *
+ * Author: Rémi Denis-Courmont
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+
+#include <linux/netdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_phonet.h>
+#include <linux/if_arp.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/cdc.h>
+#include <linux/usb/composite.h>
+
+#include "u_phonet.h"
+#include "u_ether.h"
+
+#define PN_MEDIA_USB   0x1B
+#define MAXPACKET      512
+#if (PAGE_SIZE % MAXPACKET)
+#error MAXPACKET must divide PAGE_SIZE!
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+struct phonet_port {
+       struct f_phonet                 *usb;
+       spinlock_t                      lock;
+};
+
+struct f_phonet {
+       struct usb_function             function;
+       struct {
+               struct sk_buff          *skb;
+               spinlock_t              lock;
+       } rx;
+       struct net_device               *dev;
+       struct usb_ep                   *in_ep, *out_ep;
+
+       struct usb_request              *in_req;
+       struct usb_request              *out_reqv[0];
+};
+
+static int phonet_rxq_size = 17;
+
+static inline struct f_phonet *func_to_pn(struct usb_function *f)
+{
+       return container_of(f, struct f_phonet, function);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define USB_CDC_SUBCLASS_PHONET        0xfe
+#define USB_CDC_PHONET_TYPE    0xab
+
+static struct usb_interface_descriptor
+pn_control_intf_desc = {
+       .bLength =              sizeof pn_control_intf_desc,
+       .bDescriptorType =      USB_DT_INTERFACE,
+
+       /* .bInterfaceNumber =  DYNAMIC, */
+       .bInterfaceClass =      USB_CLASS_COMM,
+       .bInterfaceSubClass =   USB_CDC_SUBCLASS_PHONET,
+};
+
+static const struct usb_cdc_header_desc
+pn_header_desc = {
+       .bLength =              sizeof pn_header_desc,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubType =   USB_CDC_HEADER_TYPE,
+       .bcdCDC =               cpu_to_le16(0x0110),
+};
+
+static const struct usb_cdc_header_desc
+pn_phonet_desc = {
+       .bLength =              sizeof pn_phonet_desc,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubType =   USB_CDC_PHONET_TYPE,
+       .bcdCDC =               cpu_to_le16(0x1505), /* ??? */
+};
+
+static struct usb_cdc_union_desc
+pn_union_desc = {
+       .bLength =              sizeof pn_union_desc,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubType =   USB_CDC_UNION_TYPE,
+
+       /* .bMasterInterface0 = DYNAMIC, */
+       /* .bSlaveInterface0 =  DYNAMIC, */
+};
+
+static struct usb_interface_descriptor
+pn_data_nop_intf_desc = {
+       .bLength =              sizeof pn_data_nop_intf_desc,
+       .bDescriptorType =      USB_DT_INTERFACE,
+
+       /* .bInterfaceNumber =  DYNAMIC, */
+       .bAlternateSetting =    0,
+       .bNumEndpoints =        0,
+       .bInterfaceClass =      USB_CLASS_CDC_DATA,
+};
+
+static struct usb_interface_descriptor
+pn_data_intf_desc = {
+       .bLength =              sizeof pn_data_intf_desc,
+       .bDescriptorType =      USB_DT_INTERFACE,
+
+       /* .bInterfaceNumber =  DYNAMIC, */
+       .bAlternateSetting =    1,
+       .bNumEndpoints =        2,
+       .bInterfaceClass =      USB_CLASS_CDC_DATA,
+};
+
+static struct usb_endpoint_descriptor
+pn_fs_sink_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor
+pn_hs_sink_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(MAXPACKET),
+};
+
+static struct usb_endpoint_descriptor
+pn_fs_source_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor
+pn_hs_source_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *fs_pn_function[] = {
+       (struct usb_descriptor_header *) &pn_control_intf_desc,
+       (struct usb_descriptor_header *) &pn_header_desc,
+       (struct usb_descriptor_header *) &pn_phonet_desc,
+       (struct usb_descriptor_header *) &pn_union_desc,
+       (struct usb_descriptor_header *) &pn_data_nop_intf_desc,
+       (struct usb_descriptor_header *) &pn_data_intf_desc,
+       (struct usb_descriptor_header *) &pn_fs_sink_desc,
+       (struct usb_descriptor_header *) &pn_fs_source_desc,
+       NULL,
+};
+
+static struct usb_descriptor_header *hs_pn_function[] = {
+       (struct usb_descriptor_header *) &pn_control_intf_desc,
+       (struct usb_descriptor_header *) &pn_header_desc,
+       (struct usb_descriptor_header *) &pn_phonet_desc,
+       (struct usb_descriptor_header *) &pn_union_desc,
+       (struct usb_descriptor_header *) &pn_data_nop_intf_desc,
+       (struct usb_descriptor_header *) &pn_data_intf_desc,
+       (struct usb_descriptor_header *) &pn_hs_sink_desc,
+       (struct usb_descriptor_header *) &pn_hs_source_desc,
+       NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int pn_net_open(struct net_device *dev)
+{
+       netif_wake_queue(dev);
+       return 0;
+}
+
+static int pn_net_close(struct net_device *dev)
+{
+       netif_stop_queue(dev);
+       return 0;
+}
+
+static void pn_tx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct f_phonet *fp = ep->driver_data;
+       struct net_device *dev = fp->dev;
+       struct sk_buff *skb = req->context;
+
+       switch (req->status) {
+       case 0:
+               dev->stats.tx_packets++;
+               dev->stats.tx_bytes += skb->len;
+               break;
+
+       case -ESHUTDOWN: /* disconnected */
+       case -ECONNRESET: /* disabled */
+               dev->stats.tx_aborted_errors++;
+       default:
+               dev->stats.tx_errors++;
+       }
+
+       dev_kfree_skb_any(skb);
+       netif_wake_queue(dev);
+}
+
+static int pn_net_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct phonet_port *port = netdev_priv(dev);
+       struct f_phonet *fp;
+       struct usb_request *req;
+       unsigned long flags;
+
+       if (skb->protocol != htons(ETH_P_PHONET))
+               goto out;
+
+       spin_lock_irqsave(&port->lock, flags);
+       fp = port->usb;
+       if (unlikely(!fp)) /* race with carrier loss */
+               goto out_unlock;
+
+       req = fp->in_req;
+       req->buf = skb->data;
+       req->length = skb->len;
+       req->complete = pn_tx_complete;
+       req->zero = 1;
+       req->context = skb;
+
+       if (unlikely(usb_ep_queue(fp->in_ep, req, GFP_ATOMIC)))
+               goto out_unlock;
+
+       netif_stop_queue(dev);
+       skb = NULL;
+
+out_unlock:
+       spin_unlock_irqrestore(&port->lock, flags);
+out:
+       if (unlikely(skb)) {
+               dev_kfree_skb(skb);
+               dev->stats.tx_dropped++;
+       }
+       return NETDEV_TX_OK;
+}
+
+static int pn_net_mtu(struct net_device *dev, int new_mtu)
+{
+       if ((new_mtu < PHONET_MIN_MTU) || (new_mtu > PHONET_MAX_MTU))
+               return -EINVAL;
+       dev->mtu = new_mtu;
+       return 0;
+}
+
+static const struct net_device_ops pn_netdev_ops = {
+       .ndo_open       = pn_net_open,
+       .ndo_stop       = pn_net_close,
+       .ndo_start_xmit = pn_net_xmit,
+       .ndo_change_mtu = pn_net_mtu,
+};
+
+static void pn_net_setup(struct net_device *dev)
+{
+       dev->features           = 0;
+       dev->type               = ARPHRD_PHONET;
+       dev->flags              = IFF_POINTOPOINT | IFF_NOARP;
+       dev->mtu                = PHONET_DEV_MTU;
+       dev->hard_header_len    = 1;
+       dev->dev_addr[0]        = PN_MEDIA_USB;
+       dev->addr_len           = 1;
+       dev->tx_queue_len       = 1;
+
+       dev->netdev_ops         = &pn_netdev_ops;
+       dev->destructor         = free_netdev;
+       dev->header_ops         = &phonet_header_ops;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Queue buffer for data from the host
+ */
+static int
+pn_rx_submit(struct f_phonet *fp, struct usb_request *req, gfp_t gfp_flags)
+{
+       struct page *page;
+       int err;
+
+       page = __skb_alloc_page(gfp_flags | __GFP_NOMEMALLOC, NULL);
+       if (!page)
+               return -ENOMEM;
+
+       req->buf = page_address(page);
+       req->length = PAGE_SIZE;
+       req->context = page;
+
+       err = usb_ep_queue(fp->out_ep, req, gfp_flags);
+       if (unlikely(err))
+               put_page(page);
+       return err;
+}
+
+static void pn_rx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct f_phonet *fp = ep->driver_data;
+       struct net_device *dev = fp->dev;
+       struct page *page = req->context;
+       struct sk_buff *skb;
+       unsigned long flags;
+       int status = req->status;
+
+       switch (status) {
+       case 0:
+               spin_lock_irqsave(&fp->rx.lock, flags);
+               skb = fp->rx.skb;
+               if (!skb)
+                       skb = fp->rx.skb = netdev_alloc_skb(dev, 12);
+               if (req->actual < req->length) /* Last fragment */
+                       fp->rx.skb = NULL;
+               spin_unlock_irqrestore(&fp->rx.lock, flags);
+
+               if (unlikely(!skb))
+                       break;
+
+               if (skb->len == 0) { /* First fragment */
+                       skb->protocol = htons(ETH_P_PHONET);
+                       skb_reset_mac_header(skb);
+                       /* Can't use pskb_pull() on page in IRQ */
+                       memcpy(skb_put(skb, 1), page_address(page), 1);
+               }
+
+               skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page,
+                               skb->len <= 1, req->actual, PAGE_SIZE);
+               page = NULL;
+
+               if (req->actual < req->length) { /* Last fragment */
+                       skb->dev = dev;
+                       dev->stats.rx_packets++;
+                       dev->stats.rx_bytes += skb->len;
+
+                       netif_rx(skb);
+               }
+               break;
+
+       /* Do not resubmit in these cases: */
+       case -ESHUTDOWN: /* disconnect */
+       case -ECONNABORTED: /* hw reset */
+       case -ECONNRESET: /* dequeued (unlink or netif down) */
+               req = NULL;
+               break;
+
+       /* Do resubmit in these cases: */
+       case -EOVERFLOW: /* request buffer overflow */
+               dev->stats.rx_over_errors++;
+       default:
+               dev->stats.rx_errors++;
+               break;
+       }
+
+       if (page)
+               put_page(page);
+       if (req)
+               pn_rx_submit(fp, req, GFP_ATOMIC | __GFP_COLD);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void __pn_reset(struct usb_function *f)
+{
+       struct f_phonet *fp = func_to_pn(f);
+       struct net_device *dev = fp->dev;
+       struct phonet_port *port = netdev_priv(dev);
+
+       netif_carrier_off(dev);
+       port->usb = NULL;
+
+       usb_ep_disable(fp->out_ep);
+       usb_ep_disable(fp->in_ep);
+       if (fp->rx.skb) {
+               dev_kfree_skb_irq(fp->rx.skb);
+               fp->rx.skb = NULL;
+       }
+}
+
+static int pn_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+       struct f_phonet *fp = func_to_pn(f);
+       struct usb_gadget *gadget = fp->function.config->cdev->gadget;
+
+       if (intf == pn_control_intf_desc.bInterfaceNumber)
+               /* control interface, no altsetting */
+               return (alt > 0) ? -EINVAL : 0;
+
+       if (intf == pn_data_intf_desc.bInterfaceNumber) {
+               struct net_device *dev = fp->dev;
+               struct phonet_port *port = netdev_priv(dev);
+
+               /* data intf (0: inactive, 1: active) */
+               if (alt > 1)
+                       return -EINVAL;
+
+               spin_lock(&port->lock);
+               __pn_reset(f);
+               if (alt == 1) {
+                       int i;
+
+                       if (config_ep_by_speed(gadget, f, fp->in_ep) ||
+                           config_ep_by_speed(gadget, f, fp->out_ep)) {
+                               fp->in_ep->desc = NULL;
+                               fp->out_ep->desc = NULL;
+                               spin_unlock(&port->lock);
+                               return -EINVAL;
+                       }
+                       usb_ep_enable(fp->out_ep);
+                       usb_ep_enable(fp->in_ep);
+
+                       port->usb = fp;
+                       fp->out_ep->driver_data = fp;
+                       fp->in_ep->driver_data = fp;
+
+                       netif_carrier_on(dev);
+                       for (i = 0; i < phonet_rxq_size; i++)
+                               pn_rx_submit(fp, fp->out_reqv[i], GFP_ATOMIC | __GFP_COLD);
+               }
+               spin_unlock(&port->lock);
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+static int pn_get_alt(struct usb_function *f, unsigned intf)
+{
+       struct f_phonet *fp = func_to_pn(f);
+
+       if (intf == pn_control_intf_desc.bInterfaceNumber)
+               return 0;
+
+       if (intf == pn_data_intf_desc.bInterfaceNumber) {
+               struct phonet_port *port = netdev_priv(fp->dev);
+               u8 alt;
+
+               spin_lock(&port->lock);
+               alt = port->usb != NULL;
+               spin_unlock(&port->lock);
+               return alt;
+       }
+
+       return -EINVAL;
+}
+
+static void pn_disconnect(struct usb_function *f)
+{
+       struct f_phonet *fp = func_to_pn(f);
+       struct phonet_port *port = netdev_priv(fp->dev);
+       unsigned long flags;
+
+       /* remain disabled until set_alt */
+       spin_lock_irqsave(&port->lock, flags);
+       __pn_reset(f);
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int pn_bind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct usb_composite_dev *cdev = c->cdev;
+       struct usb_gadget *gadget = cdev->gadget;
+       struct f_phonet *fp = func_to_pn(f);
+       struct usb_ep *ep;
+       int status, i;
+
+       struct f_phonet_opts *phonet_opts;
+
+       phonet_opts = container_of(f->fi, struct f_phonet_opts, func_inst);
+
+       /*
+        * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
+        * configurations are bound in sequence with list_for_each_entry,
+        * in each configuration its functions are bound in sequence
+        * with list_for_each_entry, so we assume no race condition
+        * with regard to phonet_opts->bound access
+        */
+       if (!phonet_opts->bound) {
+               gphonet_set_gadget(phonet_opts->net, gadget);
+               status = gphonet_register_netdev(phonet_opts->net);
+               if (status)
+                       return status;
+               phonet_opts->bound = true;
+       }
+
+       /* Reserve interface IDs */
+       status = usb_interface_id(c, f);
+       if (status < 0)
+               goto err;
+       pn_control_intf_desc.bInterfaceNumber = status;
+       pn_union_desc.bMasterInterface0 = status;
+
+       status = usb_interface_id(c, f);
+       if (status < 0)
+               goto err;
+       pn_data_nop_intf_desc.bInterfaceNumber = status;
+       pn_data_intf_desc.bInterfaceNumber = status;
+       pn_union_desc.bSlaveInterface0 = status;
+
+       /* Reserve endpoints */
+       status = -ENODEV;
+       ep = usb_ep_autoconfig(gadget, &pn_fs_sink_desc);
+       if (!ep)
+               goto err;
+       fp->out_ep = ep;
+       ep->driver_data = fp; /* Claim */
+
+       ep = usb_ep_autoconfig(gadget, &pn_fs_source_desc);
+       if (!ep)
+               goto err;
+       fp->in_ep = ep;
+       ep->driver_data = fp; /* Claim */
+
+       pn_hs_sink_desc.bEndpointAddress = pn_fs_sink_desc.bEndpointAddress;
+       pn_hs_source_desc.bEndpointAddress = pn_fs_source_desc.bEndpointAddress;
+
+       /* Do not try to bind Phonet twice... */
+       status = usb_assign_descriptors(f, fs_pn_function, hs_pn_function,
+                       NULL);
+       if (status)
+               goto err;
+
+       /* Incoming USB requests */
+       status = -ENOMEM;
+       for (i = 0; i < phonet_rxq_size; i++) {
+               struct usb_request *req;
+
+               req = usb_ep_alloc_request(fp->out_ep, GFP_KERNEL);
+               if (!req)
+                       goto err_req;
+
+               req->complete = pn_rx_complete;
+               fp->out_reqv[i] = req;
+       }
+
+       /* Outgoing USB requests */
+       fp->in_req = usb_ep_alloc_request(fp->in_ep, GFP_KERNEL);
+       if (!fp->in_req)
+               goto err_req;
+
+       INFO(cdev, "USB CDC Phonet function\n");
+       INFO(cdev, "using %s, OUT %s, IN %s\n", cdev->gadget->name,
+               fp->out_ep->name, fp->in_ep->name);
+       return 0;
+
+err_req:
+       for (i = 0; i < phonet_rxq_size && fp->out_reqv[i]; i++)
+               usb_ep_free_request(fp->out_ep, fp->out_reqv[i]);
+err:
+       usb_free_all_descriptors(f);
+       if (fp->out_ep)
+               fp->out_ep->driver_data = NULL;
+       if (fp->in_ep)
+               fp->in_ep->driver_data = NULL;
+       ERROR(cdev, "USB CDC Phonet: cannot autoconfigure\n");
+       return status;
+}
+
+static inline struct f_phonet_opts *to_f_phonet_opts(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct f_phonet_opts,
+                       func_inst.group);
+}
+
+CONFIGFS_ATTR_STRUCT(f_phonet_opts);
+static ssize_t f_phonet_attr_show(struct config_item *item,
+                               struct configfs_attribute *attr,
+                               char *page)
+{
+       struct f_phonet_opts *opts = to_f_phonet_opts(item);
+       struct f_phonet_opts_attribute *f_phonet_opts_attr =
+               container_of(attr, struct f_phonet_opts_attribute, attr);
+       ssize_t ret = 0;
+
+       if (f_phonet_opts_attr->show)
+               ret = f_phonet_opts_attr->show(opts, page);
+       return ret;
+}
+
+static void phonet_attr_release(struct config_item *item)
+{
+       struct f_phonet_opts *opts = to_f_phonet_opts(item);
+
+       usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations phonet_item_ops = {
+       .release                = phonet_attr_release,
+       .show_attribute         = f_phonet_attr_show,
+};
+
+static ssize_t f_phonet_ifname_show(struct f_phonet_opts *opts, char *page)
+{
+       return gether_get_ifname(opts->net, page, PAGE_SIZE);
+}
+
+static struct f_phonet_opts_attribute f_phonet_ifname =
+       __CONFIGFS_ATTR_RO(ifname, f_phonet_ifname_show);
+
+static struct configfs_attribute *phonet_attrs[] = {
+       &f_phonet_ifname.attr,
+       NULL,
+};
+
+static struct config_item_type phonet_func_type = {
+       .ct_item_ops    = &phonet_item_ops,
+       .ct_attrs       = phonet_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+static void phonet_free_inst(struct usb_function_instance *f)
+{
+       struct f_phonet_opts *opts;
+
+       opts = container_of(f, struct f_phonet_opts, func_inst);
+       if (opts->bound)
+               gphonet_cleanup(opts->net);
+       else
+               free_netdev(opts->net);
+       kfree(opts);
+}
+
+static struct usb_function_instance *phonet_alloc_inst(void)
+{
+       struct f_phonet_opts *opts;
+
+       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+       if (!opts)
+               return ERR_PTR(-ENOMEM);
+
+       opts->func_inst.free_func_inst = phonet_free_inst;
+       opts->net = gphonet_setup_default();
+       if (IS_ERR(opts->net)) {
+               struct net_device *net = opts->net;
+               kfree(opts);
+               return ERR_CAST(net);
+       }
+
+       config_group_init_type_name(&opts->func_inst.group, "",
+                       &phonet_func_type);
+
+       return &opts->func_inst;
+}
+
+static void phonet_free(struct usb_function *f)
+{
+       struct f_phonet *phonet;
+
+       phonet = func_to_pn(f);
+       kfree(phonet);
+}
+
+static void pn_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct f_phonet *fp = func_to_pn(f);
+       int i;
+
+       /* We are already disconnected */
+       if (fp->in_req)
+               usb_ep_free_request(fp->in_ep, fp->in_req);
+       for (i = 0; i < phonet_rxq_size; i++)
+               if (fp->out_reqv[i])
+                       usb_ep_free_request(fp->out_ep, fp->out_reqv[i]);
+
+       usb_free_all_descriptors(f);
+}
+
+static struct usb_function *phonet_alloc(struct usb_function_instance *fi)
+{
+       struct f_phonet *fp;
+       struct f_phonet_opts *opts;
+       int size;
+
+       size = sizeof(*fp) + (phonet_rxq_size * sizeof(struct usb_request *));
+       fp = kzalloc(size, GFP_KERNEL);
+       if (!fp)
+               return ERR_PTR(-ENOMEM);
+
+       opts = container_of(fi, struct f_phonet_opts, func_inst);
+
+       fp->dev = opts->net;
+       fp->function.name = "phonet";
+       fp->function.bind = pn_bind;
+       fp->function.unbind = pn_unbind;
+       fp->function.set_alt = pn_set_alt;
+       fp->function.get_alt = pn_get_alt;
+       fp->function.disable = pn_disconnect;
+       fp->function.free_func = phonet_free;
+       spin_lock_init(&fp->rx.lock);
+
+       return &fp->function;
+}
+
+struct net_device *gphonet_setup_default(void)
+{
+       struct net_device *dev;
+       struct phonet_port *port;
+
+       /* Create net device */
+       dev = alloc_netdev(sizeof(*port), "upnlink%d", pn_net_setup);
+       if (!dev)
+               return ERR_PTR(-ENOMEM);
+
+       port = netdev_priv(dev);
+       spin_lock_init(&port->lock);
+       netif_carrier_off(dev);
+
+       return dev;
+}
+
+void gphonet_set_gadget(struct net_device *net, struct usb_gadget *g)
+{
+       SET_NETDEV_DEV(net, &g->dev);
+}
+
+int gphonet_register_netdev(struct net_device *net)
+{
+       int status;
+
+       status = register_netdev(net);
+       if (status)
+               free_netdev(net);
+
+       return status;
+}
+
+void gphonet_cleanup(struct net_device *dev)
+{
+       unregister_netdev(dev);
+}
+
+DECLARE_USB_FUNCTION_INIT(phonet, phonet_alloc_inst, phonet_alloc);
+MODULE_AUTHOR("Rémi Denis-Courmont");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c
new file mode 100644 (file)
index 0000000..eed3ad8
--- /dev/null
@@ -0,0 +1,1029 @@
+/*
+ * f_rndis.c -- RNDIS link function driver
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
+ * Copyright (C) 2008 Nokia Corporation
+ * Copyright (C) 2009 Samsung Electronics
+ *                    Author: Michal Nazarewicz (mina86@mina86.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/etherdevice.h>
+
+#include <linux/atomic.h>
+
+#include "u_ether.h"
+#include "u_ether_configfs.h"
+#include "u_rndis.h"
+#include "rndis.h"
+#include "configfs.h"
+
+/*
+ * This function is an RNDIS Ethernet port -- a Microsoft protocol that's
+ * been promoted instead of the standard CDC Ethernet.  The published RNDIS
+ * spec is ambiguous, incomplete, and needlessly complex.  Variants such as
+ * ActiveSync have even worse status in terms of specification.
+ *
+ * In short:  it's a protocol controlled by (and for) Microsoft, not for an
+ * Open ecosystem or markets.  Linux supports it *only* because Microsoft
+ * doesn't support the CDC Ethernet standard.
+ *
+ * The RNDIS data transfer model is complex, with multiple Ethernet packets
+ * per USB message, and out of band data.  The control model is built around
+ * what's essentially an "RNDIS RPC" protocol.  It's all wrapped in a CDC ACM
+ * (modem, not Ethernet) veneer, with those ACM descriptors being entirely
+ * useless (they're ignored).  RNDIS expects to be the only function in its
+ * configuration, so it's no real help if you need composite devices; and
+ * it expects to be the first configuration too.
+ *
+ * There is a single technical advantage of RNDIS over CDC Ethernet, if you
+ * discount the fluff that its RPC can be made to deliver: it doesn't need
+ * a NOP altsetting for the data interface.  That lets it work on some of the
+ * "so smart it's stupid" hardware which takes over configuration changes
+ * from the software, and adds restrictions like "no altsettings".
+ *
+ * Unfortunately MSFT's RNDIS drivers are buggy.  They hang or oops, and
+ * have all sorts of contrary-to-specification oddities that can prevent
+ * them from working sanely.  Since bugfixes (or accurate specs, letting
+ * Linux work around those bugs) are unlikely to ever come from MSFT, you
+ * may want to avoid using RNDIS on purely operational grounds.
+ *
+ * Omissions from the RNDIS 1.0 specification include:
+ *
+ *   - Power management ... references data that's scattered around lots
+ *     of other documentation, which is incorrect/incomplete there too.
+ *
+ *   - There are various undocumented protocol requirements, like the need
+ *     to send garbage in some control-OUT messages.
+ *
+ *   - MS-Windows drivers sometimes emit undocumented requests.
+ */
+
+struct f_rndis {
+       struct gether                   port;
+       u8                              ctrl_id, data_id;
+       u8                              ethaddr[ETH_ALEN];
+       u32                             vendorID;
+       const char                      *manufacturer;
+       int                             config;
+
+       struct usb_ep                   *notify;
+       struct usb_request              *notify_req;
+       atomic_t                        notify_count;
+};
+
+static inline struct f_rndis *func_to_rndis(struct usb_function *f)
+{
+       return container_of(f, struct f_rndis, port.func);
+}
+
+/* peak (theoretical) bulk transfer rate in bits-per-second */
+static unsigned int bitrate(struct usb_gadget *g)
+{
+       if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
+               return 13 * 1024 * 8 * 1000 * 8;
+       else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+               return 13 * 512 * 8 * 1000 * 8;
+       else
+               return 19 * 64 * 1 * 1000 * 8;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ */
+
+#define RNDIS_STATUS_INTERVAL_MS       32
+#define STATUS_BYTECOUNT               8       /* 8 bytes data */
+
+
+/* interface descriptor: */
+
+static struct usb_interface_descriptor rndis_control_intf = {
+       .bLength =              sizeof rndis_control_intf,
+       .bDescriptorType =      USB_DT_INTERFACE,
+
+       /* .bInterfaceNumber = DYNAMIC */
+       /* status endpoint is optional; this could be patched later */
+       .bNumEndpoints =        1,
+       .bInterfaceClass =      USB_CLASS_COMM,
+       .bInterfaceSubClass =   USB_CDC_SUBCLASS_ACM,
+       .bInterfaceProtocol =   USB_CDC_ACM_PROTO_VENDOR,
+       /* .iInterface = DYNAMIC */
+};
+
+static struct usb_cdc_header_desc header_desc = {
+       .bLength =              sizeof header_desc,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubType =   USB_CDC_HEADER_TYPE,
+
+       .bcdCDC =               cpu_to_le16(0x0110),
+};
+
+static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor = {
+       .bLength =              sizeof call_mgmt_descriptor,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubType =   USB_CDC_CALL_MANAGEMENT_TYPE,
+
+       .bmCapabilities =       0x00,
+       .bDataInterface =       0x01,
+};
+
+static struct usb_cdc_acm_descriptor rndis_acm_descriptor = {
+       .bLength =              sizeof rndis_acm_descriptor,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubType =   USB_CDC_ACM_TYPE,
+
+       .bmCapabilities =       0x00,
+};
+
+static struct usb_cdc_union_desc rndis_union_desc = {
+       .bLength =              sizeof(rndis_union_desc),
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubType =   USB_CDC_UNION_TYPE,
+       /* .bMasterInterface0 = DYNAMIC */
+       /* .bSlaveInterface0 =  DYNAMIC */
+};
+
+/* the data interface has two bulk endpoints */
+
+static struct usb_interface_descriptor rndis_data_intf = {
+       .bLength =              sizeof rndis_data_intf,
+       .bDescriptorType =      USB_DT_INTERFACE,
+
+       /* .bInterfaceNumber = DYNAMIC */
+       .bNumEndpoints =        2,
+       .bInterfaceClass =      USB_CLASS_CDC_DATA,
+       .bInterfaceSubClass =   0,
+       .bInterfaceProtocol =   0,
+       /* .iInterface = DYNAMIC */
+};
+
+
+static struct usb_interface_assoc_descriptor
+rndis_iad_descriptor = {
+       .bLength =              sizeof rndis_iad_descriptor,
+       .bDescriptorType =      USB_DT_INTERFACE_ASSOCIATION,
+
+       .bFirstInterface =      0, /* XXX, hardcoded */
+       .bInterfaceCount =      2,      // control + data
+       .bFunctionClass =       USB_CLASS_COMM,
+       .bFunctionSubClass =    USB_CDC_SUBCLASS_ETHERNET,
+       .bFunctionProtocol =    USB_CDC_PROTO_NONE,
+       /* .iFunction = DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor fs_notify_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_INT,
+       .wMaxPacketSize =       cpu_to_le16(STATUS_BYTECOUNT),
+       .bInterval =            RNDIS_STATUS_INTERVAL_MS,
+};
+
+static struct usb_endpoint_descriptor fs_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor fs_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *eth_fs_function[] = {
+       (struct usb_descriptor_header *) &rndis_iad_descriptor,
+
+       /* control interface matches ACM, not Ethernet */
+       (struct usb_descriptor_header *) &rndis_control_intf,
+       (struct usb_descriptor_header *) &header_desc,
+       (struct usb_descriptor_header *) &call_mgmt_descriptor,
+       (struct usb_descriptor_header *) &rndis_acm_descriptor,
+       (struct usb_descriptor_header *) &rndis_union_desc,
+       (struct usb_descriptor_header *) &fs_notify_desc,
+
+       /* data interface has no altsetting */
+       (struct usb_descriptor_header *) &rndis_data_intf,
+       (struct usb_descriptor_header *) &fs_in_desc,
+       (struct usb_descriptor_header *) &fs_out_desc,
+       NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor hs_notify_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_INT,
+       .wMaxPacketSize =       cpu_to_le16(STATUS_BYTECOUNT),
+       .bInterval =            USB_MS_TO_HS_INTERVAL(RNDIS_STATUS_INTERVAL_MS)
+};
+
+static struct usb_endpoint_descriptor hs_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor hs_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *eth_hs_function[] = {
+       (struct usb_descriptor_header *) &rndis_iad_descriptor,
+
+       /* control interface matches ACM, not Ethernet */
+       (struct usb_descriptor_header *) &rndis_control_intf,
+       (struct usb_descriptor_header *) &header_desc,
+       (struct usb_descriptor_header *) &call_mgmt_descriptor,
+       (struct usb_descriptor_header *) &rndis_acm_descriptor,
+       (struct usb_descriptor_header *) &rndis_union_desc,
+       (struct usb_descriptor_header *) &hs_notify_desc,
+
+       /* data interface has no altsetting */
+       (struct usb_descriptor_header *) &rndis_data_intf,
+       (struct usb_descriptor_header *) &hs_in_desc,
+       (struct usb_descriptor_header *) &hs_out_desc,
+       NULL,
+};
+
+/* super speed support: */
+
+static struct usb_endpoint_descriptor ss_notify_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_INT,
+       .wMaxPacketSize =       cpu_to_le16(STATUS_BYTECOUNT),
+       .bInterval =            USB_MS_TO_HS_INTERVAL(RNDIS_STATUS_INTERVAL_MS)
+};
+
+static struct usb_ss_ep_comp_descriptor ss_intr_comp_desc = {
+       .bLength =              sizeof ss_intr_comp_desc,
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+
+       /* the following 3 values can be tweaked if necessary */
+       /* .bMaxBurst =         0, */
+       /* .bmAttributes =      0, */
+       .wBytesPerInterval =    cpu_to_le16(STATUS_BYTECOUNT),
+};
+
+static struct usb_endpoint_descriptor ss_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor ss_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ss_bulk_comp_desc = {
+       .bLength =              sizeof ss_bulk_comp_desc,
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+
+       /* the following 2 values can be tweaked if necessary */
+       /* .bMaxBurst =         0, */
+       /* .bmAttributes =      0, */
+};
+
+static struct usb_descriptor_header *eth_ss_function[] = {
+       (struct usb_descriptor_header *) &rndis_iad_descriptor,
+
+       /* control interface matches ACM, not Ethernet */
+       (struct usb_descriptor_header *) &rndis_control_intf,
+       (struct usb_descriptor_header *) &header_desc,
+       (struct usb_descriptor_header *) &call_mgmt_descriptor,
+       (struct usb_descriptor_header *) &rndis_acm_descriptor,
+       (struct usb_descriptor_header *) &rndis_union_desc,
+       (struct usb_descriptor_header *) &ss_notify_desc,
+       (struct usb_descriptor_header *) &ss_intr_comp_desc,
+
+       /* data interface has no altsetting */
+       (struct usb_descriptor_header *) &rndis_data_intf,
+       (struct usb_descriptor_header *) &ss_in_desc,
+       (struct usb_descriptor_header *) &ss_bulk_comp_desc,
+       (struct usb_descriptor_header *) &ss_out_desc,
+       (struct usb_descriptor_header *) &ss_bulk_comp_desc,
+       NULL,
+};
+
+/* string descriptors: */
+
+static struct usb_string rndis_string_defs[] = {
+       [0].s = "RNDIS Communications Control",
+       [1].s = "RNDIS Ethernet Data",
+       [2].s = "RNDIS",
+       {  } /* end of list */
+};
+
+static struct usb_gadget_strings rndis_string_table = {
+       .language =             0x0409, /* en-us */
+       .strings =              rndis_string_defs,
+};
+
+static struct usb_gadget_strings *rndis_strings[] = {
+       &rndis_string_table,
+       NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static struct sk_buff *rndis_add_header(struct gether *port,
+                                       struct sk_buff *skb)
+{
+       struct sk_buff *skb2;
+
+       skb2 = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type));
+       if (skb2)
+               rndis_add_hdr(skb2);
+
+       dev_kfree_skb(skb);
+       return skb2;
+}
+
+static void rndis_response_available(void *_rndis)
+{
+       struct f_rndis                  *rndis = _rndis;
+       struct usb_request              *req = rndis->notify_req;
+       struct usb_composite_dev        *cdev = rndis->port.func.config->cdev;
+       __le32                          *data = req->buf;
+       int                             status;
+
+       if (atomic_inc_return(&rndis->notify_count) != 1)
+               return;
+
+       /* Send RNDIS RESPONSE_AVAILABLE notification; a
+        * USB_CDC_NOTIFY_RESPONSE_AVAILABLE "should" work too
+        *
+        * This is the only notification defined by RNDIS.
+        */
+       data[0] = cpu_to_le32(1);
+       data[1] = cpu_to_le32(0);
+
+       status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC);
+       if (status) {
+               atomic_dec(&rndis->notify_count);
+               DBG(cdev, "notify/0 --> %d\n", status);
+       }
+}
+
+static void rndis_response_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct f_rndis                  *rndis = req->context;
+       struct usb_composite_dev        *cdev = rndis->port.func.config->cdev;
+       int                             status = req->status;
+
+       /* after TX:
+        *  - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control)
+        *  - RNDIS_RESPONSE_AVAILABLE (status/irq)
+        */
+       switch (status) {
+       case -ECONNRESET:
+       case -ESHUTDOWN:
+               /* connection gone */
+               atomic_set(&rndis->notify_count, 0);
+               break;
+       default:
+               DBG(cdev, "RNDIS %s response error %d, %d/%d\n",
+                       ep->name, status,
+                       req->actual, req->length);
+               /* FALLTHROUGH */
+       case 0:
+               if (ep != rndis->notify)
+                       break;
+
+               /* handle multiple pending RNDIS_RESPONSE_AVAILABLE
+                * notifications by resending until we're done
+                */
+               if (atomic_dec_and_test(&rndis->notify_count))
+                       break;
+               status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC);
+               if (status) {
+                       atomic_dec(&rndis->notify_count);
+                       DBG(cdev, "notify/1 --> %d\n", status);
+               }
+               break;
+       }
+}
+
+static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct f_rndis                  *rndis = req->context;
+       int                             status;
+
+       /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
+//     spin_lock(&dev->lock);
+       status = rndis_msg_parser(rndis->config, (u8 *) req->buf);
+       if (status < 0)
+               pr_err("RNDIS command error %d, %d/%d\n",
+                       status, req->actual, req->length);
+//     spin_unlock(&dev->lock);
+}
+
+static int
+rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+       struct f_rndis          *rndis = func_to_rndis(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+       struct usb_request      *req = cdev->req;
+       int                     value = -EOPNOTSUPP;
+       u16                     w_index = le16_to_cpu(ctrl->wIndex);
+       u16                     w_value = le16_to_cpu(ctrl->wValue);
+       u16                     w_length = le16_to_cpu(ctrl->wLength);
+
+       /* composite driver infrastructure handles everything except
+        * CDC class messages; interface activation uses set_alt().
+        */
+       switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+
+       /* RNDIS uses the CDC command encapsulation mechanism to implement
+        * an RPC scheme, with much getting/setting of attributes by OID.
+        */
+       case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+                       | USB_CDC_SEND_ENCAPSULATED_COMMAND:
+               if (w_value || w_index != rndis->ctrl_id)
+                       goto invalid;
+               /* read the request; process it later */
+               value = w_length;
+               req->complete = rndis_command_complete;
+               req->context = rndis;
+               /* later, rndis_response_available() sends a notification */
+               break;
+
+       case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+                       | USB_CDC_GET_ENCAPSULATED_RESPONSE:
+               if (w_value || w_index != rndis->ctrl_id)
+                       goto invalid;
+               else {
+                       u8 *buf;
+                       u32 n;
+
+                       /* return the result */
+                       buf = rndis_get_next_response(rndis->config, &n);
+                       if (buf) {
+                               memcpy(req->buf, buf, n);
+                               req->complete = rndis_response_complete;
+                               req->context = rndis;
+                               rndis_free_response(rndis->config, buf);
+                               value = n;
+                       }
+                       /* else stalls ... spec says to avoid that */
+               }
+               break;
+
+       default:
+invalid:
+               VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+                       ctrl->bRequestType, ctrl->bRequest,
+                       w_value, w_index, w_length);
+       }
+
+       /* respond with data transfer or status phase? */
+       if (value >= 0) {
+               DBG(cdev, "rndis req%02x.%02x v%04x i%04x l%d\n",
+                       ctrl->bRequestType, ctrl->bRequest,
+                       w_value, w_index, w_length);
+               req->zero = (value < w_length);
+               req->length = value;
+               value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+               if (value < 0)
+                       ERROR(cdev, "rndis response on err %d\n", value);
+       }
+
+       /* device either stalls (value < 0) or reports success */
+       return value;
+}
+
+
+static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+       struct f_rndis          *rndis = func_to_rndis(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+
+       /* we know alt == 0 */
+
+       if (intf == rndis->ctrl_id) {
+               if (rndis->notify->driver_data) {
+                       VDBG(cdev, "reset rndis control %d\n", intf);
+                       usb_ep_disable(rndis->notify);
+               }
+               if (!rndis->notify->desc) {
+                       VDBG(cdev, "init rndis ctrl %d\n", intf);
+                       if (config_ep_by_speed(cdev->gadget, f, rndis->notify))
+                               goto fail;
+               }
+               usb_ep_enable(rndis->notify);
+               rndis->notify->driver_data = rndis;
+
+       } else if (intf == rndis->data_id) {
+               struct net_device       *net;
+
+               if (rndis->port.in_ep->driver_data) {
+                       DBG(cdev, "reset rndis\n");
+                       gether_disconnect(&rndis->port);
+               }
+
+               if (!rndis->port.in_ep->desc || !rndis->port.out_ep->desc) {
+                       DBG(cdev, "init rndis\n");
+                       if (config_ep_by_speed(cdev->gadget, f,
+                                              rndis->port.in_ep) ||
+                           config_ep_by_speed(cdev->gadget, f,
+                                              rndis->port.out_ep)) {
+                               rndis->port.in_ep->desc = NULL;
+                               rndis->port.out_ep->desc = NULL;
+                               goto fail;
+                       }
+               }
+
+               /* Avoid ZLPs; they can be troublesome. */
+               rndis->port.is_zlp_ok = false;
+
+               /* RNDIS should be in the "RNDIS uninitialized" state,
+                * either never activated or after rndis_uninit().
+                *
+                * We don't want data to flow here until a nonzero packet
+                * filter is set, at which point it enters "RNDIS data
+                * initialized" state ... but we do want the endpoints
+                * to be activated.  It's a strange little state.
+                *
+                * REVISIT the RNDIS gadget code has done this wrong for a
+                * very long time.  We need another call to the link layer
+                * code -- gether_updown(...bool) maybe -- to do it right.
+                */
+               rndis->port.cdc_filter = 0;
+
+               DBG(cdev, "RNDIS RX/TX early activation ... \n");
+               net = gether_connect(&rndis->port);
+               if (IS_ERR(net))
+                       return PTR_ERR(net);
+
+               rndis_set_param_dev(rndis->config, net,
+                               &rndis->port.cdc_filter);
+       } else
+               goto fail;
+
+       return 0;
+fail:
+       return -EINVAL;
+}
+
+static void rndis_disable(struct usb_function *f)
+{
+       struct f_rndis          *rndis = func_to_rndis(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+
+       if (!rndis->notify->driver_data)
+               return;
+
+       DBG(cdev, "rndis deactivated\n");
+
+       rndis_uninit(rndis->config);
+       gether_disconnect(&rndis->port);
+
+       usb_ep_disable(rndis->notify);
+       rndis->notify->driver_data = NULL;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * This isn't quite the same mechanism as CDC Ethernet, since the
+ * notification scheme passes less data, but the same set of link
+ * states must be tested.  A key difference is that altsettings are
+ * not used to tell whether the link should send packets or not.
+ */
+
+static void rndis_open(struct gether *geth)
+{
+       struct f_rndis          *rndis = func_to_rndis(&geth->func);
+       struct usb_composite_dev *cdev = geth->func.config->cdev;
+
+       DBG(cdev, "%s\n", __func__);
+
+       rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3,
+                               bitrate(cdev->gadget) / 100);
+       rndis_signal_connect(rndis->config);
+}
+
+static void rndis_close(struct gether *geth)
+{
+       struct f_rndis          *rndis = func_to_rndis(&geth->func);
+
+       DBG(geth->func.config->cdev, "%s\n", __func__);
+
+       rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0);
+       rndis_signal_disconnect(rndis->config);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Some controllers can't support RNDIS ... */
+static inline bool can_support_rndis(struct usb_configuration *c)
+{
+       /* everything else is *presumably* fine */
+       return true;
+}
+
+/* ethernet function driver setup/binding */
+
+static int
+rndis_bind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct usb_composite_dev *cdev = c->cdev;
+       struct f_rndis          *rndis = func_to_rndis(f);
+       struct usb_string       *us;
+       int                     status;
+       struct usb_ep           *ep;
+
+       struct f_rndis_opts *rndis_opts;
+
+       if (!can_support_rndis(c))
+               return -EINVAL;
+
+       rndis_opts = container_of(f->fi, struct f_rndis_opts, func_inst);
+
+       if (cdev->use_os_string) {
+               f->os_desc_table = kzalloc(sizeof(*f->os_desc_table),
+                                          GFP_KERNEL);
+               if (!f->os_desc_table)
+                       return PTR_ERR(f->os_desc_table);
+               f->os_desc_n = 1;
+               f->os_desc_table[0].os_desc = &rndis_opts->rndis_os_desc;
+       }
+
+       /*
+        * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
+        * configurations are bound in sequence with list_for_each_entry,
+        * in each configuration its functions are bound in sequence
+        * with list_for_each_entry, so we assume no race condition
+        * with regard to rndis_opts->bound access
+        */
+       if (!rndis_opts->bound) {
+               gether_set_gadget(rndis_opts->net, cdev->gadget);
+               status = gether_register_netdev(rndis_opts->net);
+               if (status)
+                       goto fail;
+               rndis_opts->bound = true;
+       }
+
+       us = usb_gstrings_attach(cdev, rndis_strings,
+                                ARRAY_SIZE(rndis_string_defs));
+       if (IS_ERR(us)) {
+               status = PTR_ERR(us);
+               goto fail;
+       }
+       rndis_control_intf.iInterface = us[0].id;
+       rndis_data_intf.iInterface = us[1].id;
+       rndis_iad_descriptor.iFunction = us[2].id;
+
+       /* allocate instance-specific interface IDs */
+       status = usb_interface_id(c, f);
+       if (status < 0)
+               goto fail;
+       rndis->ctrl_id = status;
+       rndis_iad_descriptor.bFirstInterface = status;
+
+       rndis_control_intf.bInterfaceNumber = status;
+       rndis_union_desc.bMasterInterface0 = status;
+
+       status = usb_interface_id(c, f);
+       if (status < 0)
+               goto fail;
+       rndis->data_id = status;
+
+       rndis_data_intf.bInterfaceNumber = status;
+       rndis_union_desc.bSlaveInterface0 = status;
+
+       status = -ENODEV;
+
+       /* allocate instance-specific endpoints */
+       ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc);
+       if (!ep)
+               goto fail;
+       rndis->port.in_ep = ep;
+       ep->driver_data = cdev; /* claim */
+
+       ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc);
+       if (!ep)
+               goto fail;
+       rndis->port.out_ep = ep;
+       ep->driver_data = cdev; /* claim */
+
+       /* NOTE:  a status/notification endpoint is, strictly speaking,
+        * optional.  We don't treat it that way though!  It's simpler,
+        * and some newer profiles don't treat it as optional.
+        */
+       ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc);
+       if (!ep)
+               goto fail;
+       rndis->notify = ep;
+       ep->driver_data = cdev; /* claim */
+
+       status = -ENOMEM;
+
+       /* allocate notification request and buffer */
+       rndis->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
+       if (!rndis->notify_req)
+               goto fail;
+       rndis->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL);
+       if (!rndis->notify_req->buf)
+               goto fail;
+       rndis->notify_req->length = STATUS_BYTECOUNT;
+       rndis->notify_req->context = rndis;
+       rndis->notify_req->complete = rndis_response_complete;
+
+       /* support all relevant hardware speeds... we expect that when
+        * hardware is dual speed, all bulk-capable endpoints work at
+        * both speeds
+        */
+       hs_in_desc.bEndpointAddress = fs_in_desc.bEndpointAddress;
+       hs_out_desc.bEndpointAddress = fs_out_desc.bEndpointAddress;
+       hs_notify_desc.bEndpointAddress = fs_notify_desc.bEndpointAddress;
+
+       ss_in_desc.bEndpointAddress = fs_in_desc.bEndpointAddress;
+       ss_out_desc.bEndpointAddress = fs_out_desc.bEndpointAddress;
+       ss_notify_desc.bEndpointAddress = fs_notify_desc.bEndpointAddress;
+
+       status = usb_assign_descriptors(f, eth_fs_function, eth_hs_function,
+                       eth_ss_function);
+       if (status)
+               goto fail;
+
+       rndis->port.open = rndis_open;
+       rndis->port.close = rndis_close;
+
+       rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0);
+       rndis_set_host_mac(rndis->config, rndis->ethaddr);
+
+       if (rndis->manufacturer && rndis->vendorID &&
+                       rndis_set_param_vendor(rndis->config, rndis->vendorID,
+                                              rndis->manufacturer))
+               goto fail;
+
+       /* NOTE:  all that is done without knowing or caring about
+        * the network link ... which is unavailable to this code
+        * until we're activated via set_alt().
+        */
+
+       DBG(cdev, "RNDIS: %s speed IN/%s OUT/%s NOTIFY/%s\n",
+                       gadget_is_superspeed(c->cdev->gadget) ? "super" :
+                       gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+                       rndis->port.in_ep->name, rndis->port.out_ep->name,
+                       rndis->notify->name);
+       return 0;
+
+fail:
+       kfree(f->os_desc_table);
+       f->os_desc_n = 0;
+       usb_free_all_descriptors(f);
+
+       if (rndis->notify_req) {
+               kfree(rndis->notify_req->buf);
+               usb_ep_free_request(rndis->notify, rndis->notify_req);
+       }
+
+       /* we might as well release our claims on endpoints */
+       if (rndis->notify)
+               rndis->notify->driver_data = NULL;
+       if (rndis->port.out_ep)
+               rndis->port.out_ep->driver_data = NULL;
+       if (rndis->port.in_ep)
+               rndis->port.in_ep->driver_data = NULL;
+
+       ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
+
+       return status;
+}
+
+void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net)
+{
+       struct f_rndis_opts *opts;
+
+       opts = container_of(f, struct f_rndis_opts, func_inst);
+       if (opts->bound)
+               gether_cleanup(netdev_priv(opts->net));
+       else
+               free_netdev(opts->net);
+       opts->borrowed_net = opts->bound = true;
+       opts->net = net;
+}
+EXPORT_SYMBOL_GPL(rndis_borrow_net);
+
+static inline struct f_rndis_opts *to_f_rndis_opts(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct f_rndis_opts,
+                           func_inst.group);
+}
+
+/* f_rndis_item_ops */
+USB_ETHERNET_CONFIGFS_ITEM(rndis);
+
+/* f_rndis_opts_dev_addr */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(rndis);
+
+/* f_rndis_opts_host_addr */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(rndis);
+
+/* f_rndis_opts_qmult */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(rndis);
+
+/* f_rndis_opts_ifname */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(rndis);
+
+static struct configfs_attribute *rndis_attrs[] = {
+       &f_rndis_opts_dev_addr.attr,
+       &f_rndis_opts_host_addr.attr,
+       &f_rndis_opts_qmult.attr,
+       &f_rndis_opts_ifname.attr,
+       NULL,
+};
+
+static struct config_item_type rndis_func_type = {
+       .ct_item_ops    = &rndis_item_ops,
+       .ct_attrs       = rndis_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+static void rndis_free_inst(struct usb_function_instance *f)
+{
+       struct f_rndis_opts *opts;
+
+       opts = container_of(f, struct f_rndis_opts, func_inst);
+       if (!opts->borrowed_net) {
+               if (opts->bound)
+                       gether_cleanup(netdev_priv(opts->net));
+               else
+                       free_netdev(opts->net);
+       }
+
+       kfree(opts->rndis_os_desc.group.default_groups); /* single VLA chunk */
+       kfree(opts);
+}
+
+static struct usb_function_instance *rndis_alloc_inst(void)
+{
+       struct f_rndis_opts *opts;
+       struct usb_os_desc *descs[1];
+
+       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+       if (!opts)
+               return ERR_PTR(-ENOMEM);
+       opts->rndis_os_desc.ext_compat_id = opts->rndis_ext_compat_id;
+
+       mutex_init(&opts->lock);
+       opts->func_inst.free_func_inst = rndis_free_inst;
+       opts->net = gether_setup_default();
+       if (IS_ERR(opts->net)) {
+               struct net_device *net = opts->net;
+               kfree(opts);
+               return ERR_CAST(net);
+       }
+       INIT_LIST_HEAD(&opts->rndis_os_desc.ext_prop);
+
+       descs[0] = &opts->rndis_os_desc;
+       usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs,
+                                      THIS_MODULE);
+       config_group_init_type_name(&opts->func_inst.group, "",
+                                   &rndis_func_type);
+
+       return &opts->func_inst;
+}
+
+static void rndis_free(struct usb_function *f)
+{
+       struct f_rndis *rndis;
+       struct f_rndis_opts *opts;
+
+       rndis = func_to_rndis(f);
+       rndis_deregister(rndis->config);
+       opts = container_of(f->fi, struct f_rndis_opts, func_inst);
+       kfree(rndis);
+       mutex_lock(&opts->lock);
+       opts->refcnt--;
+       mutex_unlock(&opts->lock);
+}
+
+static void rndis_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct f_rndis          *rndis = func_to_rndis(f);
+
+       kfree(f->os_desc_table);
+       f->os_desc_n = 0;
+       usb_free_all_descriptors(f);
+
+       kfree(rndis->notify_req->buf);
+       usb_ep_free_request(rndis->notify, rndis->notify_req);
+}
+
+static struct usb_function *rndis_alloc(struct usb_function_instance *fi)
+{
+       struct f_rndis  *rndis;
+       struct f_rndis_opts *opts;
+       int status;
+
+       /* allocate and initialize one new instance */
+       rndis = kzalloc(sizeof(*rndis), GFP_KERNEL);
+       if (!rndis)
+               return ERR_PTR(-ENOMEM);
+
+       opts = container_of(fi, struct f_rndis_opts, func_inst);
+       mutex_lock(&opts->lock);
+       opts->refcnt++;
+
+       gether_get_host_addr_u8(opts->net, rndis->ethaddr);
+       rndis->vendorID = opts->vendor_id;
+       rndis->manufacturer = opts->manufacturer;
+
+       rndis->port.ioport = netdev_priv(opts->net);
+       mutex_unlock(&opts->lock);
+       /* RNDIS activates when the host changes this filter */
+       rndis->port.cdc_filter = 0;
+
+       /* RNDIS has special (and complex) framing */
+       rndis->port.header_len = sizeof(struct rndis_packet_msg_type);
+       rndis->port.wrap = rndis_add_header;
+       rndis->port.unwrap = rndis_rm_hdr;
+
+       rndis->port.func.name = "rndis";
+       /* descriptors are per-instance copies */
+       rndis->port.func.bind = rndis_bind;
+       rndis->port.func.unbind = rndis_unbind;
+       rndis->port.func.set_alt = rndis_set_alt;
+       rndis->port.func.setup = rndis_setup;
+       rndis->port.func.disable = rndis_disable;
+       rndis->port.func.free_func = rndis_free;
+
+       status = rndis_register(rndis_response_available, rndis);
+       if (status < 0) {
+               kfree(rndis);
+               return ERR_PTR(status);
+       }
+       rndis->config = status;
+
+       return &rndis->port.func;
+}
+
+DECLARE_USB_FUNCTION(rndis, rndis_alloc_inst, rndis_alloc);
+
+static int __init rndis_mod_init(void)
+{
+       int ret;
+
+       ret = rndis_init();
+       if (ret)
+               return ret;
+
+       return usb_function_register(&rndisusb_func);
+}
+module_init(rndis_mod_init);
+
+static void __exit rndis_mod_exit(void)
+{
+       usb_function_unregister(&rndisusb_func);
+       rndis_exit();
+}
+module_exit(rndis_mod_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c
new file mode 100644 (file)
index 0000000..9ecbcbf
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+ * f_serial.c - generic USB serial function driver
+ *
+ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2008 by David Brownell
+ * Copyright (C) 2008 by Nokia Corporation
+ *
+ * This software is distributed under the terms of the GNU General
+ * Public License ("GPL") as published by the Free Software Foundation,
+ * either version 2 of that License or (at your option) any later version.
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+
+#include "u_serial.h"
+#include "gadget_chips.h"
+
+
+/*
+ * This function packages a simple "generic serial" port with no real
+ * control mechanisms, just raw data transfer over two bulk endpoints.
+ *
+ * Because it's not standardized, this isn't as interoperable as the
+ * CDC ACM driver.  However, for many purposes it's just as functional
+ * if you can arrange appropriate host side drivers.
+ */
+
+struct f_gser {
+       struct gserial                  port;
+       u8                              data_id;
+       u8                              port_num;
+};
+
+static inline struct f_gser *func_to_gser(struct usb_function *f)
+{
+       return container_of(f, struct f_gser, port.func);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* interface descriptor: */
+
+static struct usb_interface_descriptor gser_interface_desc = {
+       .bLength =              USB_DT_INTERFACE_SIZE,
+       .bDescriptorType =      USB_DT_INTERFACE,
+       /* .bInterfaceNumber = DYNAMIC */
+       .bNumEndpoints =        2,
+       .bInterfaceClass =      USB_CLASS_VENDOR_SPEC,
+       .bInterfaceSubClass =   0,
+       .bInterfaceProtocol =   0,
+       /* .iInterface = DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor gser_fs_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor gser_fs_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *gser_fs_function[] = {
+       (struct usb_descriptor_header *) &gser_interface_desc,
+       (struct usb_descriptor_header *) &gser_fs_in_desc,
+       (struct usb_descriptor_header *) &gser_fs_out_desc,
+       NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor gser_hs_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor gser_hs_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *gser_hs_function[] = {
+       (struct usb_descriptor_header *) &gser_interface_desc,
+       (struct usb_descriptor_header *) &gser_hs_in_desc,
+       (struct usb_descriptor_header *) &gser_hs_out_desc,
+       NULL,
+};
+
+static struct usb_endpoint_descriptor gser_ss_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor gser_ss_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor gser_ss_bulk_comp_desc = {
+       .bLength =              sizeof gser_ss_bulk_comp_desc,
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_descriptor_header *gser_ss_function[] = {
+       (struct usb_descriptor_header *) &gser_interface_desc,
+       (struct usb_descriptor_header *) &gser_ss_in_desc,
+       (struct usb_descriptor_header *) &gser_ss_bulk_comp_desc,
+       (struct usb_descriptor_header *) &gser_ss_out_desc,
+       (struct usb_descriptor_header *) &gser_ss_bulk_comp_desc,
+       NULL,
+};
+
+/* string descriptors: */
+
+static struct usb_string gser_string_defs[] = {
+       [0].s = "Generic Serial",
+       {  } /* end of list */
+};
+
+static struct usb_gadget_strings gser_string_table = {
+       .language =             0x0409, /* en-us */
+       .strings =              gser_string_defs,
+};
+
+static struct usb_gadget_strings *gser_strings[] = {
+       &gser_string_table,
+       NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+       struct f_gser           *gser = func_to_gser(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+
+       /* we know alt == 0, so this is an activation or a reset */
+
+       if (gser->port.in->driver_data) {
+               DBG(cdev, "reset generic ttyGS%d\n", gser->port_num);
+               gserial_disconnect(&gser->port);
+       }
+       if (!gser->port.in->desc || !gser->port.out->desc) {
+               DBG(cdev, "activate generic ttyGS%d\n", gser->port_num);
+               if (config_ep_by_speed(cdev->gadget, f, gser->port.in) ||
+                   config_ep_by_speed(cdev->gadget, f, gser->port.out)) {
+                       gser->port.in->desc = NULL;
+                       gser->port.out->desc = NULL;
+                       return -EINVAL;
+               }
+       }
+       gserial_connect(&gser->port, gser->port_num);
+       return 0;
+}
+
+static void gser_disable(struct usb_function *f)
+{
+       struct f_gser   *gser = func_to_gser(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+
+       DBG(cdev, "generic ttyGS%d deactivated\n", gser->port_num);
+       gserial_disconnect(&gser->port);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* serial function driver setup/binding */
+
+static int gser_bind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct usb_composite_dev *cdev = c->cdev;
+       struct f_gser           *gser = func_to_gser(f);
+       int                     status;
+       struct usb_ep           *ep;
+
+       /* REVISIT might want instance-specific strings to help
+        * distinguish instances ...
+        */
+
+       /* maybe allocate device-global string ID */
+       if (gser_string_defs[0].id == 0) {
+               status = usb_string_id(c->cdev);
+               if (status < 0)
+                       return status;
+               gser_string_defs[0].id = status;
+       }
+
+       /* allocate instance-specific interface IDs */
+       status = usb_interface_id(c, f);
+       if (status < 0)
+               goto fail;
+       gser->data_id = status;
+       gser_interface_desc.bInterfaceNumber = status;
+
+       status = -ENODEV;
+
+       /* allocate instance-specific endpoints */
+       ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_in_desc);
+       if (!ep)
+               goto fail;
+       gser->port.in = ep;
+       ep->driver_data = cdev; /* claim */
+
+       ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_out_desc);
+       if (!ep)
+               goto fail;
+       gser->port.out = ep;
+       ep->driver_data = cdev; /* claim */
+
+       /* support all relevant hardware speeds... we expect that when
+        * hardware is dual speed, all bulk-capable endpoints work at
+        * both speeds
+        */
+       gser_hs_in_desc.bEndpointAddress = gser_fs_in_desc.bEndpointAddress;
+       gser_hs_out_desc.bEndpointAddress = gser_fs_out_desc.bEndpointAddress;
+
+       gser_ss_in_desc.bEndpointAddress = gser_fs_in_desc.bEndpointAddress;
+       gser_ss_out_desc.bEndpointAddress = gser_fs_out_desc.bEndpointAddress;
+
+       status = usb_assign_descriptors(f, gser_fs_function, gser_hs_function,
+                       gser_ss_function);
+       if (status)
+               goto fail;
+       DBG(cdev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n",
+                       gser->port_num,
+                       gadget_is_superspeed(c->cdev->gadget) ? "super" :
+                       gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+                       gser->port.in->name, gser->port.out->name);
+       return 0;
+
+fail:
+       /* we might as well release our claims on endpoints */
+       if (gser->port.out)
+               gser->port.out->driver_data = NULL;
+       if (gser->port.in)
+               gser->port.in->driver_data = NULL;
+
+       ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
+
+       return status;
+}
+
+static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct f_serial_opts,
+                           func_inst.group);
+}
+
+CONFIGFS_ATTR_STRUCT(f_serial_opts);
+static ssize_t f_serial_attr_show(struct config_item *item,
+                                 struct configfs_attribute *attr,
+                                 char *page)
+{
+       struct f_serial_opts *opts = to_f_serial_opts(item);
+       struct f_serial_opts_attribute *f_serial_opts_attr =
+               container_of(attr, struct f_serial_opts_attribute, attr);
+       ssize_t ret = 0;
+
+       if (f_serial_opts_attr->show)
+               ret = f_serial_opts_attr->show(opts, page);
+
+       return ret;
+}
+
+static void serial_attr_release(struct config_item *item)
+{
+       struct f_serial_opts *opts = to_f_serial_opts(item);
+
+       usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations serial_item_ops = {
+       .release        = serial_attr_release,
+       .show_attribute = f_serial_attr_show,
+};
+
+static ssize_t f_serial_port_num_show(struct f_serial_opts *opts, char *page)
+{
+       return sprintf(page, "%u\n", opts->port_num);
+}
+
+static struct f_serial_opts_attribute f_serial_port_num =
+       __CONFIGFS_ATTR_RO(port_num, f_serial_port_num_show);
+
+static struct configfs_attribute *acm_attrs[] = {
+       &f_serial_port_num.attr,
+       NULL,
+};
+
+static struct config_item_type serial_func_type = {
+       .ct_item_ops    = &serial_item_ops,
+       .ct_attrs       = acm_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+static void gser_free_inst(struct usb_function_instance *f)
+{
+       struct f_serial_opts *opts;
+
+       opts = container_of(f, struct f_serial_opts, func_inst);
+       gserial_free_line(opts->port_num);
+       kfree(opts);
+}
+
+static struct usb_function_instance *gser_alloc_inst(void)
+{
+       struct f_serial_opts *opts;
+       int ret;
+
+       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+       if (!opts)
+               return ERR_PTR(-ENOMEM);
+
+       opts->func_inst.free_func_inst = gser_free_inst;
+       ret = gserial_alloc_line(&opts->port_num);
+       if (ret) {
+               kfree(opts);
+               return ERR_PTR(ret);
+       }
+       config_group_init_type_name(&opts->func_inst.group, "",
+                                   &serial_func_type);
+
+       return &opts->func_inst;
+}
+
+static void gser_free(struct usb_function *f)
+{
+       struct f_gser *serial;
+
+       serial = func_to_gser(f);
+       kfree(serial);
+}
+
+static void gser_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+       usb_free_all_descriptors(f);
+}
+
+static struct usb_function *gser_alloc(struct usb_function_instance *fi)
+{
+       struct f_gser   *gser;
+       struct f_serial_opts *opts;
+
+       /* allocate and initialize one new instance */
+       gser = kzalloc(sizeof(*gser), GFP_KERNEL);
+       if (!gser)
+               return ERR_PTR(-ENOMEM);
+
+       opts = container_of(fi, struct f_serial_opts, func_inst);
+
+       gser->port_num = opts->port_num;
+
+       gser->port.func.name = "gser";
+       gser->port.func.strings = gser_strings;
+       gser->port.func.bind = gser_bind;
+       gser->port.func.unbind = gser_unbind;
+       gser->port.func.set_alt = gser_set_alt;
+       gser->port.func.disable = gser_disable;
+       gser->port.func.free_func = gser_free;
+
+       return &gser->port.func;
+}
+
+DECLARE_USB_FUNCTION_INIT(gser, gser_alloc_inst, gser_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Al Borchers");
+MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c
new file mode 100644 (file)
index 0000000..d3cd52d
--- /dev/null
@@ -0,0 +1,1247 @@
+/*
+ * f_sourcesink.c - USB peripheral source/sink configuration driver
+ *
+ * Copyright (C) 2003-2008 David Brownell
+ * Copyright (C) 2008 by Nokia Corporation
+ *
+ * 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.
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/usb/composite.h>
+#include <linux/err.h>
+
+#include "g_zero.h"
+#include "gadget_chips.h"
+#include "u_f.h"
+
+/*
+ * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral
+ * controller drivers.
+ *
+ * This just sinks bulk packets OUT to the peripheral and sources them IN
+ * to the host, optionally with specific data patterns for integrity tests.
+ * As such it supports basic functionality and load tests.
+ *
+ * In terms of control messaging, this supports all the standard requests
+ * plus two that support control-OUT tests.  If the optional "autoresume"
+ * mode is enabled, it provides good functional coverage for the "USBCV"
+ * test harness from USB-IF.
+ *
+ * Note that because this doesn't queue more than one request at a time,
+ * some other function must be used to test queueing logic.  The network
+ * link (g_ether) is the best overall option for that, since its TX and RX
+ * queues are relatively independent, will receive a range of packet sizes,
+ * and can often be made to run out completely.  Those issues are important
+ * when stress testing peripheral controller drivers.
+ *
+ *
+ * This is currently packaged as a configuration driver, which can't be
+ * combined with other functions to make composite devices.  However, it
+ * can be combined with other independent configurations.
+ */
+struct f_sourcesink {
+       struct usb_function     function;
+
+       struct usb_ep           *in_ep;
+       struct usb_ep           *out_ep;
+       struct usb_ep           *iso_in_ep;
+       struct usb_ep           *iso_out_ep;
+       int                     cur_alt;
+};
+
+static inline struct f_sourcesink *func_to_ss(struct usb_function *f)
+{
+       return container_of(f, struct f_sourcesink, function);
+}
+
+static unsigned pattern;
+static unsigned isoc_interval;
+static unsigned isoc_maxpacket;
+static unsigned isoc_mult;
+static unsigned isoc_maxburst;
+static unsigned buflen;
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_interface_descriptor source_sink_intf_alt0 = {
+       .bLength =              USB_DT_INTERFACE_SIZE,
+       .bDescriptorType =      USB_DT_INTERFACE,
+
+       .bAlternateSetting =    0,
+       .bNumEndpoints =        2,
+       .bInterfaceClass =      USB_CLASS_VENDOR_SPEC,
+       /* .iInterface          = DYNAMIC */
+};
+
+static struct usb_interface_descriptor source_sink_intf_alt1 = {
+       .bLength =              USB_DT_INTERFACE_SIZE,
+       .bDescriptorType =      USB_DT_INTERFACE,
+
+       .bAlternateSetting =    1,
+       .bNumEndpoints =        4,
+       .bInterfaceClass =      USB_CLASS_VENDOR_SPEC,
+       /* .iInterface          = DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor fs_source_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor fs_sink_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor fs_iso_source_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_ISOC,
+       .wMaxPacketSize =       cpu_to_le16(1023),
+       .bInterval =            4,
+};
+
+static struct usb_endpoint_descriptor fs_iso_sink_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_ISOC,
+       .wMaxPacketSize =       cpu_to_le16(1023),
+       .bInterval =            4,
+};
+
+static struct usb_descriptor_header *fs_source_sink_descs[] = {
+       (struct usb_descriptor_header *) &source_sink_intf_alt0,
+       (struct usb_descriptor_header *) &fs_sink_desc,
+       (struct usb_descriptor_header *) &fs_source_desc,
+       (struct usb_descriptor_header *) &source_sink_intf_alt1,
+#define FS_ALT_IFC_1_OFFSET    3
+       (struct usb_descriptor_header *) &fs_sink_desc,
+       (struct usb_descriptor_header *) &fs_source_desc,
+       (struct usb_descriptor_header *) &fs_iso_sink_desc,
+       (struct usb_descriptor_header *) &fs_iso_source_desc,
+       NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor hs_source_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor hs_sink_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor hs_iso_source_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bmAttributes =         USB_ENDPOINT_XFER_ISOC,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+       .bInterval =            4,
+};
+
+static struct usb_endpoint_descriptor hs_iso_sink_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bmAttributes =         USB_ENDPOINT_XFER_ISOC,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+       .bInterval =            4,
+};
+
+static struct usb_descriptor_header *hs_source_sink_descs[] = {
+       (struct usb_descriptor_header *) &source_sink_intf_alt0,
+       (struct usb_descriptor_header *) &hs_source_desc,
+       (struct usb_descriptor_header *) &hs_sink_desc,
+       (struct usb_descriptor_header *) &source_sink_intf_alt1,
+#define HS_ALT_IFC_1_OFFSET    3
+       (struct usb_descriptor_header *) &hs_source_desc,
+       (struct usb_descriptor_header *) &hs_sink_desc,
+       (struct usb_descriptor_header *) &hs_iso_source_desc,
+       (struct usb_descriptor_header *) &hs_iso_sink_desc,
+       NULL,
+};
+
+/* super speed support: */
+
+static struct usb_endpoint_descriptor ss_source_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ss_source_comp_desc = {
+       .bLength =              USB_DT_SS_EP_COMP_SIZE,
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+
+       .bMaxBurst =            0,
+       .bmAttributes =         0,
+       .wBytesPerInterval =    0,
+};
+
+static struct usb_endpoint_descriptor ss_sink_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ss_sink_comp_desc = {
+       .bLength =              USB_DT_SS_EP_COMP_SIZE,
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+
+       .bMaxBurst =            0,
+       .bmAttributes =         0,
+       .wBytesPerInterval =    0,
+};
+
+static struct usb_endpoint_descriptor ss_iso_source_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bmAttributes =         USB_ENDPOINT_XFER_ISOC,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+       .bInterval =            4,
+};
+
+static struct usb_ss_ep_comp_descriptor ss_iso_source_comp_desc = {
+       .bLength =              USB_DT_SS_EP_COMP_SIZE,
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+
+       .bMaxBurst =            0,
+       .bmAttributes =         0,
+       .wBytesPerInterval =    cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor ss_iso_sink_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bmAttributes =         USB_ENDPOINT_XFER_ISOC,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+       .bInterval =            4,
+};
+
+static struct usb_ss_ep_comp_descriptor ss_iso_sink_comp_desc = {
+       .bLength =              USB_DT_SS_EP_COMP_SIZE,
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+
+       .bMaxBurst =            0,
+       .bmAttributes =         0,
+       .wBytesPerInterval =    cpu_to_le16(1024),
+};
+
+static struct usb_descriptor_header *ss_source_sink_descs[] = {
+       (struct usb_descriptor_header *) &source_sink_intf_alt0,
+       (struct usb_descriptor_header *) &ss_source_desc,
+       (struct usb_descriptor_header *) &ss_source_comp_desc,
+       (struct usb_descriptor_header *) &ss_sink_desc,
+       (struct usb_descriptor_header *) &ss_sink_comp_desc,
+       (struct usb_descriptor_header *) &source_sink_intf_alt1,
+#define SS_ALT_IFC_1_OFFSET    5
+       (struct usb_descriptor_header *) &ss_source_desc,
+       (struct usb_descriptor_header *) &ss_source_comp_desc,
+       (struct usb_descriptor_header *) &ss_sink_desc,
+       (struct usb_descriptor_header *) &ss_sink_comp_desc,
+       (struct usb_descriptor_header *) &ss_iso_source_desc,
+       (struct usb_descriptor_header *) &ss_iso_source_comp_desc,
+       (struct usb_descriptor_header *) &ss_iso_sink_desc,
+       (struct usb_descriptor_header *) &ss_iso_sink_comp_desc,
+       NULL,
+};
+
+/* function-specific strings: */
+
+static struct usb_string strings_sourcesink[] = {
+       [0].s = "source and sink data",
+       {  }                    /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_sourcesink = {
+       .language       = 0x0409,       /* en-us */
+       .strings        = strings_sourcesink,
+};
+
+static struct usb_gadget_strings *sourcesink_strings[] = {
+       &stringtab_sourcesink,
+       NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static inline struct usb_request *ss_alloc_ep_req(struct usb_ep *ep, int len)
+{
+       return alloc_ep_req(ep, len, buflen);
+}
+
+void free_ep_req(struct usb_ep *ep, struct usb_request *req)
+{
+       kfree(req->buf);
+       usb_ep_free_request(ep, req);
+}
+
+static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep)
+{
+       int                     value;
+
+       if (ep->driver_data) {
+               value = usb_ep_disable(ep);
+               if (value < 0)
+                       DBG(cdev, "disable %s --> %d\n",
+                                       ep->name, value);
+               ep->driver_data = NULL;
+       }
+}
+
+void disable_endpoints(struct usb_composite_dev *cdev,
+               struct usb_ep *in, struct usb_ep *out,
+               struct usb_ep *iso_in, struct usb_ep *iso_out)
+{
+       disable_ep(cdev, in);
+       disable_ep(cdev, out);
+       if (iso_in)
+               disable_ep(cdev, iso_in);
+       if (iso_out)
+               disable_ep(cdev, iso_out);
+}
+
+static int
+sourcesink_bind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct usb_composite_dev *cdev = c->cdev;
+       struct f_sourcesink     *ss = func_to_ss(f);
+       int     id;
+       int ret;
+
+       /* allocate interface ID(s) */
+       id = usb_interface_id(c, f);
+       if (id < 0)
+               return id;
+       source_sink_intf_alt0.bInterfaceNumber = id;
+       source_sink_intf_alt1.bInterfaceNumber = id;
+
+       /* allocate bulk endpoints */
+       ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
+       if (!ss->in_ep) {
+autoconf_fail:
+               ERROR(cdev, "%s: can't autoconfigure on %s\n",
+                       f->name, cdev->gadget->name);
+               return -ENODEV;
+       }
+       ss->in_ep->driver_data = cdev;  /* claim */
+
+       ss->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_sink_desc);
+       if (!ss->out_ep)
+               goto autoconf_fail;
+       ss->out_ep->driver_data = cdev; /* claim */
+
+       /* sanity check the isoc module parameters */
+       if (isoc_interval < 1)
+               isoc_interval = 1;
+       if (isoc_interval > 16)
+               isoc_interval = 16;
+       if (isoc_mult > 2)
+               isoc_mult = 2;
+       if (isoc_maxburst > 15)
+               isoc_maxburst = 15;
+
+       /* fill in the FS isoc descriptors from the module parameters */
+       fs_iso_source_desc.wMaxPacketSize = isoc_maxpacket > 1023 ?
+                                               1023 : isoc_maxpacket;
+       fs_iso_source_desc.bInterval = isoc_interval;
+       fs_iso_sink_desc.wMaxPacketSize = isoc_maxpacket > 1023 ?
+                                               1023 : isoc_maxpacket;
+       fs_iso_sink_desc.bInterval = isoc_interval;
+
+       /* allocate iso endpoints */
+       ss->iso_in_ep = usb_ep_autoconfig(cdev->gadget, &fs_iso_source_desc);
+       if (!ss->iso_in_ep)
+               goto no_iso;
+       ss->iso_in_ep->driver_data = cdev;      /* claim */
+
+       ss->iso_out_ep = usb_ep_autoconfig(cdev->gadget, &fs_iso_sink_desc);
+       if (ss->iso_out_ep) {
+               ss->iso_out_ep->driver_data = cdev;     /* claim */
+       } else {
+               ss->iso_in_ep->driver_data = NULL;
+               ss->iso_in_ep = NULL;
+no_iso:
+               /*
+                * We still want to work even if the UDC doesn't have isoc
+                * endpoints, so null out the alt interface that contains
+                * them and continue.
+                */
+               fs_source_sink_descs[FS_ALT_IFC_1_OFFSET] = NULL;
+               hs_source_sink_descs[HS_ALT_IFC_1_OFFSET] = NULL;
+               ss_source_sink_descs[SS_ALT_IFC_1_OFFSET] = NULL;
+       }
+
+       if (isoc_maxpacket > 1024)
+               isoc_maxpacket = 1024;
+
+       /* support high speed hardware */
+       hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress;
+       hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress;
+
+       /*
+        * Fill in the HS isoc descriptors from the module parameters.
+        * We assume that the user knows what they are doing and won't
+        * give parameters that their UDC doesn't support.
+        */
+       hs_iso_source_desc.wMaxPacketSize = isoc_maxpacket;
+       hs_iso_source_desc.wMaxPacketSize |= isoc_mult << 11;
+       hs_iso_source_desc.bInterval = isoc_interval;
+       hs_iso_source_desc.bEndpointAddress =
+               fs_iso_source_desc.bEndpointAddress;
+
+       hs_iso_sink_desc.wMaxPacketSize = isoc_maxpacket;
+       hs_iso_sink_desc.wMaxPacketSize |= isoc_mult << 11;
+       hs_iso_sink_desc.bInterval = isoc_interval;
+       hs_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress;
+
+       /* support super speed hardware */
+       ss_source_desc.bEndpointAddress =
+               fs_source_desc.bEndpointAddress;
+       ss_sink_desc.bEndpointAddress =
+               fs_sink_desc.bEndpointAddress;
+
+       /*
+        * Fill in the SS isoc descriptors from the module parameters.
+        * We assume that the user knows what they are doing and won't
+        * give parameters that their UDC doesn't support.
+        */
+       ss_iso_source_desc.wMaxPacketSize = isoc_maxpacket;
+       ss_iso_source_desc.bInterval = isoc_interval;
+       ss_iso_source_comp_desc.bmAttributes = isoc_mult;
+       ss_iso_source_comp_desc.bMaxBurst = isoc_maxburst;
+       ss_iso_source_comp_desc.wBytesPerInterval =
+               isoc_maxpacket * (isoc_mult + 1) * (isoc_maxburst + 1);
+       ss_iso_source_desc.bEndpointAddress =
+               fs_iso_source_desc.bEndpointAddress;
+
+       ss_iso_sink_desc.wMaxPacketSize = isoc_maxpacket;
+       ss_iso_sink_desc.bInterval = isoc_interval;
+       ss_iso_sink_comp_desc.bmAttributes = isoc_mult;
+       ss_iso_sink_comp_desc.bMaxBurst = isoc_maxburst;
+       ss_iso_sink_comp_desc.wBytesPerInterval =
+               isoc_maxpacket * (isoc_mult + 1) * (isoc_maxburst + 1);
+       ss_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress;
+
+       ret = usb_assign_descriptors(f, fs_source_sink_descs,
+                       hs_source_sink_descs, ss_source_sink_descs);
+       if (ret)
+               return ret;
+
+       DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s\n",
+           (gadget_is_superspeed(c->cdev->gadget) ? "super" :
+            (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")),
+                       f->name, ss->in_ep->name, ss->out_ep->name,
+                       ss->iso_in_ep ? ss->iso_in_ep->name : "<none>",
+                       ss->iso_out_ep ? ss->iso_out_ep->name : "<none>");
+       return 0;
+}
+
+static void
+sourcesink_free_func(struct usb_function *f)
+{
+       struct f_ss_opts *opts;
+
+       opts = container_of(f->fi, struct f_ss_opts, func_inst);
+
+       mutex_lock(&opts->lock);
+       opts->refcnt--;
+       mutex_unlock(&opts->lock);
+
+       usb_free_all_descriptors(f);
+       kfree(func_to_ss(f));
+}
+
+/* optionally require specific source/sink data patterns  */
+static int check_read_data(struct f_sourcesink *ss, struct usb_request *req)
+{
+       unsigned                i;
+       u8                      *buf = req->buf;
+       struct usb_composite_dev *cdev = ss->function.config->cdev;
+
+       if (pattern == 2)
+               return 0;
+
+       for (i = 0; i < req->actual; i++, buf++) {
+               switch (pattern) {
+
+               /* all-zeroes has no synchronization issues */
+               case 0:
+                       if (*buf == 0)
+                               continue;
+                       break;
+
+               /* "mod63" stays in sync with short-terminated transfers,
+                * OR otherwise when host and gadget agree on how large
+                * each usb transfer request should be.  Resync is done
+                * with set_interface or set_config.  (We *WANT* it to
+                * get quickly out of sync if controllers or their drivers
+                * stutter for any reason, including buffer duplication...)
+                */
+               case 1:
+                       if (*buf == (u8)(i % 63))
+                               continue;
+                       break;
+               }
+               ERROR(cdev, "bad OUT byte, buf[%d] = %d\n", i, *buf);
+               usb_ep_set_halt(ss->out_ep);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static void reinit_write_data(struct usb_ep *ep, struct usb_request *req)
+{
+       unsigned        i;
+       u8              *buf = req->buf;
+
+       switch (pattern) {
+       case 0:
+               memset(req->buf, 0, req->length);
+               break;
+       case 1:
+               for  (i = 0; i < req->length; i++)
+                       *buf++ = (u8) (i % 63);
+               break;
+       case 2:
+               break;
+       }
+}
+
+static void source_sink_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct usb_composite_dev        *cdev;
+       struct f_sourcesink             *ss = ep->driver_data;
+       int                             status = req->status;
+
+       /* driver_data will be null if ep has been disabled */
+       if (!ss)
+               return;
+
+       cdev = ss->function.config->cdev;
+
+       switch (status) {
+
+       case 0:                         /* normal completion? */
+               if (ep == ss->out_ep) {
+                       check_read_data(ss, req);
+                       if (pattern != 2)
+                               memset(req->buf, 0x55, req->length);
+               }
+               break;
+
+       /* this endpoint is normally active while we're configured */
+       case -ECONNABORTED:             /* hardware forced ep reset */
+       case -ECONNRESET:               /* request dequeued */
+       case -ESHUTDOWN:                /* disconnect from host */
+               VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status,
+                               req->actual, req->length);
+               if (ep == ss->out_ep)
+                       check_read_data(ss, req);
+               free_ep_req(ep, req);
+               return;
+
+       case -EOVERFLOW:                /* buffer overrun on read means that
+                                        * we didn't provide a big enough
+                                        * buffer.
+                                        */
+       default:
+#if 1
+               DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name,
+                               status, req->actual, req->length);
+#endif
+       case -EREMOTEIO:                /* short read */
+               break;
+       }
+
+       status = usb_ep_queue(ep, req, GFP_ATOMIC);
+       if (status) {
+               ERROR(cdev, "kill %s:  resubmit %d bytes --> %d\n",
+                               ep->name, req->length, status);
+               usb_ep_set_halt(ep);
+               /* FIXME recover later ... somehow */
+       }
+}
+
+static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
+               bool is_iso, int speed)
+{
+       struct usb_ep           *ep;
+       struct usb_request      *req;
+       int                     i, size, status;
+
+       for (i = 0; i < 8; i++) {
+               if (is_iso) {
+                       switch (speed) {
+                       case USB_SPEED_SUPER:
+                               size = isoc_maxpacket * (isoc_mult + 1) *
+                                               (isoc_maxburst + 1);
+                               break;
+                       case USB_SPEED_HIGH:
+                               size = isoc_maxpacket * (isoc_mult + 1);
+                               break;
+                       default:
+                               size = isoc_maxpacket > 1023 ?
+                                               1023 : isoc_maxpacket;
+                               break;
+                       }
+                       ep = is_in ? ss->iso_in_ep : ss->iso_out_ep;
+                       req = ss_alloc_ep_req(ep, size);
+               } else {
+                       ep = is_in ? ss->in_ep : ss->out_ep;
+                       req = ss_alloc_ep_req(ep, 0);
+               }
+
+               if (!req)
+                       return -ENOMEM;
+
+               req->complete = source_sink_complete;
+               if (is_in)
+                       reinit_write_data(ep, req);
+               else if (pattern != 2)
+                       memset(req->buf, 0x55, req->length);
+
+               status = usb_ep_queue(ep, req, GFP_ATOMIC);
+               if (status) {
+                       struct usb_composite_dev        *cdev;
+
+                       cdev = ss->function.config->cdev;
+                       ERROR(cdev, "start %s%s %s --> %d\n",
+                             is_iso ? "ISO-" : "", is_in ? "IN" : "OUT",
+                             ep->name, status);
+                       free_ep_req(ep, req);
+               }
+
+               if (!is_iso)
+                       break;
+       }
+
+       return status;
+}
+
+static void disable_source_sink(struct f_sourcesink *ss)
+{
+       struct usb_composite_dev        *cdev;
+
+       cdev = ss->function.config->cdev;
+       disable_endpoints(cdev, ss->in_ep, ss->out_ep, ss->iso_in_ep,
+                       ss->iso_out_ep);
+       VDBG(cdev, "%s disabled\n", ss->function.name);
+}
+
+static int
+enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss,
+               int alt)
+{
+       int                                     result = 0;
+       int                                     speed = cdev->gadget->speed;
+       struct usb_ep                           *ep;
+
+       /* one bulk endpoint writes (sources) zeroes IN (to the host) */
+       ep = ss->in_ep;
+       result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
+       if (result)
+               return result;
+       result = usb_ep_enable(ep);
+       if (result < 0)
+               return result;
+       ep->driver_data = ss;
+
+       result = source_sink_start_ep(ss, true, false, speed);
+       if (result < 0) {
+fail:
+               ep = ss->in_ep;
+               usb_ep_disable(ep);
+               ep->driver_data = NULL;
+               return result;
+       }
+
+       /* one bulk endpoint reads (sinks) anything OUT (from the host) */
+       ep = ss->out_ep;
+       result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
+       if (result)
+               goto fail;
+       result = usb_ep_enable(ep);
+       if (result < 0)
+               goto fail;
+       ep->driver_data = ss;
+
+       result = source_sink_start_ep(ss, false, false, speed);
+       if (result < 0) {
+fail2:
+               ep = ss->out_ep;
+               usb_ep_disable(ep);
+               ep->driver_data = NULL;
+               goto fail;
+       }
+
+       if (alt == 0)
+               goto out;
+
+       /* one iso endpoint writes (sources) zeroes IN (to the host) */
+       ep = ss->iso_in_ep;
+       if (ep) {
+               result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
+               if (result)
+                       goto fail2;
+               result = usb_ep_enable(ep);
+               if (result < 0)
+                       goto fail2;
+               ep->driver_data = ss;
+
+               result = source_sink_start_ep(ss, true, true, speed);
+               if (result < 0) {
+fail3:
+                       ep = ss->iso_in_ep;
+                       if (ep) {
+                               usb_ep_disable(ep);
+                               ep->driver_data = NULL;
+                       }
+                       goto fail2;
+               }
+       }
+
+       /* one iso endpoint reads (sinks) anything OUT (from the host) */
+       ep = ss->iso_out_ep;
+       if (ep) {
+               result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
+               if (result)
+                       goto fail3;
+               result = usb_ep_enable(ep);
+               if (result < 0)
+                       goto fail3;
+               ep->driver_data = ss;
+
+               result = source_sink_start_ep(ss, false, true, speed);
+               if (result < 0) {
+                       usb_ep_disable(ep);
+                       ep->driver_data = NULL;
+                       goto fail3;
+               }
+       }
+out:
+       ss->cur_alt = alt;
+
+       DBG(cdev, "%s enabled, alt intf %d\n", ss->function.name, alt);
+       return result;
+}
+
+static int sourcesink_set_alt(struct usb_function *f,
+               unsigned intf, unsigned alt)
+{
+       struct f_sourcesink             *ss = func_to_ss(f);
+       struct usb_composite_dev        *cdev = f->config->cdev;
+
+       if (ss->in_ep->driver_data)
+               disable_source_sink(ss);
+       return enable_source_sink(cdev, ss, alt);
+}
+
+static int sourcesink_get_alt(struct usb_function *f, unsigned intf)
+{
+       struct f_sourcesink             *ss = func_to_ss(f);
+
+       return ss->cur_alt;
+}
+
+static void sourcesink_disable(struct usb_function *f)
+{
+       struct f_sourcesink     *ss = func_to_ss(f);
+
+       disable_source_sink(ss);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int sourcesink_setup(struct usb_function *f,
+               const struct usb_ctrlrequest *ctrl)
+{
+       struct usb_configuration        *c = f->config;
+       struct usb_request      *req = c->cdev->req;
+       int                     value = -EOPNOTSUPP;
+       u16                     w_index = le16_to_cpu(ctrl->wIndex);
+       u16                     w_value = le16_to_cpu(ctrl->wValue);
+       u16                     w_length = le16_to_cpu(ctrl->wLength);
+
+       req->length = USB_COMP_EP0_BUFSIZ;
+
+       /* composite driver infrastructure handles everything except
+        * the two control test requests.
+        */
+       switch (ctrl->bRequest) {
+
+       /*
+        * These are the same vendor-specific requests supported by
+        * Intel's USB 2.0 compliance test devices.  We exceed that
+        * device spec by allowing multiple-packet requests.
+        *
+        * NOTE:  the Control-OUT data stays in req->buf ... better
+        * would be copying it into a scratch buffer, so that other
+        * requests may safely intervene.
+        */
+       case 0x5b:      /* control WRITE test -- fill the buffer */
+               if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR))
+                       goto unknown;
+               if (w_value || w_index)
+                       break;
+               /* just read that many bytes into the buffer */
+               if (w_length > req->length)
+                       break;
+               value = w_length;
+               break;
+       case 0x5c:      /* control READ test -- return the buffer */
+               if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR))
+                       goto unknown;
+               if (w_value || w_index)
+                       break;
+               /* expect those bytes are still in the buffer; send back */
+               if (w_length > req->length)
+                       break;
+               value = w_length;
+               break;
+
+       default:
+unknown:
+               VDBG(c->cdev,
+                       "unknown control req%02x.%02x v%04x i%04x l%d\n",
+                       ctrl->bRequestType, ctrl->bRequest,
+                       w_value, w_index, w_length);
+       }
+
+       /* respond with data transfer or status phase? */
+       if (value >= 0) {
+               VDBG(c->cdev, "source/sink req%02x.%02x v%04x i%04x l%d\n",
+                       ctrl->bRequestType, ctrl->bRequest,
+                       w_value, w_index, w_length);
+               req->zero = 0;
+               req->length = value;
+               value = usb_ep_queue(c->cdev->gadget->ep0, req, GFP_ATOMIC);
+               if (value < 0)
+                       ERROR(c->cdev, "source/sink response, err %d\n",
+                                       value);
+       }
+
+       /* device either stalls (value < 0) or reports success */
+       return value;
+}
+
+static struct usb_function *source_sink_alloc_func(
+               struct usb_function_instance *fi)
+{
+       struct f_sourcesink     *ss;
+       struct f_ss_opts        *ss_opts;
+
+       ss = kzalloc(sizeof(*ss), GFP_KERNEL);
+       if (!ss)
+               return NULL;
+
+       ss_opts =  container_of(fi, struct f_ss_opts, func_inst);
+
+       mutex_lock(&ss_opts->lock);
+       ss_opts->refcnt++;
+       mutex_unlock(&ss_opts->lock);
+
+       pattern = ss_opts->pattern;
+       isoc_interval = ss_opts->isoc_interval;
+       isoc_maxpacket = ss_opts->isoc_maxpacket;
+       isoc_mult = ss_opts->isoc_mult;
+       isoc_maxburst = ss_opts->isoc_maxburst;
+       buflen = ss_opts->bulk_buflen;
+
+       ss->function.name = "source/sink";
+       ss->function.bind = sourcesink_bind;
+       ss->function.set_alt = sourcesink_set_alt;
+       ss->function.get_alt = sourcesink_get_alt;
+       ss->function.disable = sourcesink_disable;
+       ss->function.setup = sourcesink_setup;
+       ss->function.strings = sourcesink_strings;
+
+       ss->function.free_func = sourcesink_free_func;
+
+       return &ss->function;
+}
+
+static inline struct f_ss_opts *to_f_ss_opts(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct f_ss_opts,
+                           func_inst.group);
+}
+
+CONFIGFS_ATTR_STRUCT(f_ss_opts);
+CONFIGFS_ATTR_OPS(f_ss_opts);
+
+static void ss_attr_release(struct config_item *item)
+{
+       struct f_ss_opts *ss_opts = to_f_ss_opts(item);
+
+       usb_put_function_instance(&ss_opts->func_inst);
+}
+
+static struct configfs_item_operations ss_item_ops = {
+       .release                = ss_attr_release,
+       .show_attribute         = f_ss_opts_attr_show,
+       .store_attribute        = f_ss_opts_attr_store,
+};
+
+static ssize_t f_ss_opts_pattern_show(struct f_ss_opts *opts, char *page)
+{
+       int result;
+
+       mutex_lock(&opts->lock);
+       result = sprintf(page, "%d", opts->pattern);
+       mutex_unlock(&opts->lock);
+
+       return result;
+}
+
+static ssize_t f_ss_opts_pattern_store(struct f_ss_opts *opts,
+                                      const char *page, size_t len)
+{
+       int ret;
+       u8 num;
+
+       mutex_lock(&opts->lock);
+       if (opts->refcnt) {
+               ret = -EBUSY;
+               goto end;
+       }
+
+       ret = kstrtou8(page, 0, &num);
+       if (ret)
+               goto end;
+
+       if (num != 0 && num != 1 && num != 2) {
+               ret = -EINVAL;
+               goto end;
+       }
+
+       opts->pattern = num;
+       ret = len;
+end:
+       mutex_unlock(&opts->lock);
+       return ret;
+}
+
+static struct f_ss_opts_attribute f_ss_opts_pattern =
+       __CONFIGFS_ATTR(pattern, S_IRUGO | S_IWUSR,
+                       f_ss_opts_pattern_show,
+                       f_ss_opts_pattern_store);
+
+static ssize_t f_ss_opts_isoc_interval_show(struct f_ss_opts *opts, char *page)
+{
+       int result;
+
+       mutex_lock(&opts->lock);
+       result = sprintf(page, "%d", opts->isoc_interval);
+       mutex_unlock(&opts->lock);
+
+       return result;
+}
+
+static ssize_t f_ss_opts_isoc_interval_store(struct f_ss_opts *opts,
+                                      const char *page, size_t len)
+{
+       int ret;
+       u8 num;
+
+       mutex_lock(&opts->lock);
+       if (opts->refcnt) {
+               ret = -EBUSY;
+               goto end;
+       }
+
+       ret = kstrtou8(page, 0, &num);
+       if (ret)
+               goto end;
+
+       if (num > 16) {
+               ret = -EINVAL;
+               goto end;
+       }
+
+       opts->isoc_interval = num;
+       ret = len;
+end:
+       mutex_unlock(&opts->lock);
+       return ret;
+}
+
+static struct f_ss_opts_attribute f_ss_opts_isoc_interval =
+       __CONFIGFS_ATTR(isoc_interval, S_IRUGO | S_IWUSR,
+                       f_ss_opts_isoc_interval_show,
+                       f_ss_opts_isoc_interval_store);
+
+static ssize_t f_ss_opts_isoc_maxpacket_show(struct f_ss_opts *opts, char *page)
+{
+       int result;
+
+       mutex_lock(&opts->lock);
+       result = sprintf(page, "%d", opts->isoc_maxpacket);
+       mutex_unlock(&opts->lock);
+
+       return result;
+}
+
+static ssize_t f_ss_opts_isoc_maxpacket_store(struct f_ss_opts *opts,
+                                      const char *page, size_t len)
+{
+       int ret;
+       u16 num;
+
+       mutex_lock(&opts->lock);
+       if (opts->refcnt) {
+               ret = -EBUSY;
+               goto end;
+       }
+
+       ret = kstrtou16(page, 0, &num);
+       if (ret)
+               goto end;
+
+       if (num > 1024) {
+               ret = -EINVAL;
+               goto end;
+       }
+
+       opts->isoc_maxpacket = num;
+       ret = len;
+end:
+       mutex_unlock(&opts->lock);
+       return ret;
+}
+
+static struct f_ss_opts_attribute f_ss_opts_isoc_maxpacket =
+       __CONFIGFS_ATTR(isoc_maxpacket, S_IRUGO | S_IWUSR,
+                       f_ss_opts_isoc_maxpacket_show,
+                       f_ss_opts_isoc_maxpacket_store);
+
+static ssize_t f_ss_opts_isoc_mult_show(struct f_ss_opts *opts, char *page)
+{
+       int result;
+
+       mutex_lock(&opts->lock);
+       result = sprintf(page, "%d", opts->isoc_mult);
+       mutex_unlock(&opts->lock);
+
+       return result;
+}
+
+static ssize_t f_ss_opts_isoc_mult_store(struct f_ss_opts *opts,
+                                      const char *page, size_t len)
+{
+       int ret;
+       u8 num;
+
+       mutex_lock(&opts->lock);
+       if (opts->refcnt) {
+               ret = -EBUSY;
+               goto end;
+       }
+
+       ret = kstrtou8(page, 0, &num);
+       if (ret)
+               goto end;
+
+       if (num > 2) {
+               ret = -EINVAL;
+               goto end;
+       }
+
+       opts->isoc_mult = num;
+       ret = len;
+end:
+       mutex_unlock(&opts->lock);
+       return ret;
+}
+
+static struct f_ss_opts_attribute f_ss_opts_isoc_mult =
+       __CONFIGFS_ATTR(isoc_mult, S_IRUGO | S_IWUSR,
+                       f_ss_opts_isoc_mult_show,
+                       f_ss_opts_isoc_mult_store);
+
+static ssize_t f_ss_opts_isoc_maxburst_show(struct f_ss_opts *opts, char *page)
+{
+       int result;
+
+       mutex_lock(&opts->lock);
+       result = sprintf(page, "%d", opts->isoc_maxburst);
+       mutex_unlock(&opts->lock);
+
+       return result;
+}
+
+static ssize_t f_ss_opts_isoc_maxburst_store(struct f_ss_opts *opts,
+                                      const char *page, size_t len)
+{
+       int ret;
+       u8 num;
+
+       mutex_lock(&opts->lock);
+       if (opts->refcnt) {
+               ret = -EBUSY;
+               goto end;
+       }
+
+       ret = kstrtou8(page, 0, &num);
+       if (ret)
+               goto end;
+
+       if (num > 15) {
+               ret = -EINVAL;
+               goto end;
+       }
+
+       opts->isoc_maxburst = num;
+       ret = len;
+end:
+       mutex_unlock(&opts->lock);
+       return ret;
+}
+
+static struct f_ss_opts_attribute f_ss_opts_isoc_maxburst =
+       __CONFIGFS_ATTR(isoc_maxburst, S_IRUGO | S_IWUSR,
+                       f_ss_opts_isoc_maxburst_show,
+                       f_ss_opts_isoc_maxburst_store);
+
+static ssize_t f_ss_opts_bulk_buflen_show(struct f_ss_opts *opts, char *page)
+{
+       int result;
+
+       mutex_lock(&opts->lock);
+       result = sprintf(page, "%d", opts->bulk_buflen);
+       mutex_unlock(&opts->lock);
+
+       return result;
+}
+
+static ssize_t f_ss_opts_bulk_buflen_store(struct f_ss_opts *opts,
+                                          const char *page, size_t len)
+{
+       int ret;
+       u32 num;
+
+       mutex_lock(&opts->lock);
+       if (opts->refcnt) {
+               ret = -EBUSY;
+               goto end;
+       }
+
+       ret = kstrtou32(page, 0, &num);
+       if (ret)
+               goto end;
+
+       opts->bulk_buflen = num;
+       ret = len;
+end:
+       mutex_unlock(&opts->lock);
+       return ret;
+}
+
+static struct f_ss_opts_attribute f_ss_opts_bulk_buflen =
+       __CONFIGFS_ATTR(buflen, S_IRUGO | S_IWUSR,
+                       f_ss_opts_bulk_buflen_show,
+                       f_ss_opts_bulk_buflen_store);
+
+static struct configfs_attribute *ss_attrs[] = {
+       &f_ss_opts_pattern.attr,
+       &f_ss_opts_isoc_interval.attr,
+       &f_ss_opts_isoc_maxpacket.attr,
+       &f_ss_opts_isoc_mult.attr,
+       &f_ss_opts_isoc_maxburst.attr,
+       &f_ss_opts_bulk_buflen.attr,
+       NULL,
+};
+
+static struct config_item_type ss_func_type = {
+       .ct_item_ops    = &ss_item_ops,
+       .ct_attrs       = ss_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+static void source_sink_free_instance(struct usb_function_instance *fi)
+{
+       struct f_ss_opts *ss_opts;
+
+       ss_opts = container_of(fi, struct f_ss_opts, func_inst);
+       kfree(ss_opts);
+}
+
+static struct usb_function_instance *source_sink_alloc_inst(void)
+{
+       struct f_ss_opts *ss_opts;
+
+       ss_opts = kzalloc(sizeof(*ss_opts), GFP_KERNEL);
+       if (!ss_opts)
+               return ERR_PTR(-ENOMEM);
+       mutex_init(&ss_opts->lock);
+       ss_opts->func_inst.free_func_inst = source_sink_free_instance;
+       ss_opts->isoc_interval = GZERO_ISOC_INTERVAL;
+       ss_opts->isoc_maxpacket = GZERO_ISOC_MAXPACKET;
+       ss_opts->bulk_buflen = GZERO_BULK_BUFLEN;
+
+       config_group_init_type_name(&ss_opts->func_inst.group, "",
+                                   &ss_func_type);
+
+       return &ss_opts->func_inst;
+}
+DECLARE_USB_FUNCTION(SourceSink, source_sink_alloc_inst,
+               source_sink_alloc_func);
+
+static int __init sslb_modinit(void)
+{
+       int ret;
+
+       ret = usb_function_register(&SourceSinkusb_func);
+       if (ret)
+               return ret;
+       ret = lb_modinit();
+       if (ret)
+               usb_function_unregister(&SourceSinkusb_func);
+       return ret;
+}
+static void __exit sslb_modexit(void)
+{
+       usb_function_unregister(&SourceSinkusb_func);
+       lb_modexit();
+}
+module_init(sslb_modinit);
+module_exit(sslb_modexit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/f_subset.c b/drivers/usb/gadget/function/f_subset.c
new file mode 100644 (file)
index 0000000..1ea8baf
--- /dev/null
@@ -0,0 +1,519 @@
+/*
+ * f_subset.c -- "CDC Subset" Ethernet link function driver
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * 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.
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/etherdevice.h>
+
+#include "u_ether.h"
+#include "u_ether_configfs.h"
+#include "u_gether.h"
+
+/*
+ * This function packages a simple "CDC Subset" Ethernet port with no real
+ * control mechanisms; just raw data transfer over two bulk endpoints.
+ * The data transfer model is exactly that of CDC Ethernet, which is
+ * why we call it the "CDC Subset".
+ *
+ * Because it's not standardized, this has some interoperability issues.
+ * They mostly relate to driver binding, since the data transfer model is
+ * so simple (CDC Ethernet).  The original versions of this protocol used
+ * specific product/vendor IDs:  byteswapped IDs for Digital Equipment's
+ * SA-1100 "Itsy" board, which could run Linux 2.4 kernels and supported
+ * daughtercards with USB peripheral connectors.  (It was used more often
+ * with other boards, using the Itsy identifiers.)  Linux hosts recognized
+ * this with CONFIG_USB_ARMLINUX; these devices have only one configuration
+ * and one interface.
+ *
+ * At some point, MCCI defined a (nonconformant) CDC MDLM variant called
+ * "SAFE", which happens to have a mode which is identical to the "CDC
+ * Subset" in terms of data transfer and lack of control model.  This was
+ * adopted by later Sharp Zaurus models, and by some other software which
+ * Linux hosts recognize with CONFIG_USB_NET_ZAURUS.
+ *
+ * Because Microsoft's RNDIS drivers are far from robust, we added a few
+ * descriptors to the CDC Subset code, making this code look like a SAFE
+ * implementation.  This lets you use MCCI's host side MS-Windows drivers
+ * if you get fed up with RNDIS.  It also makes it easier for composite
+ * drivers to work, since they can use class based binding instead of
+ * caring about specific product and vendor IDs.
+ */
+
+struct f_gether {
+       struct gether                   port;
+
+       char                            ethaddr[14];
+};
+
+static inline struct f_gether *func_to_geth(struct usb_function *f)
+{
+       return container_of(f, struct f_gether, port.func);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * "Simple" CDC-subset option is a simple vendor-neutral model that most
+ * full speed controllers can handle:  one interface, two bulk endpoints.
+ * To assist host side drivers, we fancy it up a bit, and add descriptors so
+ * some host side drivers will understand it as a "SAFE" variant.
+ *
+ * "SAFE" loosely follows CDC WMC MDLM, violating the spec in various ways.
+ * Data endpoints live in the control interface, there's no data interface.
+ * And it's not used to talk to a cell phone radio.
+ */
+
+/* interface descriptor: */
+
+static struct usb_interface_descriptor subset_data_intf = {
+       .bLength =              sizeof subset_data_intf,
+       .bDescriptorType =      USB_DT_INTERFACE,
+
+       /* .bInterfaceNumber = DYNAMIC */
+       .bAlternateSetting =    0,
+       .bNumEndpoints =        2,
+       .bInterfaceClass =      USB_CLASS_COMM,
+       .bInterfaceSubClass =   USB_CDC_SUBCLASS_MDLM,
+       .bInterfaceProtocol =   0,
+       /* .iInterface = DYNAMIC */
+};
+
+static struct usb_cdc_header_desc mdlm_header_desc = {
+       .bLength =              sizeof mdlm_header_desc,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubType =   USB_CDC_HEADER_TYPE,
+
+       .bcdCDC =               cpu_to_le16(0x0110),
+};
+
+static struct usb_cdc_mdlm_desc mdlm_desc = {
+       .bLength =              sizeof mdlm_desc,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubType =   USB_CDC_MDLM_TYPE,
+
+       .bcdVersion =           cpu_to_le16(0x0100),
+       .bGUID = {
+               0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6,
+               0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f,
+       },
+};
+
+/* since "usb_cdc_mdlm_detail_desc" is a variable length structure, we
+ * can't really use its struct.  All we do here is say that we're using
+ * the submode of "SAFE" which directly matches the CDC Subset.
+ */
+static u8 mdlm_detail_desc[] = {
+       6,
+       USB_DT_CS_INTERFACE,
+       USB_CDC_MDLM_DETAIL_TYPE,
+
+       0,      /* "SAFE" */
+       0,      /* network control capabilities (none) */
+       0,      /* network data capabilities ("raw" encapsulation) */
+};
+
+static struct usb_cdc_ether_desc ether_desc = {
+       .bLength =              sizeof ether_desc,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubType =   USB_CDC_ETHERNET_TYPE,
+
+       /* this descriptor actually adds value, surprise! */
+       /* .iMACAddress = DYNAMIC */
+       .bmEthernetStatistics = cpu_to_le32(0), /* no statistics */
+       .wMaxSegmentSize =      cpu_to_le16(ETH_FRAME_LEN),
+       .wNumberMCFilters =     cpu_to_le16(0),
+       .bNumberPowerFilters =  0,
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor fs_subset_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor fs_subset_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *fs_eth_function[] = {
+       (struct usb_descriptor_header *) &subset_data_intf,
+       (struct usb_descriptor_header *) &mdlm_header_desc,
+       (struct usb_descriptor_header *) &mdlm_desc,
+       (struct usb_descriptor_header *) &mdlm_detail_desc,
+       (struct usb_descriptor_header *) &ether_desc,
+       (struct usb_descriptor_header *) &fs_subset_in_desc,
+       (struct usb_descriptor_header *) &fs_subset_out_desc,
+       NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor hs_subset_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor hs_subset_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *hs_eth_function[] = {
+       (struct usb_descriptor_header *) &subset_data_intf,
+       (struct usb_descriptor_header *) &mdlm_header_desc,
+       (struct usb_descriptor_header *) &mdlm_desc,
+       (struct usb_descriptor_header *) &mdlm_detail_desc,
+       (struct usb_descriptor_header *) &ether_desc,
+       (struct usb_descriptor_header *) &hs_subset_in_desc,
+       (struct usb_descriptor_header *) &hs_subset_out_desc,
+       NULL,
+};
+
+/* super speed support: */
+
+static struct usb_endpoint_descriptor ss_subset_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor ss_subset_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ss_subset_bulk_comp_desc = {
+       .bLength =              sizeof ss_subset_bulk_comp_desc,
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+
+       /* the following 2 values can be tweaked if necessary */
+       /* .bMaxBurst =         0, */
+       /* .bmAttributes =      0, */
+};
+
+static struct usb_descriptor_header *ss_eth_function[] = {
+       (struct usb_descriptor_header *) &subset_data_intf,
+       (struct usb_descriptor_header *) &mdlm_header_desc,
+       (struct usb_descriptor_header *) &mdlm_desc,
+       (struct usb_descriptor_header *) &mdlm_detail_desc,
+       (struct usb_descriptor_header *) &ether_desc,
+       (struct usb_descriptor_header *) &ss_subset_in_desc,
+       (struct usb_descriptor_header *) &ss_subset_bulk_comp_desc,
+       (struct usb_descriptor_header *) &ss_subset_out_desc,
+       (struct usb_descriptor_header *) &ss_subset_bulk_comp_desc,
+       NULL,
+};
+
+/* string descriptors: */
+
+static struct usb_string geth_string_defs[] = {
+       [0].s = "CDC Ethernet Subset/SAFE",
+       [1].s = "",
+       {  } /* end of list */
+};
+
+static struct usb_gadget_strings geth_string_table = {
+       .language =             0x0409, /* en-us */
+       .strings =              geth_string_defs,
+};
+
+static struct usb_gadget_strings *geth_strings[] = {
+       &geth_string_table,
+       NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int geth_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+       struct f_gether         *geth = func_to_geth(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+       struct net_device       *net;
+
+       /* we know alt == 0, so this is an activation or a reset */
+
+       if (geth->port.in_ep->driver_data) {
+               DBG(cdev, "reset cdc subset\n");
+               gether_disconnect(&geth->port);
+       }
+
+       DBG(cdev, "init + activate cdc subset\n");
+       if (config_ep_by_speed(cdev->gadget, f, geth->port.in_ep) ||
+           config_ep_by_speed(cdev->gadget, f, geth->port.out_ep)) {
+               geth->port.in_ep->desc = NULL;
+               geth->port.out_ep->desc = NULL;
+               return -EINVAL;
+       }
+
+       net = gether_connect(&geth->port);
+       return PTR_ERR_OR_ZERO(net);
+}
+
+static void geth_disable(struct usb_function *f)
+{
+       struct f_gether *geth = func_to_geth(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+
+       DBG(cdev, "net deactivated\n");
+       gether_disconnect(&geth->port);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* serial function driver setup/binding */
+
+static int
+geth_bind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct usb_composite_dev *cdev = c->cdev;
+       struct f_gether         *geth = func_to_geth(f);
+       struct usb_string       *us;
+       int                     status;
+       struct usb_ep           *ep;
+
+       struct f_gether_opts    *gether_opts;
+
+       gether_opts = container_of(f->fi, struct f_gether_opts, func_inst);
+
+       /*
+        * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
+        * configurations are bound in sequence with list_for_each_entry,
+        * in each configuration its functions are bound in sequence
+        * with list_for_each_entry, so we assume no race condition
+        * with regard to gether_opts->bound access
+        */
+       if (!gether_opts->bound) {
+               mutex_lock(&gether_opts->lock);
+               gether_set_gadget(gether_opts->net, cdev->gadget);
+               status = gether_register_netdev(gether_opts->net);
+               mutex_unlock(&gether_opts->lock);
+               if (status)
+                       return status;
+               gether_opts->bound = true;
+       }
+
+       us = usb_gstrings_attach(cdev, geth_strings,
+                                ARRAY_SIZE(geth_string_defs));
+       if (IS_ERR(us))
+               return PTR_ERR(us);
+
+       subset_data_intf.iInterface = us[0].id;
+       ether_desc.iMACAddress = us[1].id;
+
+       /* allocate instance-specific interface IDs */
+       status = usb_interface_id(c, f);
+       if (status < 0)
+               goto fail;
+       subset_data_intf.bInterfaceNumber = status;
+
+       status = -ENODEV;
+
+       /* allocate instance-specific endpoints */
+       ep = usb_ep_autoconfig(cdev->gadget, &fs_subset_in_desc);
+       if (!ep)
+               goto fail;
+       geth->port.in_ep = ep;
+       ep->driver_data = cdev; /* claim */
+
+       ep = usb_ep_autoconfig(cdev->gadget, &fs_subset_out_desc);
+       if (!ep)
+               goto fail;
+       geth->port.out_ep = ep;
+       ep->driver_data = cdev; /* claim */
+
+       /* support all relevant hardware speeds... we expect that when
+        * hardware is dual speed, all bulk-capable endpoints work at
+        * both speeds
+        */
+       hs_subset_in_desc.bEndpointAddress = fs_subset_in_desc.bEndpointAddress;
+       hs_subset_out_desc.bEndpointAddress =
+               fs_subset_out_desc.bEndpointAddress;
+
+       ss_subset_in_desc.bEndpointAddress = fs_subset_in_desc.bEndpointAddress;
+       ss_subset_out_desc.bEndpointAddress =
+               fs_subset_out_desc.bEndpointAddress;
+
+       status = usb_assign_descriptors(f, fs_eth_function, hs_eth_function,
+                       ss_eth_function);
+       if (status)
+               goto fail;
+
+       /* NOTE:  all that is done without knowing or caring about
+        * the network link ... which is unavailable to this code
+        * until we're activated via set_alt().
+        */
+
+       DBG(cdev, "CDC Subset: %s speed IN/%s OUT/%s\n",
+                       gadget_is_superspeed(c->cdev->gadget) ? "super" :
+                       gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+                       geth->port.in_ep->name, geth->port.out_ep->name);
+       return 0;
+
+fail:
+       usb_free_all_descriptors(f);
+       /* we might as well release our claims on endpoints */
+       if (geth->port.out_ep)
+               geth->port.out_ep->driver_data = NULL;
+       if (geth->port.in_ep)
+               geth->port.in_ep->driver_data = NULL;
+
+       ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
+
+       return status;
+}
+
+static inline struct f_gether_opts *to_f_gether_opts(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct f_gether_opts,
+                           func_inst.group);
+}
+
+/* f_gether_item_ops */
+USB_ETHERNET_CONFIGFS_ITEM(gether);
+
+/* f_gether_opts_dev_addr */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(gether);
+
+/* f_gether_opts_host_addr */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(gether);
+
+/* f_gether_opts_qmult */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(gether);
+
+/* f_gether_opts_ifname */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(gether);
+
+static struct configfs_attribute *gether_attrs[] = {
+       &f_gether_opts_dev_addr.attr,
+       &f_gether_opts_host_addr.attr,
+       &f_gether_opts_qmult.attr,
+       &f_gether_opts_ifname.attr,
+       NULL,
+};
+
+static struct config_item_type gether_func_type = {
+       .ct_item_ops    = &gether_item_ops,
+       .ct_attrs       = gether_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+static void geth_free_inst(struct usb_function_instance *f)
+{
+       struct f_gether_opts *opts;
+
+       opts = container_of(f, struct f_gether_opts, func_inst);
+       if (opts->bound)
+               gether_cleanup(netdev_priv(opts->net));
+       else
+               free_netdev(opts->net);
+       kfree(opts);
+}
+
+static struct usb_function_instance *geth_alloc_inst(void)
+{
+       struct f_gether_opts *opts;
+
+       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+       if (!opts)
+               return ERR_PTR(-ENOMEM);
+       mutex_init(&opts->lock);
+       opts->func_inst.free_func_inst = geth_free_inst;
+       opts->net = gether_setup_default();
+       if (IS_ERR(opts->net)) {
+               struct net_device *net = opts->net;
+               kfree(opts);
+               return ERR_CAST(net);
+       }
+
+       config_group_init_type_name(&opts->func_inst.group, "",
+                                   &gether_func_type);
+
+       return &opts->func_inst;
+}
+
+static void geth_free(struct usb_function *f)
+{
+       struct f_gether *eth;
+
+       eth = func_to_geth(f);
+       kfree(eth);
+}
+
+static void geth_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+       geth_string_defs[0].id = 0;
+       usb_free_all_descriptors(f);
+}
+
+static struct usb_function *geth_alloc(struct usb_function_instance *fi)
+{
+       struct f_gether *geth;
+       struct f_gether_opts *opts;
+       int status;
+
+       /* allocate and initialize one new instance */
+       geth = kzalloc(sizeof(*geth), GFP_KERNEL);
+       if (!geth)
+               return ERR_PTR(-ENOMEM);
+
+       opts = container_of(fi, struct f_gether_opts, func_inst);
+
+       mutex_lock(&opts->lock);
+       opts->refcnt++;
+       /* export host's Ethernet address in CDC format */
+       status = gether_get_host_addr_cdc(opts->net, geth->ethaddr,
+                                         sizeof(geth->ethaddr));
+       if (status < 12) {
+               kfree(geth);
+               mutex_unlock(&opts->lock);
+               return ERR_PTR(-EINVAL);
+       }
+       geth_string_defs[1].s = geth->ethaddr;
+
+       geth->port.ioport = netdev_priv(opts->net);
+       mutex_unlock(&opts->lock);
+       geth->port.cdc_filter = DEFAULT_FILTER;
+
+       geth->port.func.name = "cdc_subset";
+       geth->port.func.bind = geth_bind;
+       geth->port.func.unbind = geth_unbind;
+       geth->port.func.set_alt = geth_set_alt;
+       geth->port.func.disable = geth_disable;
+       geth->port.func.free_func = geth_free;
+
+       return &geth->port.func;
+}
+
+DECLARE_USB_FUNCTION_INIT(geth, geth_alloc_inst, geth_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
new file mode 100644 (file)
index 0000000..2b4c82d
--- /dev/null
@@ -0,0 +1,768 @@
+/*
+ * f_audio.c -- USB Audio class function driver
+  *
+ * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
+ * Copyright (C) 2008 Analog Devices, Inc
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/atomic.h>
+
+#include "u_uac1.h"
+
+#define OUT_EP_MAX_PACKET_SIZE 200
+static int req_buf_size = OUT_EP_MAX_PACKET_SIZE;
+module_param(req_buf_size, int, S_IRUGO);
+MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size");
+
+static int req_count = 256;
+module_param(req_count, int, S_IRUGO);
+MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count");
+
+static int audio_buf_size = 48000;
+module_param(audio_buf_size, int, S_IRUGO);
+MODULE_PARM_DESC(audio_buf_size, "Audio buffer size");
+
+static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value);
+static int generic_get_cmd(struct usb_audio_control *con, u8 cmd);
+
+/*
+ * DESCRIPTORS ... most are static, but strings and full
+ * configuration descriptors are built on demand.
+ */
+
+/*
+ * We have two interfaces- AudioControl and AudioStreaming
+ * TODO: only supcard playback currently
+ */
+#define F_AUDIO_AC_INTERFACE   0
+#define F_AUDIO_AS_INTERFACE   1
+#define F_AUDIO_NUM_INTERFACES 2
+
+/* B.3.1  Standard AC Interface Descriptor */
+static struct usb_interface_descriptor ac_interface_desc __initdata = {
+       .bLength =              USB_DT_INTERFACE_SIZE,
+       .bDescriptorType =      USB_DT_INTERFACE,
+       .bNumEndpoints =        0,
+       .bInterfaceClass =      USB_CLASS_AUDIO,
+       .bInterfaceSubClass =   USB_SUBCLASS_AUDIOCONTROL,
+};
+
+DECLARE_UAC_AC_HEADER_DESCRIPTOR(2);
+
+#define UAC_DT_AC_HEADER_LENGTH        UAC_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES)
+/* 1 input terminal, 1 output terminal and 1 feature unit */
+#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH + UAC_DT_INPUT_TERMINAL_SIZE \
+       + UAC_DT_OUTPUT_TERMINAL_SIZE + UAC_DT_FEATURE_UNIT_SIZE(0))
+/* B.3.2  Class-Specific AC Interface Descriptor */
+static struct uac1_ac_header_descriptor_2 ac_header_desc = {
+       .bLength =              UAC_DT_AC_HEADER_LENGTH,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype =   UAC_HEADER,
+       .bcdADC =               __constant_cpu_to_le16(0x0100),
+       .wTotalLength =         __constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH),
+       .bInCollection =        F_AUDIO_NUM_INTERFACES,
+       .baInterfaceNr = {
+               [0] =           F_AUDIO_AC_INTERFACE,
+               [1] =           F_AUDIO_AS_INTERFACE,
+       }
+};
+
+#define INPUT_TERMINAL_ID      1
+static struct uac_input_terminal_descriptor input_terminal_desc = {
+       .bLength =              UAC_DT_INPUT_TERMINAL_SIZE,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype =   UAC_INPUT_TERMINAL,
+       .bTerminalID =          INPUT_TERMINAL_ID,
+       .wTerminalType =        UAC_TERMINAL_STREAMING,
+       .bAssocTerminal =       0,
+       .wChannelConfig =       0x3,
+};
+
+DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0);
+
+#define FEATURE_UNIT_ID                2
+static struct uac_feature_unit_descriptor_0 feature_unit_desc = {
+       .bLength                = UAC_DT_FEATURE_UNIT_SIZE(0),
+       .bDescriptorType        = USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype     = UAC_FEATURE_UNIT,
+       .bUnitID                = FEATURE_UNIT_ID,
+       .bSourceID              = INPUT_TERMINAL_ID,
+       .bControlSize           = 2,
+       .bmaControls[0]         = (UAC_FU_MUTE | UAC_FU_VOLUME),
+};
+
+static struct usb_audio_control mute_control = {
+       .list = LIST_HEAD_INIT(mute_control.list),
+       .name = "Mute Control",
+       .type = UAC_FU_MUTE,
+       /* Todo: add real Mute control code */
+       .set = generic_set_cmd,
+       .get = generic_get_cmd,
+};
+
+static struct usb_audio_control volume_control = {
+       .list = LIST_HEAD_INIT(volume_control.list),
+       .name = "Volume Control",
+       .type = UAC_FU_VOLUME,
+       /* Todo: add real Volume control code */
+       .set = generic_set_cmd,
+       .get = generic_get_cmd,
+};
+
+static struct usb_audio_control_selector feature_unit = {
+       .list = LIST_HEAD_INIT(feature_unit.list),
+       .id = FEATURE_UNIT_ID,
+       .name = "Mute & Volume Control",
+       .type = UAC_FEATURE_UNIT,
+       .desc = (struct usb_descriptor_header *)&feature_unit_desc,
+};
+
+#define OUTPUT_TERMINAL_ID     3
+static struct uac1_output_terminal_descriptor output_terminal_desc = {
+       .bLength                = UAC_DT_OUTPUT_TERMINAL_SIZE,
+       .bDescriptorType        = USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype     = UAC_OUTPUT_TERMINAL,
+       .bTerminalID            = OUTPUT_TERMINAL_ID,
+       .wTerminalType          = UAC_OUTPUT_TERMINAL_SPEAKER,
+       .bAssocTerminal         = FEATURE_UNIT_ID,
+       .bSourceID              = FEATURE_UNIT_ID,
+};
+
+/* B.4.1  Standard AS Interface Descriptor */
+static struct usb_interface_descriptor as_interface_alt_0_desc = {
+       .bLength =              USB_DT_INTERFACE_SIZE,
+       .bDescriptorType =      USB_DT_INTERFACE,
+       .bAlternateSetting =    0,
+       .bNumEndpoints =        0,
+       .bInterfaceClass =      USB_CLASS_AUDIO,
+       .bInterfaceSubClass =   USB_SUBCLASS_AUDIOSTREAMING,
+};
+
+static struct usb_interface_descriptor as_interface_alt_1_desc = {
+       .bLength =              USB_DT_INTERFACE_SIZE,
+       .bDescriptorType =      USB_DT_INTERFACE,
+       .bAlternateSetting =    1,
+       .bNumEndpoints =        1,
+       .bInterfaceClass =      USB_CLASS_AUDIO,
+       .bInterfaceSubClass =   USB_SUBCLASS_AUDIOSTREAMING,
+};
+
+/* B.4.2  Class-Specific AS Interface Descriptor */
+static struct uac1_as_header_descriptor as_header_desc = {
+       .bLength =              UAC_DT_AS_HEADER_SIZE,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype =   UAC_AS_GENERAL,
+       .bTerminalLink =        INPUT_TERMINAL_ID,
+       .bDelay =               1,
+       .wFormatTag =           UAC_FORMAT_TYPE_I_PCM,
+};
+
+DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1);
+
+static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = {
+       .bLength =              UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype =   UAC_FORMAT_TYPE,
+       .bFormatType =          UAC_FORMAT_TYPE_I,
+       .bSubframeSize =        2,
+       .bBitResolution =       16,
+       .bSamFreqType =         1,
+};
+
+/* Standard ISO OUT Endpoint Descriptor */
+static struct usb_endpoint_descriptor as_out_ep_desc  = {
+       .bLength =              USB_DT_ENDPOINT_AUDIO_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_SYNC_ADAPTIVE
+                               | USB_ENDPOINT_XFER_ISOC,
+       .wMaxPacketSize =       __constant_cpu_to_le16(OUT_EP_MAX_PACKET_SIZE),
+       .bInterval =            4,
+};
+
+/* Class-specific AS ISO OUT Endpoint Descriptor */
+static struct uac_iso_endpoint_descriptor as_iso_out_desc __initdata = {
+       .bLength =              UAC_ISO_ENDPOINT_DESC_SIZE,
+       .bDescriptorType =      USB_DT_CS_ENDPOINT,
+       .bDescriptorSubtype =   UAC_EP_GENERAL,
+       .bmAttributes =         1,
+       .bLockDelayUnits =      1,
+       .wLockDelay =           __constant_cpu_to_le16(1),
+};
+
+static struct usb_descriptor_header *f_audio_desc[] __initdata = {
+       (struct usb_descriptor_header *)&ac_interface_desc,
+       (struct usb_descriptor_header *)&ac_header_desc,
+
+       (struct usb_descriptor_header *)&input_terminal_desc,
+       (struct usb_descriptor_header *)&output_terminal_desc,
+       (struct usb_descriptor_header *)&feature_unit_desc,
+
+       (struct usb_descriptor_header *)&as_interface_alt_0_desc,
+       (struct usb_descriptor_header *)&as_interface_alt_1_desc,
+       (struct usb_descriptor_header *)&as_header_desc,
+
+       (struct usb_descriptor_header *)&as_type_i_desc,
+
+       (struct usb_descriptor_header *)&as_out_ep_desc,
+       (struct usb_descriptor_header *)&as_iso_out_desc,
+       NULL,
+};
+
+/*
+ * This function is an ALSA sound card following USB Audio Class Spec 1.0.
+ */
+
+/*-------------------------------------------------------------------------*/
+struct f_audio_buf {
+       u8 *buf;
+       int actual;
+       struct list_head list;
+};
+
+static struct f_audio_buf *f_audio_buffer_alloc(int buf_size)
+{
+       struct f_audio_buf *copy_buf;
+
+       copy_buf = kzalloc(sizeof *copy_buf, GFP_ATOMIC);
+       if (!copy_buf)
+               return ERR_PTR(-ENOMEM);
+
+       copy_buf->buf = kzalloc(buf_size, GFP_ATOMIC);
+       if (!copy_buf->buf) {
+               kfree(copy_buf);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       return copy_buf;
+}
+
+static void f_audio_buffer_free(struct f_audio_buf *audio_buf)
+{
+       kfree(audio_buf->buf);
+       kfree(audio_buf);
+}
+/*-------------------------------------------------------------------------*/
+
+struct f_audio {
+       struct gaudio                   card;
+
+       /* endpoints handle full and/or high speeds */
+       struct usb_ep                   *out_ep;
+
+       spinlock_t                      lock;
+       struct f_audio_buf *copy_buf;
+       struct work_struct playback_work;
+       struct list_head play_queue;
+
+       /* Control Set command */
+       struct list_head cs;
+       u8 set_cmd;
+       struct usb_audio_control *set_con;
+};
+
+static inline struct f_audio *func_to_audio(struct usb_function *f)
+{
+       return container_of(f, struct f_audio, card.func);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void f_audio_playback_work(struct work_struct *data)
+{
+       struct f_audio *audio = container_of(data, struct f_audio,
+                                       playback_work);
+       struct f_audio_buf *play_buf;
+
+       spin_lock_irq(&audio->lock);
+       if (list_empty(&audio->play_queue)) {
+               spin_unlock_irq(&audio->lock);
+               return;
+       }
+       play_buf = list_first_entry(&audio->play_queue,
+                       struct f_audio_buf, list);
+       list_del(&play_buf->list);
+       spin_unlock_irq(&audio->lock);
+
+       u_audio_playback(&audio->card, play_buf->buf, play_buf->actual);
+       f_audio_buffer_free(play_buf);
+}
+
+static int f_audio_out_ep_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct f_audio *audio = req->context;
+       struct usb_composite_dev *cdev = audio->card.func.config->cdev;
+       struct f_audio_buf *copy_buf = audio->copy_buf;
+       int err;
+
+       if (!copy_buf)
+               return -EINVAL;
+
+       /* Copy buffer is full, add it to the play_queue */
+       if (audio_buf_size - copy_buf->actual < req->actual) {
+               list_add_tail(&copy_buf->list, &audio->play_queue);
+               schedule_work(&audio->playback_work);
+               copy_buf = f_audio_buffer_alloc(audio_buf_size);
+               if (IS_ERR(copy_buf))
+                       return -ENOMEM;
+       }
+
+       memcpy(copy_buf->buf + copy_buf->actual, req->buf, req->actual);
+       copy_buf->actual += req->actual;
+       audio->copy_buf = copy_buf;
+
+       err = usb_ep_queue(ep, req, GFP_ATOMIC);
+       if (err)
+               ERROR(cdev, "%s queue req: %d\n", ep->name, err);
+
+       return 0;
+
+}
+
+static void f_audio_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct f_audio *audio = req->context;
+       int status = req->status;
+       u32 data = 0;
+       struct usb_ep *out_ep = audio->out_ep;
+
+       switch (status) {
+
+       case 0:                         /* normal completion? */
+               if (ep == out_ep)
+                       f_audio_out_ep_complete(ep, req);
+               else if (audio->set_con) {
+                       memcpy(&data, req->buf, req->length);
+                       audio->set_con->set(audio->set_con, audio->set_cmd,
+                                       le16_to_cpu(data));
+                       audio->set_con = NULL;
+               }
+               break;
+       default:
+               break;
+       }
+}
+
+static int audio_set_intf_req(struct usb_function *f,
+               const struct usb_ctrlrequest *ctrl)
+{
+       struct f_audio          *audio = func_to_audio(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+       struct usb_request      *req = cdev->req;
+       u8                      id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
+       u16                     len = le16_to_cpu(ctrl->wLength);
+       u16                     w_value = le16_to_cpu(ctrl->wValue);
+       u8                      con_sel = (w_value >> 8) & 0xFF;
+       u8                      cmd = (ctrl->bRequest & 0x0F);
+       struct usb_audio_control_selector *cs;
+       struct usb_audio_control *con;
+
+       DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n",
+                       ctrl->bRequest, w_value, len, id);
+
+       list_for_each_entry(cs, &audio->cs, list) {
+               if (cs->id == id) {
+                       list_for_each_entry(con, &cs->control, list) {
+                               if (con->type == con_sel) {
+                                       audio->set_con = con;
+                                       break;
+                               }
+                       }
+                       break;
+               }
+       }
+
+       audio->set_cmd = cmd;
+       req->context = audio;
+       req->complete = f_audio_complete;
+
+       return len;
+}
+
+static int audio_get_intf_req(struct usb_function *f,
+               const struct usb_ctrlrequest *ctrl)
+{
+       struct f_audio          *audio = func_to_audio(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+       struct usb_request      *req = cdev->req;
+       int                     value = -EOPNOTSUPP;
+       u8                      id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
+       u16                     len = le16_to_cpu(ctrl->wLength);
+       u16                     w_value = le16_to_cpu(ctrl->wValue);
+       u8                      con_sel = (w_value >> 8) & 0xFF;
+       u8                      cmd = (ctrl->bRequest & 0x0F);
+       struct usb_audio_control_selector *cs;
+       struct usb_audio_control *con;
+
+       DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n",
+                       ctrl->bRequest, w_value, len, id);
+
+       list_for_each_entry(cs, &audio->cs, list) {
+               if (cs->id == id) {
+                       list_for_each_entry(con, &cs->control, list) {
+                               if (con->type == con_sel && con->get) {
+                                       value = con->get(con, cmd);
+                                       break;
+                               }
+                       }
+                       break;
+               }
+       }
+
+       req->context = audio;
+       req->complete = f_audio_complete;
+       len = min_t(size_t, sizeof(value), len);
+       memcpy(req->buf, &value, len);
+
+       return len;
+}
+
+static int audio_set_endpoint_req(struct usb_function *f,
+               const struct usb_ctrlrequest *ctrl)
+{
+       struct usb_composite_dev *cdev = f->config->cdev;
+       int                     value = -EOPNOTSUPP;
+       u16                     ep = le16_to_cpu(ctrl->wIndex);
+       u16                     len = le16_to_cpu(ctrl->wLength);
+       u16                     w_value = le16_to_cpu(ctrl->wValue);
+
+       DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
+                       ctrl->bRequest, w_value, len, ep);
+
+       switch (ctrl->bRequest) {
+       case UAC_SET_CUR:
+               value = len;
+               break;
+
+       case UAC_SET_MIN:
+               break;
+
+       case UAC_SET_MAX:
+               break;
+
+       case UAC_SET_RES:
+               break;
+
+       case UAC_SET_MEM:
+               break;
+
+       default:
+               break;
+       }
+
+       return value;
+}
+
+static int audio_get_endpoint_req(struct usb_function *f,
+               const struct usb_ctrlrequest *ctrl)
+{
+       struct usb_composite_dev *cdev = f->config->cdev;
+       int value = -EOPNOTSUPP;
+       u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
+       u16 len = le16_to_cpu(ctrl->wLength);
+       u16 w_value = le16_to_cpu(ctrl->wValue);
+
+       DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
+                       ctrl->bRequest, w_value, len, ep);
+
+       switch (ctrl->bRequest) {
+       case UAC_GET_CUR:
+       case UAC_GET_MIN:
+       case UAC_GET_MAX:
+       case UAC_GET_RES:
+               value = len;
+               break;
+       case UAC_GET_MEM:
+               break;
+       default:
+               break;
+       }
+
+       return value;
+}
+
+static int
+f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+       struct usb_composite_dev *cdev = f->config->cdev;
+       struct usb_request      *req = cdev->req;
+       int                     value = -EOPNOTSUPP;
+       u16                     w_index = le16_to_cpu(ctrl->wIndex);
+       u16                     w_value = le16_to_cpu(ctrl->wValue);
+       u16                     w_length = le16_to_cpu(ctrl->wLength);
+
+       /* composite driver infrastructure handles everything; interface
+        * activation uses set_alt().
+        */
+       switch (ctrl->bRequestType) {
+       case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE:
+               value = audio_set_intf_req(f, ctrl);
+               break;
+
+       case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE:
+               value = audio_get_intf_req(f, ctrl);
+               break;
+
+       case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
+               value = audio_set_endpoint_req(f, ctrl);
+               break;
+
+       case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
+               value = audio_get_endpoint_req(f, ctrl);
+               break;
+
+       default:
+               ERROR(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+                       ctrl->bRequestType, ctrl->bRequest,
+                       w_value, w_index, w_length);
+       }
+
+       /* respond with data transfer or status phase? */
+       if (value >= 0) {
+               DBG(cdev, "audio req%02x.%02x v%04x i%04x l%d\n",
+                       ctrl->bRequestType, ctrl->bRequest,
+                       w_value, w_index, w_length);
+               req->zero = 0;
+               req->length = value;
+               value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+               if (value < 0)
+                       ERROR(cdev, "audio response on err %d\n", value);
+       }
+
+       /* device either stalls (value < 0) or reports success */
+       return value;
+}
+
+static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+       struct f_audio          *audio = func_to_audio(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+       struct usb_ep *out_ep = audio->out_ep;
+       struct usb_request *req;
+       int i = 0, err = 0;
+
+       DBG(cdev, "intf %d, alt %d\n", intf, alt);
+
+       if (intf == 1) {
+               if (alt == 1) {
+                       usb_ep_enable(out_ep);
+                       out_ep->driver_data = audio;
+                       audio->copy_buf = f_audio_buffer_alloc(audio_buf_size);
+                       if (IS_ERR(audio->copy_buf))
+                               return -ENOMEM;
+
+                       /*
+                        * allocate a bunch of read buffers
+                        * and queue them all at once.
+                        */
+                       for (i = 0; i < req_count && err == 0; i++) {
+                               req = usb_ep_alloc_request(out_ep, GFP_ATOMIC);
+                               if (req) {
+                                       req->buf = kzalloc(req_buf_size,
+                                                       GFP_ATOMIC);
+                                       if (req->buf) {
+                                               req->length = req_buf_size;
+                                               req->context = audio;
+                                               req->complete =
+                                                       f_audio_complete;
+                                               err = usb_ep_queue(out_ep,
+                                                       req, GFP_ATOMIC);
+                                               if (err)
+                                                       ERROR(cdev,
+                                                       "%s queue req: %d\n",
+                                                       out_ep->name, err);
+                                       } else
+                                               err = -ENOMEM;
+                               } else
+                                       err = -ENOMEM;
+                       }
+
+               } else {
+                       struct f_audio_buf *copy_buf = audio->copy_buf;
+                       if (copy_buf) {
+                               list_add_tail(&copy_buf->list,
+                                               &audio->play_queue);
+                               schedule_work(&audio->playback_work);
+                       }
+               }
+       }
+
+       return err;
+}
+
+static void f_audio_disable(struct usb_function *f)
+{
+       return;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void f_audio_build_desc(struct f_audio *audio)
+{
+       struct gaudio *card = &audio->card;
+       u8 *sam_freq;
+       int rate;
+
+       /* Set channel numbers */
+       input_terminal_desc.bNrChannels = u_audio_get_playback_channels(card);
+       as_type_i_desc.bNrChannels = u_audio_get_playback_channels(card);
+
+       /* Set sample rates */
+       rate = u_audio_get_playback_rate(card);
+       sam_freq = as_type_i_desc.tSamFreq[0];
+       memcpy(sam_freq, &rate, 3);
+
+       /* Todo: Set Sample bits and other parameters */
+
+       return;
+}
+
+/* audio function driver setup/binding */
+static int __init
+f_audio_bind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct usb_composite_dev *cdev = c->cdev;
+       struct f_audio          *audio = func_to_audio(f);
+       int                     status;
+       struct usb_ep           *ep = NULL;
+
+       f_audio_build_desc(audio);
+
+       /* allocate instance-specific interface IDs, and patch descriptors */
+       status = usb_interface_id(c, f);
+       if (status < 0)
+               goto fail;
+       ac_interface_desc.bInterfaceNumber = status;
+
+       status = usb_interface_id(c, f);
+       if (status < 0)
+               goto fail;
+       as_interface_alt_0_desc.bInterfaceNumber = status;
+       as_interface_alt_1_desc.bInterfaceNumber = status;
+
+       status = -ENODEV;
+
+       /* allocate instance-specific endpoints */
+       ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc);
+       if (!ep)
+               goto fail;
+       audio->out_ep = ep;
+       audio->out_ep->desc = &as_out_ep_desc;
+       ep->driver_data = cdev; /* claim */
+
+       status = -ENOMEM;
+
+       /* copy descriptors, and track endpoint copies */
+       status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL);
+       if (status)
+               goto fail;
+       return 0;
+
+fail:
+       if (ep)
+               ep->driver_data = NULL;
+       return status;
+}
+
+static void
+f_audio_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct f_audio          *audio = func_to_audio(f);
+
+       usb_free_all_descriptors(f);
+       kfree(audio);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value)
+{
+       con->data[cmd] = value;
+
+       return 0;
+}
+
+static int generic_get_cmd(struct usb_audio_control *con, u8 cmd)
+{
+       return con->data[cmd];
+}
+
+/* Todo: add more control selecotor dynamically */
+static int __init control_selector_init(struct f_audio *audio)
+{
+       INIT_LIST_HEAD(&audio->cs);
+       list_add(&feature_unit.list, &audio->cs);
+
+       INIT_LIST_HEAD(&feature_unit.control);
+       list_add(&mute_control.list, &feature_unit.control);
+       list_add(&volume_control.list, &feature_unit.control);
+
+       volume_control.data[UAC__CUR] = 0xffc0;
+       volume_control.data[UAC__MIN] = 0xe3a0;
+       volume_control.data[UAC__MAX] = 0xfff0;
+       volume_control.data[UAC__RES] = 0x0030;
+
+       return 0;
+}
+
+/**
+ * audio_bind_config - add USB audio function to a configuration
+ * @c: the configuration to supcard the USB audio function
+ * Context: single threaded during gadget setup
+ *
+ * Returns zero on success, else negative errno.
+ */
+static int __init audio_bind_config(struct usb_configuration *c)
+{
+       struct f_audio *audio;
+       int status;
+
+       /* allocate and initialize one new instance */
+       audio = kzalloc(sizeof *audio, GFP_KERNEL);
+       if (!audio)
+               return -ENOMEM;
+
+       audio->card.func.name = "g_audio";
+       audio->card.gadget = c->cdev->gadget;
+
+       INIT_LIST_HEAD(&audio->play_queue);
+       spin_lock_init(&audio->lock);
+
+       /* set up ASLA audio devices */
+       status = gaudio_setup(&audio->card);
+       if (status < 0)
+               goto setup_fail;
+
+       audio->card.func.strings = audio_strings;
+       audio->card.func.bind = f_audio_bind;
+       audio->card.func.unbind = f_audio_unbind;
+       audio->card.func.set_alt = f_audio_set_alt;
+       audio->card.func.setup = f_audio_setup;
+       audio->card.func.disable = f_audio_disable;
+
+       control_selector_init(audio);
+
+       INIT_WORK(&audio->playback_work, f_audio_playback_work);
+
+       status = usb_add_function(c, &audio->card.func);
+       if (status)
+               goto add_fail;
+
+       INFO(c->cdev, "audio_buf_size %d, req_buf_size %d, req_count %d\n",
+               audio_buf_size, req_buf_size, req_count);
+
+       return status;
+
+add_fail:
+       gaudio_cleanup();
+setup_fail:
+       kfree(audio);
+       return status;
+}
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
new file mode 100644 (file)
index 0000000..6261db4
--- /dev/null
@@ -0,0 +1,1354 @@
+/*
+ * f_uac2.c -- USB Audio Class 2.0 Function
+ *
+ * Copyright (C) 2011
+ *    Yadwinder Singh (yadi.brar01@gmail.com)
+ *    Jaswinder Singh (jaswinder.singh@linaro.org)
+ *
+ * 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.
+ */
+
+#include <linux/usb/audio.h>
+#include <linux/usb/audio-v2.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+/* Playback(USB-IN) Default Stereo - Fl/Fr */
+static int p_chmask = 0x3;
+module_param(p_chmask, uint, S_IRUGO);
+MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
+
+/* Playback Default 48 KHz */
+static int p_srate = 48000;
+module_param(p_srate, uint, S_IRUGO);
+MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
+
+/* Playback Default 16bits/sample */
+static int p_ssize = 2;
+module_param(p_ssize, uint, S_IRUGO);
+MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)");
+
+/* Capture(USB-OUT) Default Stereo - Fl/Fr */
+static int c_chmask = 0x3;
+module_param(c_chmask, uint, S_IRUGO);
+MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
+
+/* Capture Default 64 KHz */
+static int c_srate = 64000;
+module_param(c_srate, uint, S_IRUGO);
+MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
+
+/* Capture Default 16bits/sample */
+static int c_ssize = 2;
+module_param(c_ssize, uint, S_IRUGO);
+MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
+
+/* Keep everyone on toes */
+#define USB_XFERS      2
+
+/*
+ * The driver implements a simple UAC_2 topology.
+ * USB-OUT -> IT_1 -> OT_3 -> ALSA_Capture
+ * ALSA_Playback -> IT_2 -> OT_4 -> USB-IN
+ * Capture and Playback sampling rates are independently
+ *  controlled by two clock sources :
+ *    CLK_5 := c_srate, and CLK_6 := p_srate
+ */
+#define USB_OUT_IT_ID  1
+#define IO_IN_IT_ID    2
+#define IO_OUT_OT_ID   3
+#define USB_IN_OT_ID   4
+#define USB_OUT_CLK_ID 5
+#define USB_IN_CLK_ID  6
+
+#define CONTROL_ABSENT 0
+#define CONTROL_RDONLY 1
+#define CONTROL_RDWR   3
+
+#define CLK_FREQ_CTRL  0
+#define CLK_VLD_CTRL   2
+
+#define COPY_CTRL      0
+#define CONN_CTRL      2
+#define OVRLD_CTRL     4
+#define CLSTR_CTRL     6
+#define UNFLW_CTRL     8
+#define OVFLW_CTRL     10
+
+const char *uac2_name = "snd_uac2";
+
+struct uac2_req {
+       struct uac2_rtd_params *pp; /* parent param */
+       struct usb_request *req;
+};
+
+struct uac2_rtd_params {
+       struct snd_uac2_chip *uac2; /* parent chip */
+       bool ep_enabled; /* if the ep is enabled */
+       /* Size of the ring buffer */
+       size_t dma_bytes;
+       unsigned char *dma_area;
+
+       struct snd_pcm_substream *ss;
+
+       /* Ring buffer */
+       ssize_t hw_ptr;
+
+       void *rbuf;
+
+       size_t period_size;
+
+       unsigned max_psize;
+       struct uac2_req ureq[USB_XFERS];
+
+       spinlock_t lock;
+};
+
+struct snd_uac2_chip {
+       struct platform_device pdev;
+       struct platform_driver pdrv;
+
+       struct uac2_rtd_params p_prm;
+       struct uac2_rtd_params c_prm;
+
+       struct snd_card *card;
+       struct snd_pcm *pcm;
+};
+
+#define BUFF_SIZE_MAX  (PAGE_SIZE * 16)
+#define PRD_SIZE_MAX   PAGE_SIZE
+#define MIN_PERIODS    4
+
+static struct snd_pcm_hardware uac2_pcm_hardware = {
+       .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER
+                | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID
+                | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+       .rates = SNDRV_PCM_RATE_CONTINUOUS,
+       .periods_max = BUFF_SIZE_MAX / PRD_SIZE_MAX,
+       .buffer_bytes_max = BUFF_SIZE_MAX,
+       .period_bytes_max = PRD_SIZE_MAX,
+       .periods_min = MIN_PERIODS,
+};
+
+struct audio_dev {
+       u8 ac_intf, ac_alt;
+       u8 as_out_intf, as_out_alt;
+       u8 as_in_intf, as_in_alt;
+
+       struct usb_ep *in_ep, *out_ep;
+       struct usb_function func;
+
+       /* The ALSA Sound Card it represents on the USB-Client side */
+       struct snd_uac2_chip uac2;
+};
+
+static struct audio_dev *agdev_g;
+
+static inline
+struct audio_dev *func_to_agdev(struct usb_function *f)
+{
+       return container_of(f, struct audio_dev, func);
+}
+
+static inline
+struct audio_dev *uac2_to_agdev(struct snd_uac2_chip *u)
+{
+       return container_of(u, struct audio_dev, uac2);
+}
+
+static inline
+struct snd_uac2_chip *pdev_to_uac2(struct platform_device *p)
+{
+       return container_of(p, struct snd_uac2_chip, pdev);
+}
+
+static inline
+uint num_channels(uint chanmask)
+{
+       uint num = 0;
+
+       while (chanmask) {
+               num += (chanmask & 1);
+               chanmask >>= 1;
+       }
+
+       return num;
+}
+
+static void
+agdev_iso_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       unsigned pending;
+       unsigned long flags;
+       bool update_alsa = false;
+       unsigned char *src, *dst;
+       int status = req->status;
+       struct uac2_req *ur = req->context;
+       struct snd_pcm_substream *substream;
+       struct uac2_rtd_params *prm = ur->pp;
+       struct snd_uac2_chip *uac2 = prm->uac2;
+
+       /* i/f shutting down */
+       if (!prm->ep_enabled || req->status == -ESHUTDOWN)
+               return;
+
+       /*
+        * We can't really do much about bad xfers.
+        * Afterall, the ISOCH xfers could fail legitimately.
+        */
+       if (status)
+               pr_debug("%s: iso_complete status(%d) %d/%d\n",
+                       __func__, status, req->actual, req->length);
+
+       substream = prm->ss;
+
+       /* Do nothing if ALSA isn't active */
+       if (!substream)
+               goto exit;
+
+       spin_lock_irqsave(&prm->lock, flags);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               src = prm->dma_area + prm->hw_ptr;
+               req->actual = req->length;
+               dst = req->buf;
+       } else {
+               dst = prm->dma_area + prm->hw_ptr;
+               src = req->buf;
+       }
+
+       pending = prm->hw_ptr % prm->period_size;
+       pending += req->actual;
+       if (pending >= prm->period_size)
+               update_alsa = true;
+
+       prm->hw_ptr = (prm->hw_ptr + req->actual) % prm->dma_bytes;
+
+       spin_unlock_irqrestore(&prm->lock, flags);
+
+       /* Pack USB load in ALSA ring buffer */
+       memcpy(dst, src, req->actual);
+exit:
+       if (usb_ep_queue(ep, req, GFP_ATOMIC))
+               dev_err(&uac2->pdev.dev, "%d Error!\n", __LINE__);
+
+       if (update_alsa)
+               snd_pcm_period_elapsed(substream);
+
+       return;
+}
+
+static int
+uac2_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream);
+       struct uac2_rtd_params *prm;
+       unsigned long flags;
+       int err = 0;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               prm = &uac2->p_prm;
+       else
+               prm = &uac2->c_prm;
+
+       spin_lock_irqsave(&prm->lock, flags);
+
+       /* Reset */
+       prm->hw_ptr = 0;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+               prm->ss = substream;
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+               prm->ss = NULL;
+               break;
+       default:
+               err = -EINVAL;
+       }
+
+       spin_unlock_irqrestore(&prm->lock, flags);
+
+       /* Clear buffer after Play stops */
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !prm->ss)
+               memset(prm->rbuf, 0, prm->max_psize * USB_XFERS);
+
+       return err;
+}
+
+static snd_pcm_uframes_t uac2_pcm_pointer(struct snd_pcm_substream *substream)
+{
+       struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream);
+       struct uac2_rtd_params *prm;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               prm = &uac2->p_prm;
+       else
+               prm = &uac2->c_prm;
+
+       return bytes_to_frames(substream->runtime, prm->hw_ptr);
+}
+
+static int uac2_pcm_hw_params(struct snd_pcm_substream *substream,
+                              struct snd_pcm_hw_params *hw_params)
+{
+       struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream);
+       struct uac2_rtd_params *prm;
+       int err;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               prm = &uac2->p_prm;
+       else
+               prm = &uac2->c_prm;
+
+       err = snd_pcm_lib_malloc_pages(substream,
+                                       params_buffer_bytes(hw_params));
+       if (err >= 0) {
+               prm->dma_bytes = substream->runtime->dma_bytes;
+               prm->dma_area = substream->runtime->dma_area;
+               prm->period_size = params_period_bytes(hw_params);
+       }
+
+       return err;
+}
+
+static int uac2_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream);
+       struct uac2_rtd_params *prm;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               prm = &uac2->p_prm;
+       else
+               prm = &uac2->c_prm;
+
+       prm->dma_area = NULL;
+       prm->dma_bytes = 0;
+       prm->period_size = 0;
+
+       return snd_pcm_lib_free_pages(substream);
+}
+
+static int uac2_pcm_open(struct snd_pcm_substream *substream)
+{
+       struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       runtime->hw = uac2_pcm_hardware;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               spin_lock_init(&uac2->p_prm.lock);
+               runtime->hw.rate_min = p_srate;
+               runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; /* ! p_ssize ! */
+               runtime->hw.channels_min = num_channels(p_chmask);
+               runtime->hw.period_bytes_min = 2 * uac2->p_prm.max_psize
+                                               / runtime->hw.periods_min;
+       } else {
+               spin_lock_init(&uac2->c_prm.lock);
+               runtime->hw.rate_min = c_srate;
+               runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; /* ! c_ssize ! */
+               runtime->hw.channels_min = num_channels(c_chmask);
+               runtime->hw.period_bytes_min = 2 * uac2->c_prm.max_psize
+                                               / runtime->hw.periods_min;
+       }
+
+       runtime->hw.rate_max = runtime->hw.rate_min;
+       runtime->hw.channels_max = runtime->hw.channels_min;
+
+       snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+
+       return 0;
+}
+
+/* ALSA cries without these function pointers */
+static int uac2_pcm_null(struct snd_pcm_substream *substream)
+{
+       return 0;
+}
+
+static struct snd_pcm_ops uac2_pcm_ops = {
+       .open = uac2_pcm_open,
+       .close = uac2_pcm_null,
+       .ioctl = snd_pcm_lib_ioctl,
+       .hw_params = uac2_pcm_hw_params,
+       .hw_free = uac2_pcm_hw_free,
+       .trigger = uac2_pcm_trigger,
+       .pointer = uac2_pcm_pointer,
+       .prepare = uac2_pcm_null,
+};
+
+static int snd_uac2_probe(struct platform_device *pdev)
+{
+       struct snd_uac2_chip *uac2 = pdev_to_uac2(pdev);
+       struct snd_card *card;
+       struct snd_pcm *pcm;
+       int err;
+
+       /* Choose any slot, with no id */
+       err = snd_card_new(&pdev->dev, -1, NULL, THIS_MODULE, 0, &card);
+       if (err < 0)
+               return err;
+
+       uac2->card = card;
+
+       /*
+        * Create first PCM device
+        * Create a substream only for non-zero channel streams
+        */
+       err = snd_pcm_new(uac2->card, "UAC2 PCM", 0,
+                              p_chmask ? 1 : 0, c_chmask ? 1 : 0, &pcm);
+       if (err < 0)
+               goto snd_fail;
+
+       strcpy(pcm->name, "UAC2 PCM");
+       pcm->private_data = uac2;
+
+       uac2->pcm = pcm;
+
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &uac2_pcm_ops);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &uac2_pcm_ops);
+
+       strcpy(card->driver, "UAC2_Gadget");
+       strcpy(card->shortname, "UAC2_Gadget");
+       sprintf(card->longname, "UAC2_Gadget %i", pdev->id);
+
+       snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+               snd_dma_continuous_data(GFP_KERNEL), 0, BUFF_SIZE_MAX);
+
+       err = snd_card_register(card);
+       if (!err) {
+               platform_set_drvdata(pdev, card);
+               return 0;
+       }
+
+snd_fail:
+       snd_card_free(card);
+
+       uac2->pcm = NULL;
+       uac2->card = NULL;
+
+       return err;
+}
+
+static int snd_uac2_remove(struct platform_device *pdev)
+{
+       struct snd_card *card = platform_get_drvdata(pdev);
+
+       if (card)
+               return snd_card_free(card);
+
+       return 0;
+}
+
+static int alsa_uac2_init(struct audio_dev *agdev)
+{
+       struct snd_uac2_chip *uac2 = &agdev->uac2;
+       int err;
+
+       uac2->pdrv.probe = snd_uac2_probe;
+       uac2->pdrv.remove = snd_uac2_remove;
+       uac2->pdrv.driver.name = uac2_name;
+
+       uac2->pdev.id = 0;
+       uac2->pdev.name = uac2_name;
+
+       /* Register snd_uac2 driver */
+       err = platform_driver_register(&uac2->pdrv);
+       if (err)
+               return err;
+
+       /* Register snd_uac2 device */
+       err = platform_device_register(&uac2->pdev);
+       if (err)
+               platform_driver_unregister(&uac2->pdrv);
+
+       return err;
+}
+
+static void alsa_uac2_exit(struct audio_dev *agdev)
+{
+       struct snd_uac2_chip *uac2 = &agdev->uac2;
+
+       platform_driver_unregister(&uac2->pdrv);
+       platform_device_unregister(&uac2->pdev);
+}
+
+
+/* --------- USB Function Interface ------------- */
+
+enum {
+       STR_ASSOC,
+       STR_IF_CTRL,
+       STR_CLKSRC_IN,
+       STR_CLKSRC_OUT,
+       STR_USB_IT,
+       STR_IO_IT,
+       STR_USB_OT,
+       STR_IO_OT,
+       STR_AS_OUT_ALT0,
+       STR_AS_OUT_ALT1,
+       STR_AS_IN_ALT0,
+       STR_AS_IN_ALT1,
+};
+
+static char clksrc_in[8];
+static char clksrc_out[8];
+
+static struct usb_string strings_fn[] = {
+       [STR_ASSOC].s = "Source/Sink",
+       [STR_IF_CTRL].s = "Topology Control",
+       [STR_CLKSRC_IN].s = clksrc_in,
+       [STR_CLKSRC_OUT].s = clksrc_out,
+       [STR_USB_IT].s = "USBH Out",
+       [STR_IO_IT].s = "USBD Out",
+       [STR_USB_OT].s = "USBH In",
+       [STR_IO_OT].s = "USBD In",
+       [STR_AS_OUT_ALT0].s = "Playback Inactive",
+       [STR_AS_OUT_ALT1].s = "Playback Active",
+       [STR_AS_IN_ALT0].s = "Capture Inactive",
+       [STR_AS_IN_ALT1].s = "Capture Active",
+       { },
+};
+
+static struct usb_gadget_strings str_fn = {
+       .language = 0x0409,     /* en-us */
+       .strings = strings_fn,
+};
+
+static struct usb_gadget_strings *fn_strings[] = {
+       &str_fn,
+       NULL,
+};
+
+static struct usb_qualifier_descriptor devqual_desc = {
+       .bLength = sizeof devqual_desc,
+       .bDescriptorType = USB_DT_DEVICE_QUALIFIER,
+
+       .bcdUSB = cpu_to_le16(0x200),
+       .bDeviceClass = USB_CLASS_MISC,
+       .bDeviceSubClass = 0x02,
+       .bDeviceProtocol = 0x01,
+       .bNumConfigurations = 1,
+       .bRESERVED = 0,
+};
+
+static struct usb_interface_assoc_descriptor iad_desc = {
+       .bLength = sizeof iad_desc,
+       .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
+
+       .bFirstInterface = 0,
+       .bInterfaceCount = 3,
+       .bFunctionClass = USB_CLASS_AUDIO,
+       .bFunctionSubClass = UAC2_FUNCTION_SUBCLASS_UNDEFINED,
+       .bFunctionProtocol = UAC_VERSION_2,
+};
+
+/* Audio Control Interface */
+static struct usb_interface_descriptor std_ac_if_desc = {
+       .bLength = sizeof std_ac_if_desc,
+       .bDescriptorType = USB_DT_INTERFACE,
+
+       .bAlternateSetting = 0,
+       .bNumEndpoints = 0,
+       .bInterfaceClass = USB_CLASS_AUDIO,
+       .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
+       .bInterfaceProtocol = UAC_VERSION_2,
+};
+
+/* Clock source for IN traffic */
+struct uac_clock_source_descriptor in_clk_src_desc = {
+       .bLength = sizeof in_clk_src_desc,
+       .bDescriptorType = USB_DT_CS_INTERFACE,
+
+       .bDescriptorSubtype = UAC2_CLOCK_SOURCE,
+       .bClockID = USB_IN_CLK_ID,
+       .bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED,
+       .bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL),
+       .bAssocTerminal = 0,
+};
+
+/* Clock source for OUT traffic */
+struct uac_clock_source_descriptor out_clk_src_desc = {
+       .bLength = sizeof out_clk_src_desc,
+       .bDescriptorType = USB_DT_CS_INTERFACE,
+
+       .bDescriptorSubtype = UAC2_CLOCK_SOURCE,
+       .bClockID = USB_OUT_CLK_ID,
+       .bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED,
+       .bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL),
+       .bAssocTerminal = 0,
+};
+
+/* Input Terminal for USB_OUT */
+struct uac2_input_terminal_descriptor usb_out_it_desc = {
+       .bLength = sizeof usb_out_it_desc,
+       .bDescriptorType = USB_DT_CS_INTERFACE,
+
+       .bDescriptorSubtype = UAC_INPUT_TERMINAL,
+       .bTerminalID = USB_OUT_IT_ID,
+       .wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING),
+       .bAssocTerminal = 0,
+       .bCSourceID = USB_OUT_CLK_ID,
+       .iChannelNames = 0,
+       .bmControls = (CONTROL_RDWR << COPY_CTRL),
+};
+
+/* Input Terminal for I/O-In */
+struct uac2_input_terminal_descriptor io_in_it_desc = {
+       .bLength = sizeof io_in_it_desc,
+       .bDescriptorType = USB_DT_CS_INTERFACE,
+
+       .bDescriptorSubtype = UAC_INPUT_TERMINAL,
+       .bTerminalID = IO_IN_IT_ID,
+       .wTerminalType = cpu_to_le16(UAC_INPUT_TERMINAL_UNDEFINED),
+       .bAssocTerminal = 0,
+       .bCSourceID = USB_IN_CLK_ID,
+       .iChannelNames = 0,
+       .bmControls = (CONTROL_RDWR << COPY_CTRL),
+};
+
+/* Ouput Terminal for USB_IN */
+struct uac2_output_terminal_descriptor usb_in_ot_desc = {
+       .bLength = sizeof usb_in_ot_desc,
+       .bDescriptorType = USB_DT_CS_INTERFACE,
+
+       .bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
+       .bTerminalID = USB_IN_OT_ID,
+       .wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING),
+       .bAssocTerminal = 0,
+       .bSourceID = IO_IN_IT_ID,
+       .bCSourceID = USB_IN_CLK_ID,
+       .bmControls = (CONTROL_RDWR << COPY_CTRL),
+};
+
+/* Ouput Terminal for I/O-Out */
+struct uac2_output_terminal_descriptor io_out_ot_desc = {
+       .bLength = sizeof io_out_ot_desc,
+       .bDescriptorType = USB_DT_CS_INTERFACE,
+
+       .bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
+       .bTerminalID = IO_OUT_OT_ID,
+       .wTerminalType = cpu_to_le16(UAC_OUTPUT_TERMINAL_UNDEFINED),
+       .bAssocTerminal = 0,
+       .bSourceID = USB_OUT_IT_ID,
+       .bCSourceID = USB_OUT_CLK_ID,
+       .bmControls = (CONTROL_RDWR << COPY_CTRL),
+};
+
+struct uac2_ac_header_descriptor ac_hdr_desc = {
+       .bLength = sizeof ac_hdr_desc,
+       .bDescriptorType = USB_DT_CS_INTERFACE,
+
+       .bDescriptorSubtype = UAC_MS_HEADER,
+       .bcdADC = cpu_to_le16(0x200),
+       .bCategory = UAC2_FUNCTION_IO_BOX,
+       .wTotalLength = sizeof in_clk_src_desc + sizeof out_clk_src_desc
+                        + sizeof usb_out_it_desc + sizeof io_in_it_desc
+                       + sizeof usb_in_ot_desc + sizeof io_out_ot_desc,
+       .bmControls = 0,
+};
+
+/* Audio Streaming OUT Interface - Alt0 */
+static struct usb_interface_descriptor std_as_out_if0_desc = {
+       .bLength = sizeof std_as_out_if0_desc,
+       .bDescriptorType = USB_DT_INTERFACE,
+
+       .bAlternateSetting = 0,
+       .bNumEndpoints = 0,
+       .bInterfaceClass = USB_CLASS_AUDIO,
+       .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
+       .bInterfaceProtocol = UAC_VERSION_2,
+};
+
+/* Audio Streaming OUT Interface - Alt1 */
+static struct usb_interface_descriptor std_as_out_if1_desc = {
+       .bLength = sizeof std_as_out_if1_desc,
+       .bDescriptorType = USB_DT_INTERFACE,
+
+       .bAlternateSetting = 1,
+       .bNumEndpoints = 1,
+       .bInterfaceClass = USB_CLASS_AUDIO,
+       .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
+       .bInterfaceProtocol = UAC_VERSION_2,
+};
+
+/* Audio Stream OUT Intface Desc */
+struct uac2_as_header_descriptor as_out_hdr_desc = {
+       .bLength = sizeof as_out_hdr_desc,
+       .bDescriptorType = USB_DT_CS_INTERFACE,
+
+       .bDescriptorSubtype = UAC_AS_GENERAL,
+       .bTerminalLink = USB_OUT_IT_ID,
+       .bmControls = 0,
+       .bFormatType = UAC_FORMAT_TYPE_I,
+       .bmFormats = cpu_to_le32(UAC_FORMAT_TYPE_I_PCM),
+       .iChannelNames = 0,
+};
+
+/* Audio USB_OUT Format */
+struct uac2_format_type_i_descriptor as_out_fmt1_desc = {
+       .bLength = sizeof as_out_fmt1_desc,
+       .bDescriptorType = USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype = UAC_FORMAT_TYPE,
+       .bFormatType = UAC_FORMAT_TYPE_I,
+};
+
+/* STD AS ISO OUT Endpoint */
+struct usb_endpoint_descriptor fs_epout_desc = {
+       .bLength = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType = USB_DT_ENDPOINT,
+
+       .bEndpointAddress = USB_DIR_OUT,
+       .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
+       .bInterval = 1,
+};
+
+struct usb_endpoint_descriptor hs_epout_desc = {
+       .bLength = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType = USB_DT_ENDPOINT,
+
+       .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
+       .bInterval = 4,
+};
+
+/* CS AS ISO OUT Endpoint */
+static struct uac2_iso_endpoint_descriptor as_iso_out_desc = {
+       .bLength = sizeof as_iso_out_desc,
+       .bDescriptorType = USB_DT_CS_ENDPOINT,
+
+       .bDescriptorSubtype = UAC_EP_GENERAL,
+       .bmAttributes = 0,
+       .bmControls = 0,
+       .bLockDelayUnits = 0,
+       .wLockDelay = 0,
+};
+
+/* Audio Streaming IN Interface - Alt0 */
+static struct usb_interface_descriptor std_as_in_if0_desc = {
+       .bLength = sizeof std_as_in_if0_desc,
+       .bDescriptorType = USB_DT_INTERFACE,
+
+       .bAlternateSetting = 0,
+       .bNumEndpoints = 0,
+       .bInterfaceClass = USB_CLASS_AUDIO,
+       .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
+       .bInterfaceProtocol = UAC_VERSION_2,
+};
+
+/* Audio Streaming IN Interface - Alt1 */
+static struct usb_interface_descriptor std_as_in_if1_desc = {
+       .bLength = sizeof std_as_in_if1_desc,
+       .bDescriptorType = USB_DT_INTERFACE,
+
+       .bAlternateSetting = 1,
+       .bNumEndpoints = 1,
+       .bInterfaceClass = USB_CLASS_AUDIO,
+       .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
+       .bInterfaceProtocol = UAC_VERSION_2,
+};
+
+/* Audio Stream IN Intface Desc */
+struct uac2_as_header_descriptor as_in_hdr_desc = {
+       .bLength = sizeof as_in_hdr_desc,
+       .bDescriptorType = USB_DT_CS_INTERFACE,
+
+       .bDescriptorSubtype = UAC_AS_GENERAL,
+       .bTerminalLink = USB_IN_OT_ID,
+       .bmControls = 0,
+       .bFormatType = UAC_FORMAT_TYPE_I,
+       .bmFormats = cpu_to_le32(UAC_FORMAT_TYPE_I_PCM),
+       .iChannelNames = 0,
+};
+
+/* Audio USB_IN Format */
+struct uac2_format_type_i_descriptor as_in_fmt1_desc = {
+       .bLength = sizeof as_in_fmt1_desc,
+       .bDescriptorType = USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype = UAC_FORMAT_TYPE,
+       .bFormatType = UAC_FORMAT_TYPE_I,
+};
+
+/* STD AS ISO IN Endpoint */
+struct usb_endpoint_descriptor fs_epin_desc = {
+       .bLength = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType = USB_DT_ENDPOINT,
+
+       .bEndpointAddress = USB_DIR_IN,
+       .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
+       .bInterval = 1,
+};
+
+struct usb_endpoint_descriptor hs_epin_desc = {
+       .bLength = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType = USB_DT_ENDPOINT,
+
+       .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
+       .bInterval = 4,
+};
+
+/* CS AS ISO IN Endpoint */
+static struct uac2_iso_endpoint_descriptor as_iso_in_desc = {
+       .bLength = sizeof as_iso_in_desc,
+       .bDescriptorType = USB_DT_CS_ENDPOINT,
+
+       .bDescriptorSubtype = UAC_EP_GENERAL,
+       .bmAttributes = 0,
+       .bmControls = 0,
+       .bLockDelayUnits = 0,
+       .wLockDelay = 0,
+};
+
+static struct usb_descriptor_header *fs_audio_desc[] = {
+       (struct usb_descriptor_header *)&iad_desc,
+       (struct usb_descriptor_header *)&std_ac_if_desc,
+
+       (struct usb_descriptor_header *)&ac_hdr_desc,
+       (struct usb_descriptor_header *)&in_clk_src_desc,
+       (struct usb_descriptor_header *)&out_clk_src_desc,
+       (struct usb_descriptor_header *)&usb_out_it_desc,
+       (struct usb_descriptor_header *)&io_in_it_desc,
+       (struct usb_descriptor_header *)&usb_in_ot_desc,
+       (struct usb_descriptor_header *)&io_out_ot_desc,
+
+       (struct usb_descriptor_header *)&std_as_out_if0_desc,
+       (struct usb_descriptor_header *)&std_as_out_if1_desc,
+
+       (struct usb_descriptor_header *)&as_out_hdr_desc,
+       (struct usb_descriptor_header *)&as_out_fmt1_desc,
+       (struct usb_descriptor_header *)&fs_epout_desc,
+       (struct usb_descriptor_header *)&as_iso_out_desc,
+
+       (struct usb_descriptor_header *)&std_as_in_if0_desc,
+       (struct usb_descriptor_header *)&std_as_in_if1_desc,
+
+       (struct usb_descriptor_header *)&as_in_hdr_desc,
+       (struct usb_descriptor_header *)&as_in_fmt1_desc,
+       (struct usb_descriptor_header *)&fs_epin_desc,
+       (struct usb_descriptor_header *)&as_iso_in_desc,
+       NULL,
+};
+
+static struct usb_descriptor_header *hs_audio_desc[] = {
+       (struct usb_descriptor_header *)&iad_desc,
+       (struct usb_descriptor_header *)&std_ac_if_desc,
+
+       (struct usb_descriptor_header *)&ac_hdr_desc,
+       (struct usb_descriptor_header *)&in_clk_src_desc,
+       (struct usb_descriptor_header *)&out_clk_src_desc,
+       (struct usb_descriptor_header *)&usb_out_it_desc,
+       (struct usb_descriptor_header *)&io_in_it_desc,
+       (struct usb_descriptor_header *)&usb_in_ot_desc,
+       (struct usb_descriptor_header *)&io_out_ot_desc,
+
+       (struct usb_descriptor_header *)&std_as_out_if0_desc,
+       (struct usb_descriptor_header *)&std_as_out_if1_desc,
+
+       (struct usb_descriptor_header *)&as_out_hdr_desc,
+       (struct usb_descriptor_header *)&as_out_fmt1_desc,
+       (struct usb_descriptor_header *)&hs_epout_desc,
+       (struct usb_descriptor_header *)&as_iso_out_desc,
+
+       (struct usb_descriptor_header *)&std_as_in_if0_desc,
+       (struct usb_descriptor_header *)&std_as_in_if1_desc,
+
+       (struct usb_descriptor_header *)&as_in_hdr_desc,
+       (struct usb_descriptor_header *)&as_in_fmt1_desc,
+       (struct usb_descriptor_header *)&hs_epin_desc,
+       (struct usb_descriptor_header *)&as_iso_in_desc,
+       NULL,
+};
+
+struct cntrl_cur_lay3 {
+       __u32   dCUR;
+};
+
+struct cntrl_range_lay3 {
+       __u16   wNumSubRanges;
+       __u32   dMIN;
+       __u32   dMAX;
+       __u32   dRES;
+} __packed;
+
+static inline void
+free_ep(struct uac2_rtd_params *prm, struct usb_ep *ep)
+{
+       struct snd_uac2_chip *uac2 = prm->uac2;
+       int i;
+
+       prm->ep_enabled = false;
+
+       for (i = 0; i < USB_XFERS; i++) {
+               if (prm->ureq[i].req) {
+                       usb_ep_dequeue(ep, prm->ureq[i].req);
+                       usb_ep_free_request(ep, prm->ureq[i].req);
+                       prm->ureq[i].req = NULL;
+               }
+       }
+
+       if (usb_ep_disable(ep))
+               dev_err(&uac2->pdev.dev,
+                       "%s:%d Error!\n", __func__, __LINE__);
+}
+
+static int __init
+afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
+{
+       struct audio_dev *agdev = func_to_agdev(fn);
+       struct snd_uac2_chip *uac2 = &agdev->uac2;
+       struct usb_composite_dev *cdev = cfg->cdev;
+       struct usb_gadget *gadget = cdev->gadget;
+       struct uac2_rtd_params *prm;
+       int ret;
+
+       ret = usb_interface_id(cfg, fn);
+       if (ret < 0) {
+               dev_err(&uac2->pdev.dev,
+                       "%s:%d Error!\n", __func__, __LINE__);
+               return ret;
+       }
+       std_ac_if_desc.bInterfaceNumber = ret;
+       agdev->ac_intf = ret;
+       agdev->ac_alt = 0;
+
+       ret = usb_interface_id(cfg, fn);
+       if (ret < 0) {
+               dev_err(&uac2->pdev.dev,
+                       "%s:%d Error!\n", __func__, __LINE__);
+               return ret;
+       }
+       std_as_out_if0_desc.bInterfaceNumber = ret;
+       std_as_out_if1_desc.bInterfaceNumber = ret;
+       agdev->as_out_intf = ret;
+       agdev->as_out_alt = 0;
+
+       ret = usb_interface_id(cfg, fn);
+       if (ret < 0) {
+               dev_err(&uac2->pdev.dev,
+                       "%s:%d Error!\n", __func__, __LINE__);
+               return ret;
+       }
+       std_as_in_if0_desc.bInterfaceNumber = ret;
+       std_as_in_if1_desc.bInterfaceNumber = ret;
+       agdev->as_in_intf = ret;
+       agdev->as_in_alt = 0;
+
+       agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc);
+       if (!agdev->out_ep) {
+               dev_err(&uac2->pdev.dev,
+                       "%s:%d Error!\n", __func__, __LINE__);
+               goto err;
+       }
+       agdev->out_ep->driver_data = agdev;
+
+       agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc);
+       if (!agdev->in_ep) {
+               dev_err(&uac2->pdev.dev,
+                       "%s:%d Error!\n", __func__, __LINE__);
+               goto err;
+       }
+       agdev->in_ep->driver_data = agdev;
+
+       uac2->p_prm.uac2 = uac2;
+       uac2->c_prm.uac2 = uac2;
+
+       hs_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress;
+       hs_epout_desc.wMaxPacketSize = fs_epout_desc.wMaxPacketSize;
+       hs_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress;
+       hs_epin_desc.wMaxPacketSize = fs_epin_desc.wMaxPacketSize;
+
+       ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, NULL);
+       if (ret)
+               goto err;
+
+       prm = &agdev->uac2.c_prm;
+       prm->max_psize = hs_epout_desc.wMaxPacketSize;
+       prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL);
+       if (!prm->rbuf) {
+               prm->max_psize = 0;
+               goto err;
+       }
+
+       prm = &agdev->uac2.p_prm;
+       prm->max_psize = hs_epin_desc.wMaxPacketSize;
+       prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL);
+       if (!prm->rbuf) {
+               prm->max_psize = 0;
+               goto err;
+       }
+
+       ret = alsa_uac2_init(agdev);
+       if (ret)
+               goto err;
+       return 0;
+err:
+       kfree(agdev->uac2.p_prm.rbuf);
+       kfree(agdev->uac2.c_prm.rbuf);
+       usb_free_all_descriptors(fn);
+       if (agdev->in_ep)
+               agdev->in_ep->driver_data = NULL;
+       if (agdev->out_ep)
+               agdev->out_ep->driver_data = NULL;
+       return -EINVAL;
+}
+
+static void
+afunc_unbind(struct usb_configuration *cfg, struct usb_function *fn)
+{
+       struct audio_dev *agdev = func_to_agdev(fn);
+       struct uac2_rtd_params *prm;
+
+       alsa_uac2_exit(agdev);
+
+       prm = &agdev->uac2.p_prm;
+       kfree(prm->rbuf);
+
+       prm = &agdev->uac2.c_prm;
+       kfree(prm->rbuf);
+       usb_free_all_descriptors(fn);
+
+       if (agdev->in_ep)
+               agdev->in_ep->driver_data = NULL;
+       if (agdev->out_ep)
+               agdev->out_ep->driver_data = NULL;
+}
+
+static int
+afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
+{
+       struct usb_composite_dev *cdev = fn->config->cdev;
+       struct audio_dev *agdev = func_to_agdev(fn);
+       struct snd_uac2_chip *uac2 = &agdev->uac2;
+       struct usb_gadget *gadget = cdev->gadget;
+       struct usb_request *req;
+       struct usb_ep *ep;
+       struct uac2_rtd_params *prm;
+       int i;
+
+       /* No i/f has more than 2 alt settings */
+       if (alt > 1) {
+               dev_err(&uac2->pdev.dev,
+                       "%s:%d Error!\n", __func__, __LINE__);
+               return -EINVAL;
+       }
+
+       if (intf == agdev->ac_intf) {
+               /* Control I/f has only 1 AltSetting - 0 */
+               if (alt) {
+                       dev_err(&uac2->pdev.dev,
+                               "%s:%d Error!\n", __func__, __LINE__);
+                       return -EINVAL;
+               }
+               return 0;
+       }
+
+       if (intf == agdev->as_out_intf) {
+               ep = agdev->out_ep;
+               prm = &uac2->c_prm;
+               config_ep_by_speed(gadget, fn, ep);
+               agdev->as_out_alt = alt;
+       } else if (intf == agdev->as_in_intf) {
+               ep = agdev->in_ep;
+               prm = &uac2->p_prm;
+               config_ep_by_speed(gadget, fn, ep);
+               agdev->as_in_alt = alt;
+       } else {
+               dev_err(&uac2->pdev.dev,
+                       "%s:%d Error!\n", __func__, __LINE__);
+               return -EINVAL;
+       }
+
+       if (alt == 0) {
+               free_ep(prm, ep);
+               return 0;
+       }
+
+       prm->ep_enabled = true;
+       usb_ep_enable(ep);
+
+       for (i = 0; i < USB_XFERS; i++) {
+               if (prm->ureq[i].req) {
+                       if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC))
+                               dev_err(&uac2->pdev.dev, "%d Error!\n",
+                                       __LINE__);
+                       continue;
+               }
+
+               req = usb_ep_alloc_request(ep, GFP_ATOMIC);
+               if (req == NULL) {
+                       dev_err(&uac2->pdev.dev,
+                               "%s:%d Error!\n", __func__, __LINE__);
+                       return -EINVAL;
+               }
+
+               prm->ureq[i].req = req;
+               prm->ureq[i].pp = prm;
+
+               req->zero = 0;
+               req->context = &prm->ureq[i];
+               req->length = prm->max_psize;
+               req->complete = agdev_iso_complete;
+               req->buf = prm->rbuf + i * req->length;
+
+               if (usb_ep_queue(ep, req, GFP_ATOMIC))
+                       dev_err(&uac2->pdev.dev, "%d Error!\n", __LINE__);
+       }
+
+       return 0;
+}
+
+static int
+afunc_get_alt(struct usb_function *fn, unsigned intf)
+{
+       struct audio_dev *agdev = func_to_agdev(fn);
+       struct snd_uac2_chip *uac2 = &agdev->uac2;
+
+       if (intf == agdev->ac_intf)
+               return agdev->ac_alt;
+       else if (intf == agdev->as_out_intf)
+               return agdev->as_out_alt;
+       else if (intf == agdev->as_in_intf)
+               return agdev->as_in_alt;
+       else
+               dev_err(&uac2->pdev.dev,
+                       "%s:%d Invalid Interface %d!\n",
+                       __func__, __LINE__, intf);
+
+       return -EINVAL;
+}
+
+static void
+afunc_disable(struct usb_function *fn)
+{
+       struct audio_dev *agdev = func_to_agdev(fn);
+       struct snd_uac2_chip *uac2 = &agdev->uac2;
+
+       free_ep(&uac2->p_prm, agdev->in_ep);
+       agdev->as_in_alt = 0;
+
+       free_ep(&uac2->c_prm, agdev->out_ep);
+       agdev->as_out_alt = 0;
+}
+
+static int
+in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
+{
+       struct usb_request *req = fn->config->cdev->req;
+       struct audio_dev *agdev = func_to_agdev(fn);
+       struct snd_uac2_chip *uac2 = &agdev->uac2;
+       u16 w_length = le16_to_cpu(cr->wLength);
+       u16 w_index = le16_to_cpu(cr->wIndex);
+       u16 w_value = le16_to_cpu(cr->wValue);
+       u8 entity_id = (w_index >> 8) & 0xff;
+       u8 control_selector = w_value >> 8;
+       int value = -EOPNOTSUPP;
+
+       if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
+               struct cntrl_cur_lay3 c;
+
+               if (entity_id == USB_IN_CLK_ID)
+                       c.dCUR = p_srate;
+               else if (entity_id == USB_OUT_CLK_ID)
+                       c.dCUR = c_srate;
+
+               value = min_t(unsigned, w_length, sizeof c);
+               memcpy(req->buf, &c, value);
+       } else if (control_selector == UAC2_CS_CONTROL_CLOCK_VALID) {
+               *(u8 *)req->buf = 1;
+               value = min_t(unsigned, w_length, 1);
+       } else {
+               dev_err(&uac2->pdev.dev,
+                       "%s:%d control_selector=%d TODO!\n",
+                       __func__, __LINE__, control_selector);
+       }
+
+       return value;
+}
+
+static int
+in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
+{
+       struct usb_request *req = fn->config->cdev->req;
+       struct audio_dev *agdev = func_to_agdev(fn);
+       struct snd_uac2_chip *uac2 = &agdev->uac2;
+       u16 w_length = le16_to_cpu(cr->wLength);
+       u16 w_index = le16_to_cpu(cr->wIndex);
+       u16 w_value = le16_to_cpu(cr->wValue);
+       u8 entity_id = (w_index >> 8) & 0xff;
+       u8 control_selector = w_value >> 8;
+       struct cntrl_range_lay3 r;
+       int value = -EOPNOTSUPP;
+
+       if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
+               if (entity_id == USB_IN_CLK_ID)
+                       r.dMIN = p_srate;
+               else if (entity_id == USB_OUT_CLK_ID)
+                       r.dMIN = c_srate;
+               else
+                       return -EOPNOTSUPP;
+
+               r.dMAX = r.dMIN;
+               r.dRES = 0;
+               r.wNumSubRanges = 1;
+
+               value = min_t(unsigned, w_length, sizeof r);
+               memcpy(req->buf, &r, value);
+       } else {
+               dev_err(&uac2->pdev.dev,
+                       "%s:%d control_selector=%d TODO!\n",
+                       __func__, __LINE__, control_selector);
+       }
+
+       return value;
+}
+
+static int
+ac_rq_in(struct usb_function *fn, const struct usb_ctrlrequest *cr)
+{
+       if (cr->bRequest == UAC2_CS_CUR)
+               return in_rq_cur(fn, cr);
+       else if (cr->bRequest == UAC2_CS_RANGE)
+               return in_rq_range(fn, cr);
+       else
+               return -EOPNOTSUPP;
+}
+
+static int
+out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
+{
+       u16 w_length = le16_to_cpu(cr->wLength);
+       u16 w_value = le16_to_cpu(cr->wValue);
+       u8 control_selector = w_value >> 8;
+
+       if (control_selector == UAC2_CS_CONTROL_SAM_FREQ)
+               return w_length;
+
+       return -EOPNOTSUPP;
+}
+
+static int
+setup_rq_inf(struct usb_function *fn, const struct usb_ctrlrequest *cr)
+{
+       struct audio_dev *agdev = func_to_agdev(fn);
+       struct snd_uac2_chip *uac2 = &agdev->uac2;
+       u16 w_index = le16_to_cpu(cr->wIndex);
+       u8 intf = w_index & 0xff;
+
+       if (intf != agdev->ac_intf) {
+               dev_err(&uac2->pdev.dev,
+                       "%s:%d Error!\n", __func__, __LINE__);
+               return -EOPNOTSUPP;
+       }
+
+       if (cr->bRequestType & USB_DIR_IN)
+               return ac_rq_in(fn, cr);
+       else if (cr->bRequest == UAC2_CS_CUR)
+               return out_rq_cur(fn, cr);
+
+       return -EOPNOTSUPP;
+}
+
+static int
+afunc_setup(struct usb_function *fn, const struct usb_ctrlrequest *cr)
+{
+       struct usb_composite_dev *cdev = fn->config->cdev;
+       struct audio_dev *agdev = func_to_agdev(fn);
+       struct snd_uac2_chip *uac2 = &agdev->uac2;
+       struct usb_request *req = cdev->req;
+       u16 w_length = le16_to_cpu(cr->wLength);
+       int value = -EOPNOTSUPP;
+
+       /* Only Class specific requests are supposed to reach here */
+       if ((cr->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS)
+               return -EOPNOTSUPP;
+
+       if ((cr->bRequestType & USB_RECIP_MASK) == USB_RECIP_INTERFACE)
+               value = setup_rq_inf(fn, cr);
+       else
+               dev_err(&uac2->pdev.dev, "%s:%d Error!\n", __func__, __LINE__);
+
+       if (value >= 0) {
+               req->length = value;
+               req->zero = value < w_length;
+               value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+               if (value < 0) {
+                       dev_err(&uac2->pdev.dev,
+                               "%s:%d Error!\n", __func__, __LINE__);
+                       req->status = 0;
+               }
+       }
+
+       return value;
+}
+
+static int audio_bind_config(struct usb_configuration *cfg)
+{
+       int res;
+
+       agdev_g = kzalloc(sizeof *agdev_g, GFP_KERNEL);
+       if (agdev_g == NULL)
+               return -ENOMEM;
+
+       res = usb_string_ids_tab(cfg->cdev, strings_fn);
+       if (res)
+               return res;
+       iad_desc.iFunction = strings_fn[STR_ASSOC].id;
+       std_ac_if_desc.iInterface = strings_fn[STR_IF_CTRL].id;
+       in_clk_src_desc.iClockSource = strings_fn[STR_CLKSRC_IN].id;
+       out_clk_src_desc.iClockSource = strings_fn[STR_CLKSRC_OUT].id;
+       usb_out_it_desc.iTerminal = strings_fn[STR_USB_IT].id;
+       io_in_it_desc.iTerminal = strings_fn[STR_IO_IT].id;
+       usb_in_ot_desc.iTerminal = strings_fn[STR_USB_OT].id;
+       io_out_ot_desc.iTerminal = strings_fn[STR_IO_OT].id;
+       std_as_out_if0_desc.iInterface = strings_fn[STR_AS_OUT_ALT0].id;
+       std_as_out_if1_desc.iInterface = strings_fn[STR_AS_OUT_ALT1].id;
+       std_as_in_if0_desc.iInterface = strings_fn[STR_AS_IN_ALT0].id;
+       std_as_in_if1_desc.iInterface = strings_fn[STR_AS_IN_ALT1].id;
+
+       agdev_g->func.name = "uac2_func";
+       agdev_g->func.strings = fn_strings;
+       agdev_g->func.bind = afunc_bind;
+       agdev_g->func.unbind = afunc_unbind;
+       agdev_g->func.set_alt = afunc_set_alt;
+       agdev_g->func.get_alt = afunc_get_alt;
+       agdev_g->func.disable = afunc_disable;
+       agdev_g->func.setup = afunc_setup;
+
+       /* Initialize the configurable parameters */
+       usb_out_it_desc.bNrChannels = num_channels(c_chmask);
+       usb_out_it_desc.bmChannelConfig = cpu_to_le32(c_chmask);
+       io_in_it_desc.bNrChannels = num_channels(p_chmask);
+       io_in_it_desc.bmChannelConfig = cpu_to_le32(p_chmask);
+       as_out_hdr_desc.bNrChannels = num_channels(c_chmask);
+       as_out_hdr_desc.bmChannelConfig = cpu_to_le32(c_chmask);
+       as_in_hdr_desc.bNrChannels = num_channels(p_chmask);
+       as_in_hdr_desc.bmChannelConfig = cpu_to_le32(p_chmask);
+       as_out_fmt1_desc.bSubslotSize = c_ssize;
+       as_out_fmt1_desc.bBitResolution = c_ssize * 8;
+       as_in_fmt1_desc.bSubslotSize = p_ssize;
+       as_in_fmt1_desc.bBitResolution = p_ssize * 8;
+
+       snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", p_srate);
+       snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", c_srate);
+
+       res = usb_add_function(cfg, &agdev_g->func);
+       if (res < 0)
+               kfree(agdev_g);
+
+       return res;
+}
+
+static void
+uac2_unbind_config(struct usb_configuration *cfg)
+{
+       kfree(agdev_g);
+       agdev_g = NULL;
+}
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
new file mode 100644 (file)
index 0000000..e2a1f50
--- /dev/null
@@ -0,0 +1,836 @@
+/*
+ *     uvc_gadget.c  --  USB Video Class Gadget driver
+ *
+ *     Copyright (C) 2009-2010
+ *         Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/video.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-event.h>
+
+#include "uvc.h"
+
+unsigned int uvc_gadget_trace_param;
+
+/*-------------------------------------------------------------------------*/
+
+/* module parameters specific to the Video streaming endpoint */
+static unsigned int streaming_interval = 1;
+module_param(streaming_interval, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(streaming_interval, "1 - 16");
+
+static unsigned int streaming_maxpacket = 1024;
+module_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(streaming_maxpacket, "1 - 1023 (FS), 1 - 3072 (hs/ss)");
+
+static unsigned int streaming_maxburst;
+module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)");
+
+/* --------------------------------------------------------------------------
+ * Function descriptors
+ */
+
+/* string IDs are assigned dynamically */
+
+#define UVC_STRING_CONTROL_IDX                 0
+#define UVC_STRING_STREAMING_IDX               1
+
+static struct usb_string uvc_en_us_strings[] = {
+       [UVC_STRING_CONTROL_IDX].s = "UVC Camera",
+       [UVC_STRING_STREAMING_IDX].s = "Video Streaming",
+       {  }
+};
+
+static struct usb_gadget_strings uvc_stringtab = {
+       .language = 0x0409,     /* en-us */
+       .strings = uvc_en_us_strings,
+};
+
+static struct usb_gadget_strings *uvc_function_strings[] = {
+       &uvc_stringtab,
+       NULL,
+};
+
+#define UVC_INTF_VIDEO_CONTROL                 0
+#define UVC_INTF_VIDEO_STREAMING               1
+
+#define UVC_STATUS_MAX_PACKET_SIZE             16      /* 16 bytes status */
+
+static struct usb_interface_assoc_descriptor uvc_iad __initdata = {
+       .bLength                = sizeof(uvc_iad),
+       .bDescriptorType        = USB_DT_INTERFACE_ASSOCIATION,
+       .bFirstInterface        = 0,
+       .bInterfaceCount        = 2,
+       .bFunctionClass         = USB_CLASS_VIDEO,
+       .bFunctionSubClass      = UVC_SC_VIDEO_INTERFACE_COLLECTION,
+       .bFunctionProtocol      = 0x00,
+       .iFunction              = 0,
+};
+
+static struct usb_interface_descriptor uvc_control_intf __initdata = {
+       .bLength                = USB_DT_INTERFACE_SIZE,
+       .bDescriptorType        = USB_DT_INTERFACE,
+       .bInterfaceNumber       = UVC_INTF_VIDEO_CONTROL,
+       .bAlternateSetting      = 0,
+       .bNumEndpoints          = 1,
+       .bInterfaceClass        = USB_CLASS_VIDEO,
+       .bInterfaceSubClass     = UVC_SC_VIDEOCONTROL,
+       .bInterfaceProtocol     = 0x00,
+       .iInterface             = 0,
+};
+
+static struct usb_endpoint_descriptor uvc_control_ep __initdata = {
+       .bLength                = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType        = USB_DT_ENDPOINT,
+       .bEndpointAddress       = USB_DIR_IN,
+       .bmAttributes           = USB_ENDPOINT_XFER_INT,
+       .wMaxPacketSize         = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE),
+       .bInterval              = 8,
+};
+
+static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp __initdata = {
+       .bLength                = sizeof(uvc_ss_control_comp),
+       .bDescriptorType        = USB_DT_SS_ENDPOINT_COMP,
+       /* The following 3 values can be tweaked if necessary. */
+       .bMaxBurst              = 0,
+       .bmAttributes           = 0,
+       .wBytesPerInterval      = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE),
+};
+
+static struct uvc_control_endpoint_descriptor uvc_control_cs_ep __initdata = {
+       .bLength                = UVC_DT_CONTROL_ENDPOINT_SIZE,
+       .bDescriptorType        = USB_DT_CS_ENDPOINT,
+       .bDescriptorSubType     = UVC_EP_INTERRUPT,
+       .wMaxTransferSize       = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE),
+};
+
+static struct usb_interface_descriptor uvc_streaming_intf_alt0 __initdata = {
+       .bLength                = USB_DT_INTERFACE_SIZE,
+       .bDescriptorType        = USB_DT_INTERFACE,
+       .bInterfaceNumber       = UVC_INTF_VIDEO_STREAMING,
+       .bAlternateSetting      = 0,
+       .bNumEndpoints          = 0,
+       .bInterfaceClass        = USB_CLASS_VIDEO,
+       .bInterfaceSubClass     = UVC_SC_VIDEOSTREAMING,
+       .bInterfaceProtocol     = 0x00,
+       .iInterface             = 0,
+};
+
+static struct usb_interface_descriptor uvc_streaming_intf_alt1 __initdata = {
+       .bLength                = USB_DT_INTERFACE_SIZE,
+       .bDescriptorType        = USB_DT_INTERFACE,
+       .bInterfaceNumber       = UVC_INTF_VIDEO_STREAMING,
+       .bAlternateSetting      = 1,
+       .bNumEndpoints          = 1,
+       .bInterfaceClass        = USB_CLASS_VIDEO,
+       .bInterfaceSubClass     = UVC_SC_VIDEOSTREAMING,
+       .bInterfaceProtocol     = 0x00,
+       .iInterface             = 0,
+};
+
+static struct usb_endpoint_descriptor uvc_fs_streaming_ep __initdata = {
+       .bLength                = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType        = USB_DT_ENDPOINT,
+       .bEndpointAddress       = USB_DIR_IN,
+       .bmAttributes           = USB_ENDPOINT_SYNC_ASYNC
+                               | USB_ENDPOINT_XFER_ISOC,
+       /* The wMaxPacketSize and bInterval values will be initialized from
+        * module parameters.
+        */
+};
+
+static struct usb_endpoint_descriptor uvc_hs_streaming_ep __initdata = {
+       .bLength                = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType        = USB_DT_ENDPOINT,
+       .bEndpointAddress       = USB_DIR_IN,
+       .bmAttributes           = USB_ENDPOINT_SYNC_ASYNC
+                               | USB_ENDPOINT_XFER_ISOC,
+       /* The wMaxPacketSize and bInterval values will be initialized from
+        * module parameters.
+        */
+};
+
+static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = {
+       .bLength                = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType        = USB_DT_ENDPOINT,
+
+       .bEndpointAddress       = USB_DIR_IN,
+       .bmAttributes           = USB_ENDPOINT_SYNC_ASYNC
+                               | USB_ENDPOINT_XFER_ISOC,
+       /* The wMaxPacketSize and bInterval values will be initialized from
+        * module parameters.
+        */
+};
+
+static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp __initdata = {
+       .bLength                = sizeof(uvc_ss_streaming_comp),
+       .bDescriptorType        = USB_DT_SS_ENDPOINT_COMP,
+       /* The bMaxBurst, bmAttributes and wBytesPerInterval values will be
+        * initialized from module parameters.
+        */
+};
+
+static const struct usb_descriptor_header * const uvc_fs_streaming[] = {
+       (struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
+       (struct usb_descriptor_header *) &uvc_fs_streaming_ep,
+       NULL,
+};
+
+static const struct usb_descriptor_header * const uvc_hs_streaming[] = {
+       (struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
+       (struct usb_descriptor_header *) &uvc_hs_streaming_ep,
+       NULL,
+};
+
+static const struct usb_descriptor_header * const uvc_ss_streaming[] = {
+       (struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
+       (struct usb_descriptor_header *) &uvc_ss_streaming_ep,
+       (struct usb_descriptor_header *) &uvc_ss_streaming_comp,
+       NULL,
+};
+
+/* --------------------------------------------------------------------------
+ * Control requests
+ */
+
+static void
+uvc_function_ep0_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct uvc_device *uvc = req->context;
+       struct v4l2_event v4l2_event;
+       struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
+
+       if (uvc->event_setup_out) {
+               uvc->event_setup_out = 0;
+
+               memset(&v4l2_event, 0, sizeof(v4l2_event));
+               v4l2_event.type = UVC_EVENT_DATA;
+               uvc_event->data.length = req->actual;
+               memcpy(&uvc_event->data.data, req->buf, req->actual);
+               v4l2_event_queue(uvc->vdev, &v4l2_event);
+       }
+}
+
+static int
+uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+       struct uvc_device *uvc = to_uvc(f);
+       struct v4l2_event v4l2_event;
+       struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
+
+       /* printk(KERN_INFO "setup request %02x %02x value %04x index %04x %04x\n",
+        *      ctrl->bRequestType, ctrl->bRequest, le16_to_cpu(ctrl->wValue),
+        *      le16_to_cpu(ctrl->wIndex), le16_to_cpu(ctrl->wLength));
+        */
+
+       if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS) {
+               INFO(f->config->cdev, "invalid request type\n");
+               return -EINVAL;
+       }
+
+       /* Stall too big requests. */
+       if (le16_to_cpu(ctrl->wLength) > UVC_MAX_REQUEST_SIZE)
+               return -EINVAL;
+
+       memset(&v4l2_event, 0, sizeof(v4l2_event));
+       v4l2_event.type = UVC_EVENT_SETUP;
+       memcpy(&uvc_event->req, ctrl, sizeof(uvc_event->req));
+       v4l2_event_queue(uvc->vdev, &v4l2_event);
+
+       return 0;
+}
+
+void uvc_function_setup_continue(struct uvc_device *uvc)
+{
+       struct usb_composite_dev *cdev = uvc->func.config->cdev;
+
+       usb_composite_setup_continue(cdev);
+}
+
+static int
+uvc_function_get_alt(struct usb_function *f, unsigned interface)
+{
+       struct uvc_device *uvc = to_uvc(f);
+
+       INFO(f->config->cdev, "uvc_function_get_alt(%u)\n", interface);
+
+       if (interface == uvc->control_intf)
+               return 0;
+       else if (interface != uvc->streaming_intf)
+               return -EINVAL;
+       else
+               return uvc->state == UVC_STATE_STREAMING ? 1 : 0;
+}
+
+static int
+uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
+{
+       struct uvc_device *uvc = to_uvc(f);
+       struct v4l2_event v4l2_event;
+       struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
+       int ret;
+
+       INFO(f->config->cdev, "uvc_function_set_alt(%u, %u)\n", interface, alt);
+
+       if (interface == uvc->control_intf) {
+               if (alt)
+                       return -EINVAL;
+
+               if (uvc->state == UVC_STATE_DISCONNECTED) {
+                       memset(&v4l2_event, 0, sizeof(v4l2_event));
+                       v4l2_event.type = UVC_EVENT_CONNECT;
+                       uvc_event->speed = f->config->cdev->gadget->speed;
+                       v4l2_event_queue(uvc->vdev, &v4l2_event);
+
+                       uvc->state = UVC_STATE_CONNECTED;
+               }
+
+               return 0;
+       }
+
+       if (interface != uvc->streaming_intf)
+               return -EINVAL;
+
+       /* TODO
+       if (usb_endpoint_xfer_bulk(&uvc->desc.vs_ep))
+               return alt ? -EINVAL : 0;
+       */
+
+       switch (alt) {
+       case 0:
+               if (uvc->state != UVC_STATE_STREAMING)
+                       return 0;
+
+               if (uvc->video.ep)
+                       usb_ep_disable(uvc->video.ep);
+
+               memset(&v4l2_event, 0, sizeof(v4l2_event));
+               v4l2_event.type = UVC_EVENT_STREAMOFF;
+               v4l2_event_queue(uvc->vdev, &v4l2_event);
+
+               uvc->state = UVC_STATE_CONNECTED;
+               return 0;
+
+       case 1:
+               if (uvc->state != UVC_STATE_CONNECTED)
+                       return 0;
+
+               if (uvc->video.ep) {
+                       ret = config_ep_by_speed(f->config->cdev->gadget,
+                                       &(uvc->func), uvc->video.ep);
+                       if (ret)
+                               return ret;
+                       usb_ep_enable(uvc->video.ep);
+               }
+
+               memset(&v4l2_event, 0, sizeof(v4l2_event));
+               v4l2_event.type = UVC_EVENT_STREAMON;
+               v4l2_event_queue(uvc->vdev, &v4l2_event);
+               return USB_GADGET_DELAYED_STATUS;
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static void
+uvc_function_disable(struct usb_function *f)
+{
+       struct uvc_device *uvc = to_uvc(f);
+       struct v4l2_event v4l2_event;
+
+       INFO(f->config->cdev, "uvc_function_disable\n");
+
+       memset(&v4l2_event, 0, sizeof(v4l2_event));
+       v4l2_event.type = UVC_EVENT_DISCONNECT;
+       v4l2_event_queue(uvc->vdev, &v4l2_event);
+
+       uvc->state = UVC_STATE_DISCONNECTED;
+}
+
+/* --------------------------------------------------------------------------
+ * Connection / disconnection
+ */
+
+void
+uvc_function_connect(struct uvc_device *uvc)
+{
+       struct usb_composite_dev *cdev = uvc->func.config->cdev;
+       int ret;
+
+       if ((ret = usb_function_activate(&uvc->func)) < 0)
+               INFO(cdev, "UVC connect failed with %d\n", ret);
+}
+
+void
+uvc_function_disconnect(struct uvc_device *uvc)
+{
+       struct usb_composite_dev *cdev = uvc->func.config->cdev;
+       int ret;
+
+       if ((ret = usb_function_deactivate(&uvc->func)) < 0)
+               INFO(cdev, "UVC disconnect failed with %d\n", ret);
+}
+
+/* --------------------------------------------------------------------------
+ * USB probe and disconnect
+ */
+
+static int
+uvc_register_video(struct uvc_device *uvc)
+{
+       struct usb_composite_dev *cdev = uvc->func.config->cdev;
+       struct video_device *video;
+
+       /* TODO reference counting. */
+       video = video_device_alloc();
+       if (video == NULL)
+               return -ENOMEM;
+
+       video->v4l2_dev = &uvc->v4l2_dev;
+       video->fops = &uvc_v4l2_fops;
+       video->release = video_device_release;
+       strlcpy(video->name, cdev->gadget->name, sizeof(video->name));
+
+       uvc->vdev = video;
+       video_set_drvdata(video, uvc);
+
+       return video_register_device(video, VFL_TYPE_GRABBER, -1);
+}
+
+#define UVC_COPY_DESCRIPTOR(mem, dst, desc) \
+       do { \
+               memcpy(mem, desc, (desc)->bLength); \
+               *(dst)++ = mem; \
+               mem += (desc)->bLength; \
+       } while (0);
+
+#define UVC_COPY_DESCRIPTORS(mem, dst, src) \
+       do { \
+               const struct usb_descriptor_header * const *__src; \
+               for (__src = src; *__src; ++__src) { \
+                       memcpy(mem, *__src, (*__src)->bLength); \
+                       *dst++ = mem; \
+                       mem += (*__src)->bLength; \
+               } \
+       } while (0)
+
+static struct usb_descriptor_header ** __init
+uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
+{
+       struct uvc_input_header_descriptor *uvc_streaming_header;
+       struct uvc_header_descriptor *uvc_control_header;
+       const struct uvc_descriptor_header * const *uvc_control_desc;
+       const struct uvc_descriptor_header * const *uvc_streaming_cls;
+       const struct usb_descriptor_header * const *uvc_streaming_std;
+       const struct usb_descriptor_header * const *src;
+       struct usb_descriptor_header **dst;
+       struct usb_descriptor_header **hdr;
+       unsigned int control_size;
+       unsigned int streaming_size;
+       unsigned int n_desc;
+       unsigned int bytes;
+       void *mem;
+
+       switch (speed) {
+       case USB_SPEED_SUPER:
+               uvc_control_desc = uvc->desc.ss_control;
+               uvc_streaming_cls = uvc->desc.ss_streaming;
+               uvc_streaming_std = uvc_ss_streaming;
+               break;
+
+       case USB_SPEED_HIGH:
+               uvc_control_desc = uvc->desc.fs_control;
+               uvc_streaming_cls = uvc->desc.hs_streaming;
+               uvc_streaming_std = uvc_hs_streaming;
+               break;
+
+       case USB_SPEED_FULL:
+       default:
+               uvc_control_desc = uvc->desc.fs_control;
+               uvc_streaming_cls = uvc->desc.fs_streaming;
+               uvc_streaming_std = uvc_fs_streaming;
+               break;
+       }
+
+       /* Descriptors layout
+        *
+        * uvc_iad
+        * uvc_control_intf
+        * Class-specific UVC control descriptors
+        * uvc_control_ep
+        * uvc_control_cs_ep
+        * uvc_ss_control_comp (for SS only)
+        * uvc_streaming_intf_alt0
+        * Class-specific UVC streaming descriptors
+        * uvc_{fs|hs}_streaming
+        */
+
+       /* Count descriptors and compute their size. */
+       control_size = 0;
+       streaming_size = 0;
+       bytes = uvc_iad.bLength + uvc_control_intf.bLength
+             + uvc_control_ep.bLength + uvc_control_cs_ep.bLength
+             + uvc_streaming_intf_alt0.bLength;
+
+       if (speed == USB_SPEED_SUPER) {
+               bytes += uvc_ss_control_comp.bLength;
+               n_desc = 6;
+       } else {
+               n_desc = 5;
+       }
+
+       for (src = (const struct usb_descriptor_header **)uvc_control_desc;
+            *src; ++src) {
+               control_size += (*src)->bLength;
+               bytes += (*src)->bLength;
+               n_desc++;
+       }
+       for (src = (const struct usb_descriptor_header **)uvc_streaming_cls;
+            *src; ++src) {
+               streaming_size += (*src)->bLength;
+               bytes += (*src)->bLength;
+               n_desc++;
+       }
+       for (src = uvc_streaming_std; *src; ++src) {
+               bytes += (*src)->bLength;
+               n_desc++;
+       }
+
+       mem = kmalloc((n_desc + 1) * sizeof(*src) + bytes, GFP_KERNEL);
+       if (mem == NULL)
+               return NULL;
+
+       hdr = mem;
+       dst = mem;
+       mem += (n_desc + 1) * sizeof(*src);
+
+       /* Copy the descriptors. */
+       UVC_COPY_DESCRIPTOR(mem, dst, &uvc_iad);
+       UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_intf);
+
+       uvc_control_header = mem;
+       UVC_COPY_DESCRIPTORS(mem, dst,
+               (const struct usb_descriptor_header **)uvc_control_desc);
+       uvc_control_header->wTotalLength = cpu_to_le16(control_size);
+       uvc_control_header->bInCollection = 1;
+       uvc_control_header->baInterfaceNr[0] = uvc->streaming_intf;
+
+       UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_ep);
+       if (speed == USB_SPEED_SUPER)
+               UVC_COPY_DESCRIPTOR(mem, dst, &uvc_ss_control_comp);
+
+       UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_cs_ep);
+       UVC_COPY_DESCRIPTOR(mem, dst, &uvc_streaming_intf_alt0);
+
+       uvc_streaming_header = mem;
+       UVC_COPY_DESCRIPTORS(mem, dst,
+               (const struct usb_descriptor_header**)uvc_streaming_cls);
+       uvc_streaming_header->wTotalLength = cpu_to_le16(streaming_size);
+       uvc_streaming_header->bEndpointAddress = uvc->video.ep->address;
+
+       UVC_COPY_DESCRIPTORS(mem, dst, uvc_streaming_std);
+
+       *dst = NULL;
+       return hdr;
+}
+
+static void
+uvc_function_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct usb_composite_dev *cdev = c->cdev;
+       struct uvc_device *uvc = to_uvc(f);
+
+       INFO(cdev, "uvc_function_unbind\n");
+
+       video_unregister_device(uvc->vdev);
+       v4l2_device_unregister(&uvc->v4l2_dev);
+       uvc->control_ep->driver_data = NULL;
+       uvc->video.ep->driver_data = NULL;
+
+       uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id = 0;
+       usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
+       kfree(uvc->control_buf);
+
+       usb_free_all_descriptors(f);
+
+       kfree(uvc);
+}
+
+static int __init
+uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct usb_composite_dev *cdev = c->cdev;
+       struct uvc_device *uvc = to_uvc(f);
+       unsigned int max_packet_mult;
+       unsigned int max_packet_size;
+       struct usb_ep *ep;
+       int ret = -EINVAL;
+
+       INFO(cdev, "uvc_function_bind\n");
+
+       /* Sanity check the streaming endpoint module parameters.
+        */
+       streaming_interval = clamp(streaming_interval, 1U, 16U);
+       streaming_maxpacket = clamp(streaming_maxpacket, 1U, 3072U);
+       streaming_maxburst = min(streaming_maxburst, 15U);
+
+       /* Fill in the FS/HS/SS Video Streaming specific descriptors from the
+        * module parameters.
+        *
+        * NOTE: We assume that the user knows what they are doing and won't
+        * give parameters that their UDC doesn't support.
+        */
+       if (streaming_maxpacket <= 1024) {
+               max_packet_mult = 1;
+               max_packet_size = streaming_maxpacket;
+       } else if (streaming_maxpacket <= 2048) {
+               max_packet_mult = 2;
+               max_packet_size = streaming_maxpacket / 2;
+       } else {
+               max_packet_mult = 3;
+               max_packet_size = streaming_maxpacket / 3;
+       }
+
+       uvc_fs_streaming_ep.wMaxPacketSize = min(streaming_maxpacket, 1023U);
+       uvc_fs_streaming_ep.bInterval = streaming_interval;
+
+       uvc_hs_streaming_ep.wMaxPacketSize = max_packet_size;
+       uvc_hs_streaming_ep.wMaxPacketSize |= ((max_packet_mult - 1) << 11);
+       uvc_hs_streaming_ep.bInterval = streaming_interval;
+
+       uvc_ss_streaming_ep.wMaxPacketSize = max_packet_size;
+       uvc_ss_streaming_ep.bInterval = streaming_interval;
+       uvc_ss_streaming_comp.bmAttributes = max_packet_mult - 1;
+       uvc_ss_streaming_comp.bMaxBurst = streaming_maxburst;
+       uvc_ss_streaming_comp.wBytesPerInterval =
+               max_packet_size * max_packet_mult * streaming_maxburst;
+
+       /* Allocate endpoints. */
+       ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep);
+       if (!ep) {
+               INFO(cdev, "Unable to allocate control EP\n");
+               goto error;
+       }
+       uvc->control_ep = ep;
+       ep->driver_data = uvc;
+
+       if (gadget_is_superspeed(c->cdev->gadget))
+               ep = usb_ep_autoconfig_ss(cdev->gadget, &uvc_ss_streaming_ep,
+                                         &uvc_ss_streaming_comp);
+       else if (gadget_is_dualspeed(cdev->gadget))
+               ep = usb_ep_autoconfig(cdev->gadget, &uvc_hs_streaming_ep);
+       else
+               ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep);
+
+       if (!ep) {
+               INFO(cdev, "Unable to allocate streaming EP\n");
+               goto error;
+       }
+       uvc->video.ep = ep;
+       ep->driver_data = uvc;
+
+       uvc_fs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
+       uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
+       uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address;
+
+       /* Allocate interface IDs. */
+       if ((ret = usb_interface_id(c, f)) < 0)
+               goto error;
+       uvc_iad.bFirstInterface = ret;
+       uvc_control_intf.bInterfaceNumber = ret;
+       uvc->control_intf = ret;
+
+       if ((ret = usb_interface_id(c, f)) < 0)
+               goto error;
+       uvc_streaming_intf_alt0.bInterfaceNumber = ret;
+       uvc_streaming_intf_alt1.bInterfaceNumber = ret;
+       uvc->streaming_intf = ret;
+
+       /* Copy descriptors */
+       f->fs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL);
+       if (gadget_is_dualspeed(cdev->gadget))
+               f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH);
+       if (gadget_is_superspeed(c->cdev->gadget))
+               f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER);
+
+       /* Preallocate control endpoint request. */
+       uvc->control_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL);
+       uvc->control_buf = kmalloc(UVC_MAX_REQUEST_SIZE, GFP_KERNEL);
+       if (uvc->control_req == NULL || uvc->control_buf == NULL) {
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       uvc->control_req->buf = uvc->control_buf;
+       uvc->control_req->complete = uvc_function_ep0_complete;
+       uvc->control_req->context = uvc;
+
+       /* Avoid letting this gadget enumerate until the userspace server is
+        * active.
+        */
+       if ((ret = usb_function_deactivate(f)) < 0)
+               goto error;
+
+       if (v4l2_device_register(&cdev->gadget->dev, &uvc->v4l2_dev)) {
+               printk(KERN_INFO "v4l2_device_register failed\n");
+               goto error;
+       }
+
+       /* Initialise video. */
+       ret = uvc_video_init(&uvc->video);
+       if (ret < 0)
+               goto error;
+
+       /* Register a V4L2 device. */
+       ret = uvc_register_video(uvc);
+       if (ret < 0) {
+               printk(KERN_INFO "Unable to register video device\n");
+               goto error;
+       }
+
+       return 0;
+
+error:
+       v4l2_device_unregister(&uvc->v4l2_dev);
+       if (uvc->vdev)
+               video_device_release(uvc->vdev);
+
+       if (uvc->control_ep)
+               uvc->control_ep->driver_data = NULL;
+       if (uvc->video.ep)
+               uvc->video.ep->driver_data = NULL;
+
+       if (uvc->control_req) {
+               usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
+               kfree(uvc->control_buf);
+       }
+
+       usb_free_all_descriptors(f);
+       return ret;
+}
+
+/* --------------------------------------------------------------------------
+ * USB gadget function
+ */
+
+/**
+ * uvc_bind_config - add a UVC function to a configuration
+ * @c: the configuration to support the UVC instance
+ * Context: single threaded during gadget setup
+ *
+ * Returns zero on success, else negative errno.
+ *
+ * Caller must have called @uvc_setup(). Caller is also responsible for
+ * calling @uvc_cleanup() before module unload.
+ */
+int __init
+uvc_bind_config(struct usb_configuration *c,
+               const struct uvc_descriptor_header * const *fs_control,
+               const struct uvc_descriptor_header * const *ss_control,
+               const struct uvc_descriptor_header * const *fs_streaming,
+               const struct uvc_descriptor_header * const *hs_streaming,
+               const struct uvc_descriptor_header * const *ss_streaming)
+{
+       struct uvc_device *uvc;
+       int ret = 0;
+
+       /* TODO Check if the USB device controller supports the required
+        * features.
+        */
+       if (!gadget_is_dualspeed(c->cdev->gadget))
+               return -EINVAL;
+
+       uvc = kzalloc(sizeof(*uvc), GFP_KERNEL);
+       if (uvc == NULL)
+               return -ENOMEM;
+
+       uvc->state = UVC_STATE_DISCONNECTED;
+
+       /* Validate the descriptors. */
+       if (fs_control == NULL || fs_control[0] == NULL ||
+           fs_control[0]->bDescriptorSubType != UVC_VC_HEADER)
+               goto error;
+
+       if (ss_control == NULL || ss_control[0] == NULL ||
+           ss_control[0]->bDescriptorSubType != UVC_VC_HEADER)
+               goto error;
+
+       if (fs_streaming == NULL || fs_streaming[0] == NULL ||
+           fs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER)
+               goto error;
+
+       if (hs_streaming == NULL || hs_streaming[0] == NULL ||
+           hs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER)
+               goto error;
+
+       if (ss_streaming == NULL || ss_streaming[0] == NULL ||
+           ss_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER)
+               goto error;
+
+       uvc->desc.fs_control = fs_control;
+       uvc->desc.ss_control = ss_control;
+       uvc->desc.fs_streaming = fs_streaming;
+       uvc->desc.hs_streaming = hs_streaming;
+       uvc->desc.ss_streaming = ss_streaming;
+
+       /* String descriptors are global, we only need to allocate string IDs
+        * for the first UVC function. UVC functions beyond the first (if any)
+        * will reuse the same IDs.
+        */
+       if (uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id == 0) {
+               ret = usb_string_ids_tab(c->cdev, uvc_en_us_strings);
+               if (ret)
+                       goto error;
+               uvc_iad.iFunction =
+                       uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id;
+               uvc_control_intf.iInterface =
+                       uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id;
+               ret = uvc_en_us_strings[UVC_STRING_STREAMING_IDX].id;
+               uvc_streaming_intf_alt0.iInterface = ret;
+               uvc_streaming_intf_alt1.iInterface = ret;
+       }
+
+       /* Register the function. */
+       uvc->func.name = "uvc";
+       uvc->func.strings = uvc_function_strings;
+       uvc->func.bind = uvc_function_bind;
+       uvc->func.unbind = uvc_function_unbind;
+       uvc->func.get_alt = uvc_function_get_alt;
+       uvc->func.set_alt = uvc_function_set_alt;
+       uvc->func.disable = uvc_function_disable;
+       uvc->func.setup = uvc_function_setup;
+
+       ret = usb_add_function(c, &uvc->func);
+       if (ret)
+               kfree(uvc);
+
+       return ret;
+
+error:
+       kfree(uvc);
+       return ret;
+}
+
+module_param_named(trace, uvc_gadget_trace_param, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(trace, "Trace level bitmask");
+
diff --git a/drivers/usb/gadget/function/f_uvc.h b/drivers/usb/gadget/function/f_uvc.h
new file mode 100644 (file)
index 0000000..ec52752
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ *     f_uvc.h  --  USB Video Class Gadget driver
+ *
+ *     Copyright (C) 2009-2010
+ *         Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ */
+
+#ifndef _F_UVC_H_
+#define _F_UVC_H_
+
+#include <linux/usb/composite.h>
+#include <linux/usb/video.h>
+
+int uvc_bind_config(struct usb_configuration *c,
+                   const struct uvc_descriptor_header * const *fs_control,
+                   const struct uvc_descriptor_header * const *hs_control,
+                   const struct uvc_descriptor_header * const *fs_streaming,
+                   const struct uvc_descriptor_header * const *hs_streaming,
+                   const struct uvc_descriptor_header * const *ss_streaming);
+
+#endif /* _F_UVC_H_ */
+
diff --git a/drivers/usb/gadget/function/g_zero.h b/drivers/usb/gadget/function/g_zero.h
new file mode 100644 (file)
index 0000000..15f1809
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * This header declares the utility functions used by "Gadget Zero", plus
+ * interfaces to its two single-configuration function drivers.
+ */
+
+#ifndef __G_ZERO_H
+#define __G_ZERO_H
+
+#define GZERO_BULK_BUFLEN      4096
+#define GZERO_QLEN             32
+#define GZERO_ISOC_INTERVAL    4
+#define GZERO_ISOC_MAXPACKET   1024
+
+struct usb_zero_options {
+       unsigned pattern;
+       unsigned isoc_interval;
+       unsigned isoc_maxpacket;
+       unsigned isoc_mult;
+       unsigned isoc_maxburst;
+       unsigned bulk_buflen;
+       unsigned qlen;
+};
+
+struct f_ss_opts {
+       struct usb_function_instance func_inst;
+       unsigned pattern;
+       unsigned isoc_interval;
+       unsigned isoc_maxpacket;
+       unsigned isoc_mult;
+       unsigned isoc_maxburst;
+       unsigned bulk_buflen;
+
+       /*
+        * Read/write access to configfs attributes is handled by configfs.
+        *
+        * This is to protect the data from concurrent access by read/write
+        * and create symlink/remove symlink.
+        */
+       struct mutex                    lock;
+       int                             refcnt;
+};
+
+struct f_lb_opts {
+       struct usb_function_instance func_inst;
+       unsigned bulk_buflen;
+       unsigned qlen;
+
+       /*
+        * Read/write access to configfs attributes is handled by configfs.
+        *
+        * This is to protect the data from concurrent access by read/write
+        * and create symlink/remove symlink.
+        */
+       struct mutex                    lock;
+       int                             refcnt;
+};
+
+void lb_modexit(void);
+int lb_modinit(void);
+
+/* common utilities */
+void free_ep_req(struct usb_ep *ep, struct usb_request *req);
+void disable_endpoints(struct usb_composite_dev *cdev,
+               struct usb_ep *in, struct usb_ep *out,
+               struct usb_ep *iso_in, struct usb_ep *iso_out);
+
+#endif /* __G_ZERO_H */
diff --git a/drivers/usb/gadget/function/ndis.h b/drivers/usb/gadget/function/ndis.h
new file mode 100644 (file)
index 0000000..a19f72d
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * ndis.h
+ *
+ * ntddndis.h modified by Benedikt Spranger <b.spranger@pengutronix.de>
+ *
+ * Thanks to the cygwin development team,
+ * espacially to Casper S. Hornstrup <chorns@users.sourceforge.net>
+ *
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ * This source code is offered for use in the public domain. You may
+ * use, modify or distribute it freely.
+ */
+
+#ifndef _LINUX_NDIS_H
+#define _LINUX_NDIS_H
+
+enum NDIS_DEVICE_POWER_STATE {
+       NdisDeviceStateUnspecified = 0,
+       NdisDeviceStateD0,
+       NdisDeviceStateD1,
+       NdisDeviceStateD2,
+       NdisDeviceStateD3,
+       NdisDeviceStateMaximum
+};
+
+struct NDIS_PM_WAKE_UP_CAPABILITIES {
+       enum NDIS_DEVICE_POWER_STATE  MinMagicPacketWakeUp;
+       enum NDIS_DEVICE_POWER_STATE  MinPatternWakeUp;
+       enum NDIS_DEVICE_POWER_STATE  MinLinkChangeWakeUp;
+};
+
+struct NDIS_PNP_CAPABILITIES {
+       __le32                                  Flags;
+       struct NDIS_PM_WAKE_UP_CAPABILITIES     WakeUpCapabilities;
+};
+
+struct NDIS_PM_PACKET_PATTERN {
+       __le32  Priority;
+       __le32  Reserved;
+       __le32  MaskSize;
+       __le32  PatternOffset;
+       __le32  PatternSize;
+       __le32  PatternFlags;
+};
+
+#endif /* _LINUX_NDIS_H */
diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c
new file mode 100644 (file)
index 0000000..95d2324
--- /dev/null
@@ -0,0 +1,1190 @@
+/*
+ * RNDIS MSG parser
+ *
+ * Authors:    Benedikt Spranger, Pengutronix
+ *             Robert Schwebel, Pengutronix
+ *
+ *              This program is free software; you can redistribute it and/or
+ *              modify it under the terms of the GNU General Public License
+ *              version 2, as published by the Free Software Foundation.
+ *
+ *             This software was originally developed in conformance with
+ *             Microsoft's Remote NDIS Specification License Agreement.
+ *
+ * 03/12/2004 Kai-Uwe Bloem <linux-development@auerswald.de>
+ *             Fixed message length bug in init_response
+ *
+ * 03/25/2004 Kai-Uwe Bloem <linux-development@auerswald.de>
+ *             Fixed rndis_rm_hdr length bug.
+ *
+ * Copyright (C) 2004 by David Brownell
+ *             updates to merge with Linux 2.6, better match RNDIS spec
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+#include <linux/netdevice.h>
+
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+
+#include "u_rndis.h"
+
+#undef VERBOSE_DEBUG
+
+#include "rndis.h"
+
+
+/* The driver for your USB chip needs to support ep0 OUT to work with
+ * RNDIS, plus all three CDC Ethernet endpoints (interrupt not optional).
+ *
+ * Windows hosts need an INF file like Documentation/usb/linux.inf
+ * and will be happier if you provide the host_addr module parameter.
+ */
+
+#if 0
+static int rndis_debug = 0;
+module_param (rndis_debug, int, 0);
+MODULE_PARM_DESC (rndis_debug, "enable debugging");
+#else
+#define rndis_debug            0
+#endif
+
+#define RNDIS_MAX_CONFIGS      1
+
+
+static rndis_params rndis_per_dev_params[RNDIS_MAX_CONFIGS];
+
+/* Driver Version */
+static const __le32 rndis_driver_version = cpu_to_le32(1);
+
+/* Function Prototypes */
+static rndis_resp_t *rndis_add_response(int configNr, u32 length);
+
+
+/* supported OIDs */
+static const u32 oid_supported_list[] =
+{
+       /* the general stuff */
+       RNDIS_OID_GEN_SUPPORTED_LIST,
+       RNDIS_OID_GEN_HARDWARE_STATUS,
+       RNDIS_OID_GEN_MEDIA_SUPPORTED,
+       RNDIS_OID_GEN_MEDIA_IN_USE,
+       RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE,
+       RNDIS_OID_GEN_LINK_SPEED,
+       RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE,
+       RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE,
+       RNDIS_OID_GEN_VENDOR_ID,
+       RNDIS_OID_GEN_VENDOR_DESCRIPTION,
+       RNDIS_OID_GEN_VENDOR_DRIVER_VERSION,
+       RNDIS_OID_GEN_CURRENT_PACKET_FILTER,
+       RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE,
+       RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
+       RNDIS_OID_GEN_PHYSICAL_MEDIUM,
+
+       /* the statistical stuff */
+       RNDIS_OID_GEN_XMIT_OK,
+       RNDIS_OID_GEN_RCV_OK,
+       RNDIS_OID_GEN_XMIT_ERROR,
+       RNDIS_OID_GEN_RCV_ERROR,
+       RNDIS_OID_GEN_RCV_NO_BUFFER,
+#ifdef RNDIS_OPTIONAL_STATS
+       RNDIS_OID_GEN_DIRECTED_BYTES_XMIT,
+       RNDIS_OID_GEN_DIRECTED_FRAMES_XMIT,
+       RNDIS_OID_GEN_MULTICAST_BYTES_XMIT,
+       RNDIS_OID_GEN_MULTICAST_FRAMES_XMIT,
+       RNDIS_OID_GEN_BROADCAST_BYTES_XMIT,
+       RNDIS_OID_GEN_BROADCAST_FRAMES_XMIT,
+       RNDIS_OID_GEN_DIRECTED_BYTES_RCV,
+       RNDIS_OID_GEN_DIRECTED_FRAMES_RCV,
+       RNDIS_OID_GEN_MULTICAST_BYTES_RCV,
+       RNDIS_OID_GEN_MULTICAST_FRAMES_RCV,
+       RNDIS_OID_GEN_BROADCAST_BYTES_RCV,
+       RNDIS_OID_GEN_BROADCAST_FRAMES_RCV,
+       RNDIS_OID_GEN_RCV_CRC_ERROR,
+       RNDIS_OID_GEN_TRANSMIT_QUEUE_LENGTH,
+#endif /* RNDIS_OPTIONAL_STATS */
+
+       /* mandatory 802.3 */
+       /* the general stuff */
+       RNDIS_OID_802_3_PERMANENT_ADDRESS,
+       RNDIS_OID_802_3_CURRENT_ADDRESS,
+       RNDIS_OID_802_3_MULTICAST_LIST,
+       RNDIS_OID_802_3_MAC_OPTIONS,
+       RNDIS_OID_802_3_MAXIMUM_LIST_SIZE,
+
+       /* the statistical stuff */
+       RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT,
+       RNDIS_OID_802_3_XMIT_ONE_COLLISION,
+       RNDIS_OID_802_3_XMIT_MORE_COLLISIONS,
+#ifdef RNDIS_OPTIONAL_STATS
+       RNDIS_OID_802_3_XMIT_DEFERRED,
+       RNDIS_OID_802_3_XMIT_MAX_COLLISIONS,
+       RNDIS_OID_802_3_RCV_OVERRUN,
+       RNDIS_OID_802_3_XMIT_UNDERRUN,
+       RNDIS_OID_802_3_XMIT_HEARTBEAT_FAILURE,
+       RNDIS_OID_802_3_XMIT_TIMES_CRS_LOST,
+       RNDIS_OID_802_3_XMIT_LATE_COLLISIONS,
+#endif /* RNDIS_OPTIONAL_STATS */
+
+#ifdef RNDIS_PM
+       /* PM and wakeup are "mandatory" for USB, but the RNDIS specs
+        * don't say what they mean ... and the NDIS specs are often
+        * confusing and/or ambiguous in this context.  (That is, more
+        * so than their specs for the other OIDs.)
+        *
+        * FIXME someone who knows what these should do, please
+        * implement them!
+        */
+
+       /* power management */
+       OID_PNP_CAPABILITIES,
+       OID_PNP_QUERY_POWER,
+       OID_PNP_SET_POWER,
+
+#ifdef RNDIS_WAKEUP
+       /* wake up host */
+       OID_PNP_ENABLE_WAKE_UP,
+       OID_PNP_ADD_WAKE_UP_PATTERN,
+       OID_PNP_REMOVE_WAKE_UP_PATTERN,
+#endif /* RNDIS_WAKEUP */
+#endif /* RNDIS_PM */
+};
+
+
+/* NDIS Functions */
+static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
+                              unsigned buf_len, rndis_resp_t *r)
+{
+       int retval = -ENOTSUPP;
+       u32 length = 4; /* usually */
+       __le32 *outbuf;
+       int i, count;
+       rndis_query_cmplt_type *resp;
+       struct net_device *net;
+       struct rtnl_link_stats64 temp;
+       const struct rtnl_link_stats64 *stats;
+
+       if (!r) return -ENOMEM;
+       resp = (rndis_query_cmplt_type *)r->buf;
+
+       if (!resp) return -ENOMEM;
+
+       if (buf_len && rndis_debug > 1) {
+               pr_debug("query OID %08x value, len %d:\n", OID, buf_len);
+               for (i = 0; i < buf_len; i += 16) {
+                       pr_debug("%03d: %08x %08x %08x %08x\n", i,
+                               get_unaligned_le32(&buf[i]),
+                               get_unaligned_le32(&buf[i + 4]),
+                               get_unaligned_le32(&buf[i + 8]),
+                               get_unaligned_le32(&buf[i + 12]));
+               }
+       }
+
+       /* response goes here, right after the header */
+       outbuf = (__le32 *)&resp[1];
+       resp->InformationBufferOffset = cpu_to_le32(16);
+
+       net = rndis_per_dev_params[configNr].dev;
+       stats = dev_get_stats(net, &temp);
+
+       switch (OID) {
+
+       /* general oids (table 4-1) */
+
+       /* mandatory */
+       case RNDIS_OID_GEN_SUPPORTED_LIST:
+               pr_debug("%s: RNDIS_OID_GEN_SUPPORTED_LIST\n", __func__);
+               length = sizeof(oid_supported_list);
+               count  = length / sizeof(u32);
+               for (i = 0; i < count; i++)
+                       outbuf[i] = cpu_to_le32(oid_supported_list[i]);
+               retval = 0;
+               break;
+
+       /* mandatory */
+       case RNDIS_OID_GEN_HARDWARE_STATUS:
+               pr_debug("%s: RNDIS_OID_GEN_HARDWARE_STATUS\n", __func__);
+               /* Bogus question!
+                * Hardware must be ready to receive high level protocols.
+                * BTW:
+                * reddite ergo quae sunt Caesaris Caesari
+                * et quae sunt Dei Deo!
+                */
+               *outbuf = cpu_to_le32(0);
+               retval = 0;
+               break;
+
+       /* mandatory */
+       case RNDIS_OID_GEN_MEDIA_SUPPORTED:
+               pr_debug("%s: RNDIS_OID_GEN_MEDIA_SUPPORTED\n", __func__);
+               *outbuf = cpu_to_le32(rndis_per_dev_params[configNr].medium);
+               retval = 0;
+               break;
+
+       /* mandatory */
+       case RNDIS_OID_GEN_MEDIA_IN_USE:
+               pr_debug("%s: RNDIS_OID_GEN_MEDIA_IN_USE\n", __func__);
+               /* one medium, one transport... (maybe you do it better) */
+               *outbuf = cpu_to_le32(rndis_per_dev_params[configNr].medium);
+               retval = 0;
+               break;
+
+       /* mandatory */
+       case RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE:
+               pr_debug("%s: RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE\n", __func__);
+               if (rndis_per_dev_params[configNr].dev) {
+                       *outbuf = cpu_to_le32(
+                               rndis_per_dev_params[configNr].dev->mtu);
+                       retval = 0;
+               }
+               break;
+
+       /* mandatory */
+       case RNDIS_OID_GEN_LINK_SPEED:
+               if (rndis_debug > 1)
+                       pr_debug("%s: RNDIS_OID_GEN_LINK_SPEED\n", __func__);
+               if (rndis_per_dev_params[configNr].media_state
+                               == RNDIS_MEDIA_STATE_DISCONNECTED)
+                       *outbuf = cpu_to_le32(0);
+               else
+                       *outbuf = cpu_to_le32(
+                               rndis_per_dev_params[configNr].speed);
+               retval = 0;
+               break;
+
+       /* mandatory */
+       case RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE:
+               pr_debug("%s: RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE\n", __func__);
+               if (rndis_per_dev_params[configNr].dev) {
+                       *outbuf = cpu_to_le32(
+                               rndis_per_dev_params[configNr].dev->mtu);
+                       retval = 0;
+               }
+               break;
+
+       /* mandatory */
+       case RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE:
+               pr_debug("%s: RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE\n", __func__);
+               if (rndis_per_dev_params[configNr].dev) {
+                       *outbuf = cpu_to_le32(
+                               rndis_per_dev_params[configNr].dev->mtu);
+                       retval = 0;
+               }
+               break;
+
+       /* mandatory */
+       case RNDIS_OID_GEN_VENDOR_ID:
+               pr_debug("%s: RNDIS_OID_GEN_VENDOR_ID\n", __func__);
+               *outbuf = cpu_to_le32(
+                       rndis_per_dev_params[configNr].vendorID);
+               retval = 0;
+               break;
+
+       /* mandatory */
+       case RNDIS_OID_GEN_VENDOR_DESCRIPTION:
+               pr_debug("%s: RNDIS_OID_GEN_VENDOR_DESCRIPTION\n", __func__);
+               if (rndis_per_dev_params[configNr].vendorDescr) {
+                       length = strlen(rndis_per_dev_params[configNr].
+                                       vendorDescr);
+                       memcpy(outbuf,
+                               rndis_per_dev_params[configNr].vendorDescr,
+                               length);
+               } else {
+                       outbuf[0] = 0;
+               }
+               retval = 0;
+               break;
+
+       case RNDIS_OID_GEN_VENDOR_DRIVER_VERSION:
+               pr_debug("%s: RNDIS_OID_GEN_VENDOR_DRIVER_VERSION\n", __func__);
+               /* Created as LE */
+               *outbuf = rndis_driver_version;
+               retval = 0;
+               break;
+
+       /* mandatory */
+       case RNDIS_OID_GEN_CURRENT_PACKET_FILTER:
+               pr_debug("%s: RNDIS_OID_GEN_CURRENT_PACKET_FILTER\n", __func__);
+               *outbuf = cpu_to_le32(*rndis_per_dev_params[configNr].filter);
+               retval = 0;
+               break;
+
+       /* mandatory */
+       case RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE:
+               pr_debug("%s: RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE\n", __func__);
+               *outbuf = cpu_to_le32(RNDIS_MAX_TOTAL_SIZE);
+               retval = 0;
+               break;
+
+       /* mandatory */
+       case RNDIS_OID_GEN_MEDIA_CONNECT_STATUS:
+               if (rndis_debug > 1)
+                       pr_debug("%s: RNDIS_OID_GEN_MEDIA_CONNECT_STATUS\n", __func__);
+               *outbuf = cpu_to_le32(rndis_per_dev_params[configNr]
+                                               .media_state);
+               retval = 0;
+               break;
+
+       case RNDIS_OID_GEN_PHYSICAL_MEDIUM:
+               pr_debug("%s: RNDIS_OID_GEN_PHYSICAL_MEDIUM\n", __func__);
+               *outbuf = cpu_to_le32(0);
+               retval = 0;
+               break;
+
+       /* The RNDIS specification is incomplete/wrong.   Some versions
+        * of MS-Windows expect OIDs that aren't specified there.  Other
+        * versions emit undefined RNDIS messages. DOCUMENT ALL THESE!
+        */
+       case RNDIS_OID_GEN_MAC_OPTIONS:         /* from WinME */
+               pr_debug("%s: RNDIS_OID_GEN_MAC_OPTIONS\n", __func__);
+               *outbuf = cpu_to_le32(
+                         RNDIS_MAC_OPTION_RECEIVE_SERIALIZED
+                       | RNDIS_MAC_OPTION_FULL_DUPLEX);
+               retval = 0;
+               break;
+
+       /* statistics OIDs (table 4-2) */
+
+       /* mandatory */
+       case RNDIS_OID_GEN_XMIT_OK:
+               if (rndis_debug > 1)
+                       pr_debug("%s: RNDIS_OID_GEN_XMIT_OK\n", __func__);
+               if (stats) {
+                       *outbuf = cpu_to_le32(stats->tx_packets
+                               - stats->tx_errors - stats->tx_dropped);
+                       retval = 0;
+               }
+               break;
+
+       /* mandatory */
+       case RNDIS_OID_GEN_RCV_OK:
+               if (rndis_debug > 1)
+                       pr_debug("%s: RNDIS_OID_GEN_RCV_OK\n", __func__);
+               if (stats) {
+                       *outbuf = cpu_to_le32(stats->rx_packets
+                               - stats->rx_errors - stats->rx_dropped);
+                       retval = 0;
+               }
+               break;
+
+       /* mandatory */
+       case RNDIS_OID_GEN_XMIT_ERROR:
+               if (rndis_debug > 1)
+                       pr_debug("%s: RNDIS_OID_GEN_XMIT_ERROR\n", __func__);
+               if (stats) {
+                       *outbuf = cpu_to_le32(stats->tx_errors);
+                       retval = 0;
+               }
+               break;
+
+       /* mandatory */
+       case RNDIS_OID_GEN_RCV_ERROR:
+               if (rndis_debug > 1)
+                       pr_debug("%s: RNDIS_OID_GEN_RCV_ERROR\n", __func__);
+               if (stats) {
+                       *outbuf = cpu_to_le32(stats->rx_errors);
+                       retval = 0;
+               }
+               break;
+
+       /* mandatory */
+       case RNDIS_OID_GEN_RCV_NO_BUFFER:
+               pr_debug("%s: RNDIS_OID_GEN_RCV_NO_BUFFER\n", __func__);
+               if (stats) {
+                       *outbuf = cpu_to_le32(stats->rx_dropped);
+                       retval = 0;
+               }
+               break;
+
+       /* ieee802.3 OIDs (table 4-3) */
+
+       /* mandatory */
+       case RNDIS_OID_802_3_PERMANENT_ADDRESS:
+               pr_debug("%s: RNDIS_OID_802_3_PERMANENT_ADDRESS\n", __func__);
+               if (rndis_per_dev_params[configNr].dev) {
+                       length = ETH_ALEN;
+                       memcpy(outbuf,
+                               rndis_per_dev_params[configNr].host_mac,
+                               length);
+                       retval = 0;
+               }
+               break;
+
+       /* mandatory */
+       case RNDIS_OID_802_3_CURRENT_ADDRESS:
+               pr_debug("%s: RNDIS_OID_802_3_CURRENT_ADDRESS\n", __func__);
+               if (rndis_per_dev_params[configNr].dev) {
+                       length = ETH_ALEN;
+                       memcpy(outbuf,
+                               rndis_per_dev_params [configNr].host_mac,
+                               length);
+                       retval = 0;
+               }
+               break;
+
+       /* mandatory */
+       case RNDIS_OID_802_3_MULTICAST_LIST:
+               pr_debug("%s: RNDIS_OID_802_3_MULTICAST_LIST\n", __func__);
+               /* Multicast base address only */
+               *outbuf = cpu_to_le32(0xE0000000);
+               retval = 0;
+               break;
+
+       /* mandatory */
+       case RNDIS_OID_802_3_MAXIMUM_LIST_SIZE:
+               pr_debug("%s: RNDIS_OID_802_3_MAXIMUM_LIST_SIZE\n", __func__);
+               /* Multicast base address only */
+               *outbuf = cpu_to_le32(1);
+               retval = 0;
+               break;
+
+       case RNDIS_OID_802_3_MAC_OPTIONS:
+               pr_debug("%s: RNDIS_OID_802_3_MAC_OPTIONS\n", __func__);
+               *outbuf = cpu_to_le32(0);
+               retval = 0;
+               break;
+
+       /* ieee802.3 statistics OIDs (table 4-4) */
+
+       /* mandatory */
+       case RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT:
+               pr_debug("%s: RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT\n", __func__);
+               if (stats) {
+                       *outbuf = cpu_to_le32(stats->rx_frame_errors);
+                       retval = 0;
+               }
+               break;
+
+       /* mandatory */
+       case RNDIS_OID_802_3_XMIT_ONE_COLLISION:
+               pr_debug("%s: RNDIS_OID_802_3_XMIT_ONE_COLLISION\n", __func__);
+               *outbuf = cpu_to_le32(0);
+               retval = 0;
+               break;
+
+       /* mandatory */
+       case RNDIS_OID_802_3_XMIT_MORE_COLLISIONS:
+               pr_debug("%s: RNDIS_OID_802_3_XMIT_MORE_COLLISIONS\n", __func__);
+               *outbuf = cpu_to_le32(0);
+               retval = 0;
+               break;
+
+       default:
+               pr_warning("%s: query unknown OID 0x%08X\n",
+                        __func__, OID);
+       }
+       if (retval < 0)
+               length = 0;
+
+       resp->InformationBufferLength = cpu_to_le32(length);
+       r->length = length + sizeof(*resp);
+       resp->MessageLength = cpu_to_le32(r->length);
+       return retval;
+}
+
+static int gen_ndis_set_resp(u8 configNr, u32 OID, u8 *buf, u32 buf_len,
+                            rndis_resp_t *r)
+{
+       rndis_set_cmplt_type *resp;
+       int i, retval = -ENOTSUPP;
+       struct rndis_params *params;
+
+       if (!r)
+               return -ENOMEM;
+       resp = (rndis_set_cmplt_type *)r->buf;
+       if (!resp)
+               return -ENOMEM;
+
+       if (buf_len && rndis_debug > 1) {
+               pr_debug("set OID %08x value, len %d:\n", OID, buf_len);
+               for (i = 0; i < buf_len; i += 16) {
+                       pr_debug("%03d: %08x %08x %08x %08x\n", i,
+                               get_unaligned_le32(&buf[i]),
+                               get_unaligned_le32(&buf[i + 4]),
+                               get_unaligned_le32(&buf[i + 8]),
+                               get_unaligned_le32(&buf[i + 12]));
+               }
+       }
+
+       params = &rndis_per_dev_params[configNr];
+       switch (OID) {
+       case RNDIS_OID_GEN_CURRENT_PACKET_FILTER:
+
+               /* these NDIS_PACKET_TYPE_* bitflags are shared with
+                * cdc_filter; it's not RNDIS-specific
+                * NDIS_PACKET_TYPE_x == USB_CDC_PACKET_TYPE_x for x in:
+                *      PROMISCUOUS, DIRECTED,
+                *      MULTICAST, ALL_MULTICAST, BROADCAST
+                */
+               *params->filter = (u16)get_unaligned_le32(buf);
+               pr_debug("%s: RNDIS_OID_GEN_CURRENT_PACKET_FILTER %08x\n",
+                       __func__, *params->filter);
+
+               /* this call has a significant side effect:  it's
+                * what makes the packet flow start and stop, like
+                * activating the CDC Ethernet altsetting.
+                */
+               retval = 0;
+               if (*params->filter) {
+                       params->state = RNDIS_DATA_INITIALIZED;
+                       netif_carrier_on(params->dev);
+                       if (netif_running(params->dev))
+                               netif_wake_queue(params->dev);
+               } else {
+                       params->state = RNDIS_INITIALIZED;
+                       netif_carrier_off(params->dev);
+                       netif_stop_queue(params->dev);
+               }
+               break;
+
+       case RNDIS_OID_802_3_MULTICAST_LIST:
+               /* I think we can ignore this */
+               pr_debug("%s: RNDIS_OID_802_3_MULTICAST_LIST\n", __func__);
+               retval = 0;
+               break;
+
+       default:
+               pr_warning("%s: set unknown OID 0x%08X, size %d\n",
+                        __func__, OID, buf_len);
+       }
+
+       return retval;
+}
+
+/*
+ * Response Functions
+ */
+
+static int rndis_init_response(int configNr, rndis_init_msg_type *buf)
+{
+       rndis_init_cmplt_type *resp;
+       rndis_resp_t *r;
+       struct rndis_params *params = rndis_per_dev_params + configNr;
+
+       if (!params->dev)
+               return -ENOTSUPP;
+
+       r = rndis_add_response(configNr, sizeof(rndis_init_cmplt_type));
+       if (!r)
+               return -ENOMEM;
+       resp = (rndis_init_cmplt_type *)r->buf;
+
+       resp->MessageType = cpu_to_le32(RNDIS_MSG_INIT_C);
+       resp->MessageLength = cpu_to_le32(52);
+       resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+       resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+       resp->MajorVersion = cpu_to_le32(RNDIS_MAJOR_VERSION);
+       resp->MinorVersion = cpu_to_le32(RNDIS_MINOR_VERSION);
+       resp->DeviceFlags = cpu_to_le32(RNDIS_DF_CONNECTIONLESS);
+       resp->Medium = cpu_to_le32(RNDIS_MEDIUM_802_3);
+       resp->MaxPacketsPerTransfer = cpu_to_le32(1);
+       resp->MaxTransferSize = cpu_to_le32(
+                 params->dev->mtu
+               + sizeof(struct ethhdr)
+               + sizeof(struct rndis_packet_msg_type)
+               + 22);
+       resp->PacketAlignmentFactor = cpu_to_le32(0);
+       resp->AFListOffset = cpu_to_le32(0);
+       resp->AFListSize = cpu_to_le32(0);
+
+       params->resp_avail(params->v);
+       return 0;
+}
+
+static int rndis_query_response(int configNr, rndis_query_msg_type *buf)
+{
+       rndis_query_cmplt_type *resp;
+       rndis_resp_t *r;
+       struct rndis_params *params = rndis_per_dev_params + configNr;
+
+       /* pr_debug("%s: OID = %08X\n", __func__, cpu_to_le32(buf->OID)); */
+       if (!params->dev)
+               return -ENOTSUPP;
+
+       /*
+        * we need more memory:
+        * gen_ndis_query_resp expects enough space for
+        * rndis_query_cmplt_type followed by data.
+        * oid_supported_list is the largest data reply
+        */
+       r = rndis_add_response(configNr,
+               sizeof(oid_supported_list) + sizeof(rndis_query_cmplt_type));
+       if (!r)
+               return -ENOMEM;
+       resp = (rndis_query_cmplt_type *)r->buf;
+
+       resp->MessageType = cpu_to_le32(RNDIS_MSG_QUERY_C);
+       resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+
+       if (gen_ndis_query_resp(configNr, le32_to_cpu(buf->OID),
+                       le32_to_cpu(buf->InformationBufferOffset)
+                                       + 8 + (u8 *)buf,
+                       le32_to_cpu(buf->InformationBufferLength),
+                       r)) {
+               /* OID not supported */
+               resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED);
+               resp->MessageLength = cpu_to_le32(sizeof *resp);
+               resp->InformationBufferLength = cpu_to_le32(0);
+               resp->InformationBufferOffset = cpu_to_le32(0);
+       } else
+               resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+
+       params->resp_avail(params->v);
+       return 0;
+}
+
+static int rndis_set_response(int configNr, rndis_set_msg_type *buf)
+{
+       u32 BufLength, BufOffset;
+       rndis_set_cmplt_type *resp;
+       rndis_resp_t *r;
+       struct rndis_params *params = rndis_per_dev_params + configNr;
+
+       r = rndis_add_response(configNr, sizeof(rndis_set_cmplt_type));
+       if (!r)
+               return -ENOMEM;
+       resp = (rndis_set_cmplt_type *)r->buf;
+
+       BufLength = le32_to_cpu(buf->InformationBufferLength);
+       BufOffset = le32_to_cpu(buf->InformationBufferOffset);
+
+#ifdef VERBOSE_DEBUG
+       pr_debug("%s: Length: %d\n", __func__, BufLength);
+       pr_debug("%s: Offset: %d\n", __func__, BufOffset);
+       pr_debug("%s: InfoBuffer: ", __func__);
+
+       for (i = 0; i < BufLength; i++) {
+               pr_debug("%02x ", *(((u8 *) buf) + i + 8 + BufOffset));
+       }
+
+       pr_debug("\n");
+#endif
+
+       resp->MessageType = cpu_to_le32(RNDIS_MSG_SET_C);
+       resp->MessageLength = cpu_to_le32(16);
+       resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+       if (gen_ndis_set_resp(configNr, le32_to_cpu(buf->OID),
+                       ((u8 *)buf) + 8 + BufOffset, BufLength, r))
+               resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED);
+       else
+               resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+
+       params->resp_avail(params->v);
+       return 0;
+}
+
+static int rndis_reset_response(int configNr, rndis_reset_msg_type *buf)
+{
+       rndis_reset_cmplt_type *resp;
+       rndis_resp_t *r;
+       struct rndis_params *params = rndis_per_dev_params + configNr;
+
+       r = rndis_add_response(configNr, sizeof(rndis_reset_cmplt_type));
+       if (!r)
+               return -ENOMEM;
+       resp = (rndis_reset_cmplt_type *)r->buf;
+
+       resp->MessageType = cpu_to_le32(RNDIS_MSG_RESET_C);
+       resp->MessageLength = cpu_to_le32(16);
+       resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+       /* resent information */
+       resp->AddressingReset = cpu_to_le32(1);
+
+       params->resp_avail(params->v);
+       return 0;
+}
+
+static int rndis_keepalive_response(int configNr,
+                                   rndis_keepalive_msg_type *buf)
+{
+       rndis_keepalive_cmplt_type *resp;
+       rndis_resp_t *r;
+       struct rndis_params *params = rndis_per_dev_params + configNr;
+
+       /* host "should" check only in RNDIS_DATA_INITIALIZED state */
+
+       r = rndis_add_response(configNr, sizeof(rndis_keepalive_cmplt_type));
+       if (!r)
+               return -ENOMEM;
+       resp = (rndis_keepalive_cmplt_type *)r->buf;
+
+       resp->MessageType = cpu_to_le32(RNDIS_MSG_KEEPALIVE_C);
+       resp->MessageLength = cpu_to_le32(16);
+       resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+       resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+
+       params->resp_avail(params->v);
+       return 0;
+}
+
+
+/*
+ * Device to Host Comunication
+ */
+static int rndis_indicate_status_msg(int configNr, u32 status)
+{
+       rndis_indicate_status_msg_type *resp;
+       rndis_resp_t *r;
+       struct rndis_params *params = rndis_per_dev_params + configNr;
+
+       if (params->state == RNDIS_UNINITIALIZED)
+               return -ENOTSUPP;
+
+       r = rndis_add_response(configNr,
+                               sizeof(rndis_indicate_status_msg_type));
+       if (!r)
+               return -ENOMEM;
+       resp = (rndis_indicate_status_msg_type *)r->buf;
+
+       resp->MessageType = cpu_to_le32(RNDIS_MSG_INDICATE);
+       resp->MessageLength = cpu_to_le32(20);
+       resp->Status = cpu_to_le32(status);
+       resp->StatusBufferLength = cpu_to_le32(0);
+       resp->StatusBufferOffset = cpu_to_le32(0);
+
+       params->resp_avail(params->v);
+       return 0;
+}
+
+int rndis_signal_connect(int configNr)
+{
+       rndis_per_dev_params[configNr].media_state
+                       = RNDIS_MEDIA_STATE_CONNECTED;
+       return rndis_indicate_status_msg(configNr,
+                                         RNDIS_STATUS_MEDIA_CONNECT);
+}
+EXPORT_SYMBOL_GPL(rndis_signal_connect);
+
+int rndis_signal_disconnect(int configNr)
+{
+       rndis_per_dev_params[configNr].media_state
+                       = RNDIS_MEDIA_STATE_DISCONNECTED;
+       return rndis_indicate_status_msg(configNr,
+                                         RNDIS_STATUS_MEDIA_DISCONNECT);
+}
+EXPORT_SYMBOL_GPL(rndis_signal_disconnect);
+
+void rndis_uninit(int configNr)
+{
+       u8 *buf;
+       u32 length;
+
+       if (configNr >= RNDIS_MAX_CONFIGS)
+               return;
+       rndis_per_dev_params[configNr].state = RNDIS_UNINITIALIZED;
+
+       /* drain the response queue */
+       while ((buf = rndis_get_next_response(configNr, &length)))
+               rndis_free_response(configNr, buf);
+}
+EXPORT_SYMBOL_GPL(rndis_uninit);
+
+void rndis_set_host_mac(int configNr, const u8 *addr)
+{
+       rndis_per_dev_params[configNr].host_mac = addr;
+}
+EXPORT_SYMBOL_GPL(rndis_set_host_mac);
+
+/*
+ * Message Parser
+ */
+int rndis_msg_parser(u8 configNr, u8 *buf)
+{
+       u32 MsgType, MsgLength;
+       __le32 *tmp;
+       struct rndis_params *params;
+
+       if (!buf)
+               return -ENOMEM;
+
+       tmp = (__le32 *)buf;
+       MsgType   = get_unaligned_le32(tmp++);
+       MsgLength = get_unaligned_le32(tmp++);
+
+       if (configNr >= RNDIS_MAX_CONFIGS)
+               return -ENOTSUPP;
+       params = &rndis_per_dev_params[configNr];
+
+       /* NOTE: RNDIS is *EXTREMELY* chatty ... Windows constantly polls for
+        * rx/tx statistics and link status, in addition to KEEPALIVE traffic
+        * and normal HC level polling to see if there's any IN traffic.
+        */
+
+       /* For USB: responses may take up to 10 seconds */
+       switch (MsgType) {
+       case RNDIS_MSG_INIT:
+               pr_debug("%s: RNDIS_MSG_INIT\n",
+                       __func__);
+               params->state = RNDIS_INITIALIZED;
+               return rndis_init_response(configNr,
+                                       (rndis_init_msg_type *)buf);
+
+       case RNDIS_MSG_HALT:
+               pr_debug("%s: RNDIS_MSG_HALT\n",
+                       __func__);
+               params->state = RNDIS_UNINITIALIZED;
+               if (params->dev) {
+                       netif_carrier_off(params->dev);
+                       netif_stop_queue(params->dev);
+               }
+               return 0;
+
+       case RNDIS_MSG_QUERY:
+               return rndis_query_response(configNr,
+                                       (rndis_query_msg_type *)buf);
+
+       case RNDIS_MSG_SET:
+               return rndis_set_response(configNr,
+                                       (rndis_set_msg_type *)buf);
+
+       case RNDIS_MSG_RESET:
+               pr_debug("%s: RNDIS_MSG_RESET\n",
+                       __func__);
+               return rndis_reset_response(configNr,
+                                       (rndis_reset_msg_type *)buf);
+
+       case RNDIS_MSG_KEEPALIVE:
+               /* For USB: host does this every 5 seconds */
+               if (rndis_debug > 1)
+                       pr_debug("%s: RNDIS_MSG_KEEPALIVE\n",
+                               __func__);
+               return rndis_keepalive_response(configNr,
+                                                (rndis_keepalive_msg_type *)
+                                                buf);
+
+       default:
+               /* At least Windows XP emits some undefined RNDIS messages.
+                * In one case those messages seemed to relate to the host
+                * suspending itself.
+                */
+               pr_warning("%s: unknown RNDIS message 0x%08X len %d\n",
+                       __func__, MsgType, MsgLength);
+               print_hex_dump_bytes(__func__, DUMP_PREFIX_OFFSET,
+                                    buf, MsgLength);
+               break;
+       }
+
+       return -ENOTSUPP;
+}
+EXPORT_SYMBOL_GPL(rndis_msg_parser);
+
+int rndis_register(void (*resp_avail)(void *v), void *v)
+{
+       u8 i;
+
+       if (!resp_avail)
+               return -EINVAL;
+
+       for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
+               if (!rndis_per_dev_params[i].used) {
+                       rndis_per_dev_params[i].used = 1;
+                       rndis_per_dev_params[i].resp_avail = resp_avail;
+                       rndis_per_dev_params[i].v = v;
+                       pr_debug("%s: configNr = %d\n", __func__, i);
+                       return i;
+               }
+       }
+       pr_debug("failed\n");
+
+       return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(rndis_register);
+
+void rndis_deregister(int configNr)
+{
+       pr_debug("%s:\n", __func__);
+
+       if (configNr >= RNDIS_MAX_CONFIGS) return;
+       rndis_per_dev_params[configNr].used = 0;
+}
+EXPORT_SYMBOL_GPL(rndis_deregister);
+
+int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter)
+{
+       pr_debug("%s:\n", __func__);
+       if (!dev)
+               return -EINVAL;
+       if (configNr >= RNDIS_MAX_CONFIGS) return -1;
+
+       rndis_per_dev_params[configNr].dev = dev;
+       rndis_per_dev_params[configNr].filter = cdc_filter;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rndis_set_param_dev);
+
+int rndis_set_param_vendor(u8 configNr, u32 vendorID, const char *vendorDescr)
+{
+       pr_debug("%s:\n", __func__);
+       if (!vendorDescr) return -1;
+       if (configNr >= RNDIS_MAX_CONFIGS) return -1;
+
+       rndis_per_dev_params[configNr].vendorID = vendorID;
+       rndis_per_dev_params[configNr].vendorDescr = vendorDescr;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rndis_set_param_vendor);
+
+int rndis_set_param_medium(u8 configNr, u32 medium, u32 speed)
+{
+       pr_debug("%s: %u %u\n", __func__, medium, speed);
+       if (configNr >= RNDIS_MAX_CONFIGS) return -1;
+
+       rndis_per_dev_params[configNr].medium = medium;
+       rndis_per_dev_params[configNr].speed = speed;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rndis_set_param_medium);
+
+void rndis_add_hdr(struct sk_buff *skb)
+{
+       struct rndis_packet_msg_type *header;
+
+       if (!skb)
+               return;
+       header = (void *)skb_push(skb, sizeof(*header));
+       memset(header, 0, sizeof *header);
+       header->MessageType = cpu_to_le32(RNDIS_MSG_PACKET);
+       header->MessageLength = cpu_to_le32(skb->len);
+       header->DataOffset = cpu_to_le32(36);
+       header->DataLength = cpu_to_le32(skb->len - sizeof(*header));
+}
+EXPORT_SYMBOL_GPL(rndis_add_hdr);
+
+void rndis_free_response(int configNr, u8 *buf)
+{
+       rndis_resp_t *r;
+       struct list_head *act, *tmp;
+
+       list_for_each_safe(act, tmp,
+                       &(rndis_per_dev_params[configNr].resp_queue))
+       {
+               r = list_entry(act, rndis_resp_t, list);
+               if (r && r->buf == buf) {
+                       list_del(&r->list);
+                       kfree(r);
+               }
+       }
+}
+EXPORT_SYMBOL_GPL(rndis_free_response);
+
+u8 *rndis_get_next_response(int configNr, u32 *length)
+{
+       rndis_resp_t *r;
+       struct list_head *act, *tmp;
+
+       if (!length) return NULL;
+
+       list_for_each_safe(act, tmp,
+                       &(rndis_per_dev_params[configNr].resp_queue))
+       {
+               r = list_entry(act, rndis_resp_t, list);
+               if (!r->send) {
+                       r->send = 1;
+                       *length = r->length;
+                       return r->buf;
+               }
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(rndis_get_next_response);
+
+static rndis_resp_t *rndis_add_response(int configNr, u32 length)
+{
+       rndis_resp_t *r;
+
+       /* NOTE: this gets copied into ether.c USB_BUFSIZ bytes ... */
+       r = kmalloc(sizeof(rndis_resp_t) + length, GFP_ATOMIC);
+       if (!r) return NULL;
+
+       r->buf = (u8 *)(r + 1);
+       r->length = length;
+       r->send = 0;
+
+       list_add_tail(&r->list,
+               &(rndis_per_dev_params[configNr].resp_queue));
+       return r;
+}
+
+int rndis_rm_hdr(struct gether *port,
+                       struct sk_buff *skb,
+                       struct sk_buff_head *list)
+{
+       /* tmp points to a struct rndis_packet_msg_type */
+       __le32 *tmp = (void *)skb->data;
+
+       /* MessageType, MessageLength */
+       if (cpu_to_le32(RNDIS_MSG_PACKET)
+                       != get_unaligned(tmp++)) {
+               dev_kfree_skb_any(skb);
+               return -EINVAL;
+       }
+       tmp++;
+
+       /* DataOffset, DataLength */
+       if (!skb_pull(skb, get_unaligned_le32(tmp++) + 8)) {
+               dev_kfree_skb_any(skb);
+               return -EOVERFLOW;
+       }
+       skb_trim(skb, get_unaligned_le32(tmp++));
+
+       skb_queue_tail(list, skb);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rndis_rm_hdr);
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+static int rndis_proc_show(struct seq_file *m, void *v)
+{
+       rndis_params *param = m->private;
+
+       seq_printf(m,
+                        "Config Nr. %d\n"
+                        "used      : %s\n"
+                        "state     : %s\n"
+                        "medium    : 0x%08X\n"
+                        "speed     : %d\n"
+                        "cable     : %s\n"
+                        "vendor ID : 0x%08X\n"
+                        "vendor    : %s\n",
+                        param->confignr, (param->used) ? "y" : "n",
+                        ({ char *s = "?";
+                        switch (param->state) {
+                        case RNDIS_UNINITIALIZED:
+                               s = "RNDIS_UNINITIALIZED"; break;
+                        case RNDIS_INITIALIZED:
+                               s = "RNDIS_INITIALIZED"; break;
+                        case RNDIS_DATA_INITIALIZED:
+                               s = "RNDIS_DATA_INITIALIZED"; break;
+                       } s; }),
+                        param->medium,
+                        (param->media_state) ? 0 : param->speed*100,
+                        (param->media_state) ? "disconnected" : "connected",
+                        param->vendorID, param->vendorDescr);
+       return 0;
+}
+
+static ssize_t rndis_proc_write(struct file *file, const char __user *buffer,
+                               size_t count, loff_t *ppos)
+{
+       rndis_params *p = PDE_DATA(file_inode(file));
+       u32 speed = 0;
+       int i, fl_speed = 0;
+
+       for (i = 0; i < count; i++) {
+               char c;
+               if (get_user(c, buffer))
+                       return -EFAULT;
+               switch (c) {
+               case '0':
+               case '1':
+               case '2':
+               case '3':
+               case '4':
+               case '5':
+               case '6':
+               case '7':
+               case '8':
+               case '9':
+                       fl_speed = 1;
+                       speed = speed * 10 + c - '0';
+                       break;
+               case 'C':
+               case 'c':
+                       rndis_signal_connect(p->confignr);
+                       break;
+               case 'D':
+               case 'd':
+                       rndis_signal_disconnect(p->confignr);
+                       break;
+               default:
+                       if (fl_speed) p->speed = speed;
+                       else pr_debug("%c is not valid\n", c);
+                       break;
+               }
+
+               buffer++;
+       }
+
+       return count;
+}
+
+static int rndis_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, rndis_proc_show, PDE_DATA(inode));
+}
+
+static const struct file_operations rndis_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = rndis_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+       .write          = rndis_proc_write,
+};
+
+#define        NAME_TEMPLATE "driver/rndis-%03d"
+
+static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS];
+
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
+
+
+int rndis_init(void)
+{
+       u8 i;
+
+       for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+               char name [20];
+
+               sprintf(name, NAME_TEMPLATE, i);
+               rndis_connect_state[i] = proc_create_data(name, 0660, NULL,
+                                       &rndis_proc_fops,
+                                       (void *)(rndis_per_dev_params + i));
+               if (!rndis_connect_state[i]) {
+                       pr_debug("%s: remove entries", __func__);
+                       while (i) {
+                               sprintf(name, NAME_TEMPLATE, --i);
+                               remove_proc_entry(name, NULL);
+                       }
+                       pr_debug("\n");
+                       return -EIO;
+               }
+#endif
+               rndis_per_dev_params[i].confignr = i;
+               rndis_per_dev_params[i].used = 0;
+               rndis_per_dev_params[i].state = RNDIS_UNINITIALIZED;
+               rndis_per_dev_params[i].media_state
+                               = RNDIS_MEDIA_STATE_DISCONNECTED;
+               INIT_LIST_HEAD(&(rndis_per_dev_params[i].resp_queue));
+       }
+
+       return 0;
+}
+
+void rndis_exit(void)
+{
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+       u8 i;
+       char name[20];
+
+       for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
+               sprintf(name, NAME_TEMPLATE, i);
+               remove_proc_entry(name, NULL);
+       }
+#endif
+}
+
diff --git a/drivers/usb/gadget/function/rndis.h b/drivers/usb/gadget/function/rndis.h
new file mode 100644 (file)
index 0000000..0f4abb4
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * RNDIS       Definitions for Remote NDIS
+ *
+ * Authors:    Benedikt Spranger, Pengutronix
+ *             Robert Schwebel, Pengutronix
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             version 2, as published by the Free Software Foundation.
+ *
+ *             This software was originally developed in conformance with
+ *             Microsoft's Remote NDIS Specification License Agreement.
+ */
+
+#ifndef _LINUX_RNDIS_H
+#define _LINUX_RNDIS_H
+
+#include <linux/rndis.h>
+#include "u_ether.h"
+#include "ndis.h"
+
+#define RNDIS_MAXIMUM_FRAME_SIZE       1518
+#define RNDIS_MAX_TOTAL_SIZE           1558
+
+typedef struct rndis_init_msg_type
+{
+       __le32  MessageType;
+       __le32  MessageLength;
+       __le32  RequestID;
+       __le32  MajorVersion;
+       __le32  MinorVersion;
+       __le32  MaxTransferSize;
+} rndis_init_msg_type;
+
+typedef struct rndis_init_cmplt_type
+{
+       __le32  MessageType;
+       __le32  MessageLength;
+       __le32  RequestID;
+       __le32  Status;
+       __le32  MajorVersion;
+       __le32  MinorVersion;
+       __le32  DeviceFlags;
+       __le32  Medium;
+       __le32  MaxPacketsPerTransfer;
+       __le32  MaxTransferSize;
+       __le32  PacketAlignmentFactor;
+       __le32  AFListOffset;
+       __le32  AFListSize;
+} rndis_init_cmplt_type;
+
+typedef struct rndis_halt_msg_type
+{
+       __le32  MessageType;
+       __le32  MessageLength;
+       __le32  RequestID;
+} rndis_halt_msg_type;
+
+typedef struct rndis_query_msg_type
+{
+       __le32  MessageType;
+       __le32  MessageLength;
+       __le32  RequestID;
+       __le32  OID;
+       __le32  InformationBufferLength;
+       __le32  InformationBufferOffset;
+       __le32  DeviceVcHandle;
+} rndis_query_msg_type;
+
+typedef struct rndis_query_cmplt_type
+{
+       __le32  MessageType;
+       __le32  MessageLength;
+       __le32  RequestID;
+       __le32  Status;
+       __le32  InformationBufferLength;
+       __le32  InformationBufferOffset;
+} rndis_query_cmplt_type;
+
+typedef struct rndis_set_msg_type
+{
+       __le32  MessageType;
+       __le32  MessageLength;
+       __le32  RequestID;
+       __le32  OID;
+       __le32  InformationBufferLength;
+       __le32  InformationBufferOffset;
+       __le32  DeviceVcHandle;
+} rndis_set_msg_type;
+
+typedef struct rndis_set_cmplt_type
+{
+       __le32  MessageType;
+       __le32  MessageLength;
+       __le32  RequestID;
+       __le32  Status;
+} rndis_set_cmplt_type;
+
+typedef struct rndis_reset_msg_type
+{
+       __le32  MessageType;
+       __le32  MessageLength;
+       __le32  Reserved;
+} rndis_reset_msg_type;
+
+typedef struct rndis_reset_cmplt_type
+{
+       __le32  MessageType;
+       __le32  MessageLength;
+       __le32  Status;
+       __le32  AddressingReset;
+} rndis_reset_cmplt_type;
+
+typedef struct rndis_indicate_status_msg_type
+{
+       __le32  MessageType;
+       __le32  MessageLength;
+       __le32  Status;
+       __le32  StatusBufferLength;
+       __le32  StatusBufferOffset;
+} rndis_indicate_status_msg_type;
+
+typedef struct rndis_keepalive_msg_type
+{
+       __le32  MessageType;
+       __le32  MessageLength;
+       __le32  RequestID;
+} rndis_keepalive_msg_type;
+
+typedef struct rndis_keepalive_cmplt_type
+{
+       __le32  MessageType;
+       __le32  MessageLength;
+       __le32  RequestID;
+       __le32  Status;
+} rndis_keepalive_cmplt_type;
+
+struct rndis_packet_msg_type
+{
+       __le32  MessageType;
+       __le32  MessageLength;
+       __le32  DataOffset;
+       __le32  DataLength;
+       __le32  OOBDataOffset;
+       __le32  OOBDataLength;
+       __le32  NumOOBDataElements;
+       __le32  PerPacketInfoOffset;
+       __le32  PerPacketInfoLength;
+       __le32  VcHandle;
+       __le32  Reserved;
+} __attribute__ ((packed));
+
+struct rndis_config_parameter
+{
+       __le32  ParameterNameOffset;
+       __le32  ParameterNameLength;
+       __le32  ParameterType;
+       __le32  ParameterValueOffset;
+       __le32  ParameterValueLength;
+};
+
+/* implementation specific */
+enum rndis_state
+{
+       RNDIS_UNINITIALIZED,
+       RNDIS_INITIALIZED,
+       RNDIS_DATA_INITIALIZED,
+};
+
+typedef struct rndis_resp_t
+{
+       struct list_head        list;
+       u8                      *buf;
+       u32                     length;
+       int                     send;
+} rndis_resp_t;
+
+typedef struct rndis_params
+{
+       u8                      confignr;
+       u8                      used;
+       u16                     saved_filter;
+       enum rndis_state        state;
+       u32                     medium;
+       u32                     speed;
+       u32                     media_state;
+
+       const u8                *host_mac;
+       u16                     *filter;
+       struct net_device       *dev;
+
+       u32                     vendorID;
+       const char              *vendorDescr;
+       void                    (*resp_avail)(void *v);
+       void                    *v;
+       struct list_head        resp_queue;
+} rndis_params;
+
+/* RNDIS Message parser and other useless functions */
+int  rndis_msg_parser (u8 configNr, u8 *buf);
+int  rndis_register(void (*resp_avail)(void *v), void *v);
+void rndis_deregister (int configNr);
+int  rndis_set_param_dev (u8 configNr, struct net_device *dev,
+                        u16 *cdc_filter);
+int  rndis_set_param_vendor (u8 configNr, u32 vendorID,
+                           const char *vendorDescr);
+int  rndis_set_param_medium (u8 configNr, u32 medium, u32 speed);
+void rndis_add_hdr (struct sk_buff *skb);
+int rndis_rm_hdr(struct gether *port, struct sk_buff *skb,
+                       struct sk_buff_head *list);
+u8   *rndis_get_next_response (int configNr, u32 *length);
+void rndis_free_response (int configNr, u8 *buf);
+
+void rndis_uninit (int configNr);
+int  rndis_signal_connect (int configNr);
+int  rndis_signal_disconnect (int configNr);
+int  rndis_state (int configNr);
+extern void rndis_set_host_mac (int configNr, const u8 *addr);
+
+#endif  /* _LINUX_RNDIS_H */
diff --git a/drivers/usb/gadget/function/storage_common.c b/drivers/usb/gadget/function/storage_common.c
new file mode 100644 (file)
index 0000000..648f9e4
--- /dev/null
@@ -0,0 +1,504 @@
+/*
+ * storage_common.c -- Common definitions for mass storage functionality
+ *
+ * Copyright (C) 2003-2008 Alan Stern
+ * Copyeight (C) 2009 Samsung Electronics
+ * Author: Michal Nazarewicz (mina86@mina86.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+/*
+ * This file requires the following identifiers used in USB strings to
+ * be defined (each of type pointer to char):
+ *  - fsg_string_interface    -- name of the interface
+ */
+
+/*
+ * When USB_GADGET_DEBUG_FILES is defined the module param num_buffers
+ * sets the number of pipeline buffers (length of the fsg_buffhd array).
+ * The valid range of num_buffers is: num >= 2 && num <= 4.
+ */
+
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/usb/composite.h>
+
+#include "storage_common.h"
+
+/* There is only one interface. */
+
+struct usb_interface_descriptor fsg_intf_desc = {
+       .bLength =              sizeof fsg_intf_desc,
+       .bDescriptorType =      USB_DT_INTERFACE,
+
+       .bNumEndpoints =        2,              /* Adjusted during fsg_bind() */
+       .bInterfaceClass =      USB_CLASS_MASS_STORAGE,
+       .bInterfaceSubClass =   USB_SC_SCSI,    /* Adjusted during fsg_bind() */
+       .bInterfaceProtocol =   USB_PR_BULK,    /* Adjusted during fsg_bind() */
+       .iInterface =           FSG_STRING_INTERFACE,
+};
+EXPORT_SYMBOL_GPL(fsg_intf_desc);
+
+/*
+ * Three full-speed endpoint descriptors: bulk-in, bulk-out, and
+ * interrupt-in.
+ */
+
+struct usb_endpoint_descriptor fsg_fs_bulk_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       /* wMaxPacketSize set by autoconfiguration */
+};
+EXPORT_SYMBOL_GPL(fsg_fs_bulk_in_desc);
+
+struct usb_endpoint_descriptor fsg_fs_bulk_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       /* wMaxPacketSize set by autoconfiguration */
+};
+EXPORT_SYMBOL_GPL(fsg_fs_bulk_out_desc);
+
+struct usb_descriptor_header *fsg_fs_function[] = {
+       (struct usb_descriptor_header *) &fsg_intf_desc,
+       (struct usb_descriptor_header *) &fsg_fs_bulk_in_desc,
+       (struct usb_descriptor_header *) &fsg_fs_bulk_out_desc,
+       NULL,
+};
+EXPORT_SYMBOL_GPL(fsg_fs_function);
+
+
+/*
+ * USB 2.0 devices need to expose both high speed and full speed
+ * descriptors, unless they only run at full speed.
+ *
+ * That means alternate endpoint descriptors (bigger packets)
+ * and a "device qualifier" ... plus more construction options
+ * for the configuration descriptor.
+ */
+struct usb_endpoint_descriptor fsg_hs_bulk_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(512),
+};
+EXPORT_SYMBOL_GPL(fsg_hs_bulk_in_desc);
+
+struct usb_endpoint_descriptor fsg_hs_bulk_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(512),
+       .bInterval =            1,      /* NAK every 1 uframe */
+};
+EXPORT_SYMBOL_GPL(fsg_hs_bulk_out_desc);
+
+
+struct usb_descriptor_header *fsg_hs_function[] = {
+       (struct usb_descriptor_header *) &fsg_intf_desc,
+       (struct usb_descriptor_header *) &fsg_hs_bulk_in_desc,
+       (struct usb_descriptor_header *) &fsg_hs_bulk_out_desc,
+       NULL,
+};
+EXPORT_SYMBOL_GPL(fsg_hs_function);
+
+struct usb_endpoint_descriptor fsg_ss_bulk_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+EXPORT_SYMBOL_GPL(fsg_ss_bulk_in_desc);
+
+struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = {
+       .bLength =              sizeof(fsg_ss_bulk_in_comp_desc),
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+
+       /*.bMaxBurst =          DYNAMIC, */
+};
+EXPORT_SYMBOL_GPL(fsg_ss_bulk_in_comp_desc);
+
+struct usb_endpoint_descriptor fsg_ss_bulk_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+};
+EXPORT_SYMBOL_GPL(fsg_ss_bulk_out_desc);
+
+struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = {
+       .bLength =              sizeof(fsg_ss_bulk_in_comp_desc),
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+
+       /*.bMaxBurst =          DYNAMIC, */
+};
+EXPORT_SYMBOL_GPL(fsg_ss_bulk_out_comp_desc);
+
+struct usb_descriptor_header *fsg_ss_function[] = {
+       (struct usb_descriptor_header *) &fsg_intf_desc,
+       (struct usb_descriptor_header *) &fsg_ss_bulk_in_desc,
+       (struct usb_descriptor_header *) &fsg_ss_bulk_in_comp_desc,
+       (struct usb_descriptor_header *) &fsg_ss_bulk_out_desc,
+       (struct usb_descriptor_header *) &fsg_ss_bulk_out_comp_desc,
+       NULL,
+};
+EXPORT_SYMBOL_GPL(fsg_ss_function);
+
+
+ /*-------------------------------------------------------------------------*/
+
+/*
+ * If the next two routines are called while the gadget is registered,
+ * the caller must own fsg->filesem for writing.
+ */
+
+void fsg_lun_close(struct fsg_lun *curlun)
+{
+       if (curlun->filp) {
+               LDBG(curlun, "close backing file\n");
+               fput(curlun->filp);
+               curlun->filp = NULL;
+       }
+}
+EXPORT_SYMBOL_GPL(fsg_lun_close);
+
+int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
+{
+       int                             ro;
+       struct file                     *filp = NULL;
+       int                             rc = -EINVAL;
+       struct inode                    *inode = NULL;
+       loff_t                          size;
+       loff_t                          num_sectors;
+       loff_t                          min_sectors;
+       unsigned int                    blkbits;
+       unsigned int                    blksize;
+
+       /* R/W if we can, R/O if we must */
+       ro = curlun->initially_ro;
+       if (!ro) {
+               filp = filp_open(filename, O_RDWR | O_LARGEFILE, 0);
+               if (PTR_ERR(filp) == -EROFS || PTR_ERR(filp) == -EACCES)
+                       ro = 1;
+       }
+       if (ro)
+               filp = filp_open(filename, O_RDONLY | O_LARGEFILE, 0);
+       if (IS_ERR(filp)) {
+               LINFO(curlun, "unable to open backing file: %s\n", filename);
+               return PTR_ERR(filp);
+       }
+
+       if (!(filp->f_mode & FMODE_WRITE))
+               ro = 1;
+
+       inode = file_inode(filp);
+       if ((!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))) {
+               LINFO(curlun, "invalid file type: %s\n", filename);
+               goto out;
+       }
+
+       /*
+        * If we can't read the file, it's no good.
+        * If we can't write the file, use it read-only.
+        */
+       if (!(filp->f_mode & FMODE_CAN_READ)) {
+               LINFO(curlun, "file not readable: %s\n", filename);
+               goto out;
+       }
+       if (!(filp->f_mode & FMODE_CAN_WRITE))
+               ro = 1;
+
+       size = i_size_read(inode->i_mapping->host);
+       if (size < 0) {
+               LINFO(curlun, "unable to find file size: %s\n", filename);
+               rc = (int) size;
+               goto out;
+       }
+
+       if (curlun->cdrom) {
+               blksize = 2048;
+               blkbits = 11;
+       } else if (inode->i_bdev) {
+               blksize = bdev_logical_block_size(inode->i_bdev);
+               blkbits = blksize_bits(blksize);
+       } else {
+               blksize = 512;
+               blkbits = 9;
+       }
+
+       num_sectors = size >> blkbits; /* File size in logic-block-size blocks */
+       min_sectors = 1;
+       if (curlun->cdrom) {
+               min_sectors = 300;      /* Smallest track is 300 frames */
+               if (num_sectors >= 256*60*75) {
+                       num_sectors = 256*60*75 - 1;
+                       LINFO(curlun, "file too big: %s\n", filename);
+                       LINFO(curlun, "using only first %d blocks\n",
+                                       (int) num_sectors);
+               }
+       }
+       if (num_sectors < min_sectors) {
+               LINFO(curlun, "file too small: %s\n", filename);
+               rc = -ETOOSMALL;
+               goto out;
+       }
+
+       if (fsg_lun_is_open(curlun))
+               fsg_lun_close(curlun);
+
+       curlun->blksize = blksize;
+       curlun->blkbits = blkbits;
+       curlun->ro = ro;
+       curlun->filp = filp;
+       curlun->file_length = size;
+       curlun->num_sectors = num_sectors;
+       LDBG(curlun, "open backing file: %s\n", filename);
+       return 0;
+
+out:
+       fput(filp);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(fsg_lun_open);
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Sync the file data, don't bother with the metadata.
+ * This code was copied from fs/buffer.c:sys_fdatasync().
+ */
+int fsg_lun_fsync_sub(struct fsg_lun *curlun)
+{
+       struct file     *filp = curlun->filp;
+
+       if (curlun->ro || !filp)
+               return 0;
+       return vfs_fsync(filp, 1);
+}
+EXPORT_SYMBOL_GPL(fsg_lun_fsync_sub);
+
+void store_cdrom_address(u8 *dest, int msf, u32 addr)
+{
+       if (msf) {
+               /* Convert to Minutes-Seconds-Frames */
+               addr >>= 2;             /* Convert to 2048-byte frames */
+               addr += 2*75;           /* Lead-in occupies 2 seconds */
+               dest[3] = addr % 75;    /* Frames */
+               addr /= 75;
+               dest[2] = addr % 60;    /* Seconds */
+               addr /= 60;
+               dest[1] = addr;         /* Minutes */
+               dest[0] = 0;            /* Reserved */
+       } else {
+               /* Absolute sector */
+               put_unaligned_be32(addr, dest);
+       }
+}
+EXPORT_SYMBOL_GPL(store_cdrom_address);
+
+/*-------------------------------------------------------------------------*/
+
+
+ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf)
+{
+       return sprintf(buf, "%d\n", fsg_lun_is_open(curlun)
+                                 ? curlun->ro
+                                 : curlun->initially_ro);
+}
+EXPORT_SYMBOL_GPL(fsg_show_ro);
+
+ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf)
+{
+       return sprintf(buf, "%u\n", curlun->nofua);
+}
+EXPORT_SYMBOL_GPL(fsg_show_nofua);
+
+ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+                     char *buf)
+{
+       char            *p;
+       ssize_t         rc;
+
+       down_read(filesem);
+       if (fsg_lun_is_open(curlun)) {  /* Get the complete pathname */
+               p = d_path(&curlun->filp->f_path, buf, PAGE_SIZE - 1);
+               if (IS_ERR(p))
+                       rc = PTR_ERR(p);
+               else {
+                       rc = strlen(p);
+                       memmove(buf, p, rc);
+                       buf[rc] = '\n';         /* Add a newline */
+                       buf[++rc] = 0;
+               }
+       } else {                                /* No file, return 0 bytes */
+               *buf = 0;
+               rc = 0;
+       }
+       up_read(filesem);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(fsg_show_file);
+
+ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf)
+{
+       return sprintf(buf, "%u\n", curlun->cdrom);
+}
+EXPORT_SYMBOL_GPL(fsg_show_cdrom);
+
+ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf)
+{
+       return sprintf(buf, "%u\n", curlun->removable);
+}
+EXPORT_SYMBOL_GPL(fsg_show_removable);
+
+/*
+ * The caller must hold fsg->filesem for reading when calling this function.
+ */
+static ssize_t _fsg_store_ro(struct fsg_lun *curlun, bool ro)
+{
+       if (fsg_lun_is_open(curlun)) {
+               LDBG(curlun, "read-only status change prevented\n");
+               return -EBUSY;
+       }
+
+       curlun->ro = ro;
+       curlun->initially_ro = ro;
+       LDBG(curlun, "read-only status set to %d\n", curlun->ro);
+
+       return 0;
+}
+
+ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+                    const char *buf, size_t count)
+{
+       ssize_t         rc;
+       bool            ro;
+
+       rc = strtobool(buf, &ro);
+       if (rc)
+               return rc;
+
+       /*
+        * Allow the write-enable status to change only while the
+        * backing file is closed.
+        */
+       down_read(filesem);
+       rc = _fsg_store_ro(curlun, ro);
+       if (!rc)
+               rc = count;
+       up_read(filesem);
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(fsg_store_ro);
+
+ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count)
+{
+       bool            nofua;
+       int             ret;
+
+       ret = strtobool(buf, &nofua);
+       if (ret)
+               return ret;
+
+       /* Sync data when switching from async mode to sync */
+       if (!nofua && curlun->nofua)
+               fsg_lun_fsync_sub(curlun);
+
+       curlun->nofua = nofua;
+
+       return count;
+}
+EXPORT_SYMBOL_GPL(fsg_store_nofua);
+
+ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+                      const char *buf, size_t count)
+{
+       int             rc = 0;
+
+       if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) {
+               LDBG(curlun, "eject attempt prevented\n");
+               return -EBUSY;                          /* "Door is locked" */
+       }
+
+       /* Remove a trailing newline */
+       if (count > 0 && buf[count-1] == '\n')
+               ((char *) buf)[count-1] = 0;            /* Ugh! */
+
+       /* Load new medium */
+       down_write(filesem);
+       if (count > 0 && buf[0]) {
+               /* fsg_lun_open() will close existing file if any. */
+               rc = fsg_lun_open(curlun, buf);
+               if (rc == 0)
+                       curlun->unit_attention_data =
+                                       SS_NOT_READY_TO_READY_TRANSITION;
+       } else if (fsg_lun_is_open(curlun)) {
+               fsg_lun_close(curlun);
+               curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
+       }
+       up_write(filesem);
+       return (rc < 0 ? rc : count);
+}
+EXPORT_SYMBOL_GPL(fsg_store_file);
+
+ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+                       const char *buf, size_t count)
+{
+       bool            cdrom;
+       int             ret;
+
+       ret = strtobool(buf, &cdrom);
+       if (ret)
+               return ret;
+
+       down_read(filesem);
+       ret = cdrom ? _fsg_store_ro(curlun, true) : 0;
+
+       if (!ret) {
+               curlun->cdrom = cdrom;
+               ret = count;
+       }
+       up_read(filesem);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(fsg_store_cdrom);
+
+ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf,
+                           size_t count)
+{
+       bool            removable;
+       int             ret;
+
+       ret = strtobool(buf, &removable);
+       if (ret)
+               return ret;
+
+       curlun->removable = removable;
+
+       return count;
+}
+EXPORT_SYMBOL_GPL(fsg_store_removable);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/storage_common.h b/drivers/usb/gadget/function/storage_common.h
new file mode 100644 (file)
index 0000000..70c8914
--- /dev/null
@@ -0,0 +1,225 @@
+#ifndef USB_STORAGE_COMMON_H
+#define USB_STORAGE_COMMON_H
+
+#include <linux/device.h>
+#include <linux/usb/storage.h>
+#include <scsi/scsi.h>
+#include <asm/unaligned.h>
+
+#ifndef DEBUG
+#undef VERBOSE_DEBUG
+#undef DUMP_MSGS
+#endif /* !DEBUG */
+
+#ifdef VERBOSE_DEBUG
+#define VLDBG  LDBG
+#else
+#define VLDBG(lun, fmt, args...) do { } while (0)
+#endif /* VERBOSE_DEBUG */
+
+#define _LMSG(func, lun, fmt, args...)                                 \
+       do {                                                            \
+               if ((lun)->name_pfx && *(lun)->name_pfx)                \
+                       func("%s/%s: " fmt, *(lun)->name_pfx,           \
+                                (lun)->name, ## args);                 \
+               else                                                    \
+                       func("%s: " fmt, (lun)->name, ## args);         \
+       } while (0)
+
+#define LDBG(lun, fmt, args...)                _LMSG(pr_debug, lun, fmt, ## args)
+#define LERROR(lun, fmt, args...)      _LMSG(pr_err, lun, fmt, ## args)
+#define LWARN(lun, fmt, args...)       _LMSG(pr_warn, lun, fmt, ## args)
+#define LINFO(lun, fmt, args...)       _LMSG(pr_info, lun, fmt, ## args)
+
+
+#ifdef DUMP_MSGS
+
+#  define dump_msg(fsg, /* const char * */ label,                      \
+                  /* const u8 * */ buf, /* unsigned */ length)         \
+do {                                                                   \
+       if (length < 512) {                                             \
+               DBG(fsg, "%s, length %u:\n", label, length);            \
+               print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET,      \
+                              16, 1, buf, length, 0);                  \
+       }                                                               \
+} while (0)
+
+#  define dump_cdb(fsg) do { } while (0)
+
+#else
+
+#  define dump_msg(fsg, /* const char * */ label, \
+                  /* const u8 * */ buf, /* unsigned */ length) do { } while (0)
+
+#  ifdef VERBOSE_DEBUG
+
+#    define dump_cdb(fsg)                                              \
+       print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE,      \
+                      16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0)         \
+
+#  else
+
+#    define dump_cdb(fsg) do { } while (0)
+
+#  endif /* VERBOSE_DEBUG */
+
+#endif /* DUMP_MSGS */
+
+/* Length of a SCSI Command Data Block */
+#define MAX_COMMAND_SIZE       16
+
+/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */
+#define SS_NO_SENSE                            0
+#define SS_COMMUNICATION_FAILURE               0x040800
+#define SS_INVALID_COMMAND                     0x052000
+#define SS_INVALID_FIELD_IN_CDB                        0x052400
+#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE  0x052100
+#define SS_LOGICAL_UNIT_NOT_SUPPORTED          0x052500
+#define SS_MEDIUM_NOT_PRESENT                  0x023a00
+#define SS_MEDIUM_REMOVAL_PREVENTED            0x055302
+#define SS_NOT_READY_TO_READY_TRANSITION       0x062800
+#define SS_RESET_OCCURRED                      0x062900
+#define SS_SAVING_PARAMETERS_NOT_SUPPORTED     0x053900
+#define SS_UNRECOVERED_READ_ERROR              0x031100
+#define SS_WRITE_ERROR                         0x030c02
+#define SS_WRITE_PROTECTED                     0x072700
+
+#define SK(x)          ((u8) ((x) >> 16))      /* Sense Key byte, etc. */
+#define ASC(x)         ((u8) ((x) >> 8))
+#define ASCQ(x)                ((u8) (x))
+
+struct fsg_lun {
+       struct file     *filp;
+       loff_t          file_length;
+       loff_t          num_sectors;
+
+       unsigned int    initially_ro:1;
+       unsigned int    ro:1;
+       unsigned int    removable:1;
+       unsigned int    cdrom:1;
+       unsigned int    prevent_medium_removal:1;
+       unsigned int    registered:1;
+       unsigned int    info_valid:1;
+       unsigned int    nofua:1;
+
+       u32             sense_data;
+       u32             sense_data_info;
+       u32             unit_attention_data;
+
+       unsigned int    blkbits; /* Bits of logical block size
+                                                      of bound block device */
+       unsigned int    blksize; /* logical block size of bound block device */
+       struct device   dev;
+       const char      *name;          /* "lun.name" */
+       const char      **name_pfx;     /* "function.name" */
+};
+
+static inline bool fsg_lun_is_open(struct fsg_lun *curlun)
+{
+       return curlun->filp != NULL;
+}
+
+/* Default size of buffer length. */
+#define FSG_BUFLEN     ((u32)16384)
+
+/* Maximal number of LUNs supported in mass storage function */
+#define FSG_MAX_LUNS   8
+
+enum fsg_buffer_state {
+       BUF_STATE_EMPTY = 0,
+       BUF_STATE_FULL,
+       BUF_STATE_BUSY
+};
+
+struct fsg_buffhd {
+       void                            *buf;
+       enum fsg_buffer_state           state;
+       struct fsg_buffhd               *next;
+
+       /*
+        * The NetChip 2280 is faster, and handles some protocol faults
+        * better, if we don't submit any short bulk-out read requests.
+        * So we will record the intended request length here.
+        */
+       unsigned int                    bulk_out_intended_length;
+
+       struct usb_request              *inreq;
+       int                             inreq_busy;
+       struct usb_request              *outreq;
+       int                             outreq_busy;
+};
+
+enum fsg_state {
+       /* This one isn't used anywhere */
+       FSG_STATE_COMMAND_PHASE = -10,
+       FSG_STATE_DATA_PHASE,
+       FSG_STATE_STATUS_PHASE,
+
+       FSG_STATE_IDLE = 0,
+       FSG_STATE_ABORT_BULK_OUT,
+       FSG_STATE_RESET,
+       FSG_STATE_INTERFACE_CHANGE,
+       FSG_STATE_CONFIG_CHANGE,
+       FSG_STATE_DISCONNECT,
+       FSG_STATE_EXIT,
+       FSG_STATE_TERMINATED
+};
+
+enum data_direction {
+       DATA_DIR_UNKNOWN = 0,
+       DATA_DIR_FROM_HOST,
+       DATA_DIR_TO_HOST,
+       DATA_DIR_NONE
+};
+
+static inline u32 get_unaligned_be24(u8 *buf)
+{
+       return 0xffffff & (u32) get_unaligned_be32(buf - 1);
+}
+
+static inline struct fsg_lun *fsg_lun_from_dev(struct device *dev)
+{
+       return container_of(dev, struct fsg_lun, dev);
+}
+
+enum {
+       FSG_STRING_INTERFACE
+};
+
+extern struct usb_interface_descriptor fsg_intf_desc;
+
+extern struct usb_endpoint_descriptor fsg_fs_bulk_in_desc;
+extern struct usb_endpoint_descriptor fsg_fs_bulk_out_desc;
+extern struct usb_descriptor_header *fsg_fs_function[];
+
+extern struct usb_endpoint_descriptor fsg_hs_bulk_in_desc;
+extern struct usb_endpoint_descriptor fsg_hs_bulk_out_desc;
+extern struct usb_descriptor_header *fsg_hs_function[];
+
+extern struct usb_endpoint_descriptor fsg_ss_bulk_in_desc;
+extern struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc;
+extern struct usb_endpoint_descriptor fsg_ss_bulk_out_desc;
+extern struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc;
+extern struct usb_descriptor_header *fsg_ss_function[];
+
+void fsg_lun_close(struct fsg_lun *curlun);
+int fsg_lun_open(struct fsg_lun *curlun, const char *filename);
+int fsg_lun_fsync_sub(struct fsg_lun *curlun);
+void store_cdrom_address(u8 *dest, int msf, u32 addr);
+ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf);
+ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf);
+ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+                     char *buf);
+ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf);
+ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf);
+ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+                    const char *buf, size_t count);
+ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count);
+ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+                      const char *buf, size_t count);
+ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+                       const char *buf, size_t count);
+ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf,
+                           size_t count);
+
+#endif /* USB_STORAGE_COMMON_H */
diff --git a/drivers/usb/gadget/function/u_ecm.h b/drivers/usb/gadget/function/u_ecm.h
new file mode 100644 (file)
index 0000000..262cc03
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * u_ecm.h
+ *
+ * Utility definitions for the ecm function
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef U_ECM_H
+#define U_ECM_H
+
+#include <linux/usb/composite.h>
+
+struct f_ecm_opts {
+       struct usb_function_instance    func_inst;
+       struct net_device               *net;
+       bool                            bound;
+
+       /*
+        * Read/write access to configfs attributes is handled by configfs.
+        *
+        * This is to protect the data from concurrent access by read/write
+        * and create symlink/remove symlink.
+        */
+       struct mutex                    lock;
+       int                             refcnt;
+};
+
+#endif /* U_ECM_H */
diff --git a/drivers/usb/gadget/function/u_eem.h b/drivers/usb/gadget/function/u_eem.h
new file mode 100644 (file)
index 0000000..e3ae978
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * u_eem.h
+ *
+ * Utility definitions for the eem function
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef U_EEM_H
+#define U_EEM_H
+
+#include <linux/usb/composite.h>
+
+struct f_eem_opts {
+       struct usb_function_instance    func_inst;
+       struct net_device               *net;
+       bool                            bound;
+
+       /*
+        * Read/write access to configfs attributes is handled by configfs.
+        *
+        * This is to protect the data from concurrent access by read/write
+        * and create symlink/remove symlink.
+        */
+       struct mutex                    lock;
+       int                             refcnt;
+};
+
+#endif /* U_EEM_H */
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
new file mode 100644 (file)
index 0000000..6e6f876
--- /dev/null
@@ -0,0 +1,1179 @@
+/*
+ * u_ether.c -- Ethernet-over-USB link layer utilities for Gadget stack
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * 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.
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/gfp.h>
+#include <linux/device.h>
+#include <linux/ctype.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/if_vlan.h>
+
+#include "u_ether.h"
+
+
+/*
+ * This component encapsulates the Ethernet link glue needed to provide
+ * one (!) network link through the USB gadget stack, normally "usb0".
+ *
+ * The control and data models are handled by the function driver which
+ * connects to this code; such as CDC Ethernet (ECM or EEM),
+ * "CDC Subset", or RNDIS.  That includes all descriptor and endpoint
+ * management.
+ *
+ * Link level addressing is handled by this component using module
+ * parameters; if no such parameters are provided, random link level
+ * addresses are used.  Each end of the link uses one address.  The
+ * host end address is exported in various ways, and is often recorded
+ * in configuration databases.
+ *
+ * The driver which assembles each configuration using such a link is
+ * responsible for ensuring that each configuration includes at most one
+ * instance of is network link.  (The network layer provides ways for
+ * this single "physical" link to be used by multiple virtual links.)
+ */
+
+#define UETH__VERSION  "29-May-2008"
+
+struct eth_dev {
+       /* lock is held while accessing port_usb
+        */
+       spinlock_t              lock;
+       struct gether           *port_usb;
+
+       struct net_device       *net;
+       struct usb_gadget       *gadget;
+
+       spinlock_t              req_lock;       /* guard {rx,tx}_reqs */
+       struct list_head        tx_reqs, rx_reqs;
+       atomic_t                tx_qlen;
+
+       struct sk_buff_head     rx_frames;
+
+       unsigned                qmult;
+
+       unsigned                header_len;
+       struct sk_buff          *(*wrap)(struct gether *, struct sk_buff *skb);
+       int                     (*unwrap)(struct gether *,
+                                               struct sk_buff *skb,
+                                               struct sk_buff_head *list);
+
+       struct work_struct      work;
+
+       unsigned long           todo;
+#define        WORK_RX_MEMORY          0
+
+       bool                    zlp;
+       u8                      host_mac[ETH_ALEN];
+       u8                      dev_mac[ETH_ALEN];
+};
+
+/*-------------------------------------------------------------------------*/
+
+#define RX_EXTRA       20      /* bytes guarding against rx overflows */
+
+#define DEFAULT_QLEN   2       /* double buffering by default */
+
+/* for dual-speed hardware, use deeper queues at high/super speed */
+static inline int qlen(struct usb_gadget *gadget, unsigned qmult)
+{
+       if (gadget_is_dualspeed(gadget) && (gadget->speed == USB_SPEED_HIGH ||
+                                           gadget->speed == USB_SPEED_SUPER))
+               return qmult * DEFAULT_QLEN;
+       else
+               return DEFAULT_QLEN;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* REVISIT there must be a better way than having two sets
+ * of debug calls ...
+ */
+
+#undef DBG
+#undef VDBG
+#undef ERROR
+#undef INFO
+
+#define xprintk(d, level, fmt, args...) \
+       printk(level "%s: " fmt , (d)->net->name , ## args)
+
+#ifdef DEBUG
+#undef DEBUG
+#define DBG(dev, fmt, args...) \
+       xprintk(dev , KERN_DEBUG , fmt , ## args)
+#else
+#define DBG(dev, fmt, args...) \
+       do { } while (0)
+#endif /* DEBUG */
+
+#ifdef VERBOSE_DEBUG
+#define VDBG   DBG
+#else
+#define VDBG(dev, fmt, args...) \
+       do { } while (0)
+#endif /* DEBUG */
+
+#define ERROR(dev, fmt, args...) \
+       xprintk(dev , KERN_ERR , fmt , ## args)
+#define INFO(dev, fmt, args...) \
+       xprintk(dev , KERN_INFO , fmt , ## args)
+
+/*-------------------------------------------------------------------------*/
+
+/* NETWORK DRIVER HOOKUP (to the layer above this driver) */
+
+static int ueth_change_mtu(struct net_device *net, int new_mtu)
+{
+       struct eth_dev  *dev = netdev_priv(net);
+       unsigned long   flags;
+       int             status = 0;
+
+       /* don't change MTU on "live" link (peer won't know) */
+       spin_lock_irqsave(&dev->lock, flags);
+       if (dev->port_usb)
+               status = -EBUSY;
+       else if (new_mtu <= ETH_HLEN || new_mtu > ETH_FRAME_LEN)
+               status = -ERANGE;
+       else
+               net->mtu = new_mtu;
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       return status;
+}
+
+static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p)
+{
+       struct eth_dev *dev = netdev_priv(net);
+
+       strlcpy(p->driver, "g_ether", sizeof(p->driver));
+       strlcpy(p->version, UETH__VERSION, sizeof(p->version));
+       strlcpy(p->fw_version, dev->gadget->name, sizeof(p->fw_version));
+       strlcpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof(p->bus_info));
+}
+
+/* REVISIT can also support:
+ *   - WOL (by tracking suspends and issuing remote wakeup)
+ *   - msglevel (implies updated messaging)
+ *   - ... probably more ethtool ops
+ */
+
+static const struct ethtool_ops ops = {
+       .get_drvinfo = eth_get_drvinfo,
+       .get_link = ethtool_op_get_link,
+};
+
+static void defer_kevent(struct eth_dev *dev, int flag)
+{
+       if (test_and_set_bit(flag, &dev->todo))
+               return;
+       if (!schedule_work(&dev->work))
+               ERROR(dev, "kevent %d may have been dropped\n", flag);
+       else
+               DBG(dev, "kevent %d scheduled\n", flag);
+}
+
+static void rx_complete(struct usb_ep *ep, struct usb_request *req);
+
+static int
+rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
+{
+       struct sk_buff  *skb;
+       int             retval = -ENOMEM;
+       size_t          size = 0;
+       struct usb_ep   *out;
+       unsigned long   flags;
+
+       spin_lock_irqsave(&dev->lock, flags);
+       if (dev->port_usb)
+               out = dev->port_usb->out_ep;
+       else
+               out = NULL;
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       if (!out)
+               return -ENOTCONN;
+
+
+       /* Padding up to RX_EXTRA handles minor disagreements with host.
+        * Normally we use the USB "terminate on short read" convention;
+        * so allow up to (N*maxpacket), since that memory is normally
+        * already allocated.  Some hardware doesn't deal well with short
+        * reads (e.g. DMA must be N*maxpacket), so for now don't trim a
+        * byte off the end (to force hardware errors on overflow).
+        *
+        * RNDIS uses internal framing, and explicitly allows senders to
+        * pad to end-of-packet.  That's potentially nice for speed, but
+        * means receivers can't recover lost synch on their own (because
+        * new packets don't only start after a short RX).
+        */
+       size += sizeof(struct ethhdr) + dev->net->mtu + RX_EXTRA;
+       size += dev->port_usb->header_len;
+       size += out->maxpacket - 1;
+       size -= size % out->maxpacket;
+
+       if (dev->port_usb->is_fixed)
+               size = max_t(size_t, size, dev->port_usb->fixed_out_len);
+
+       skb = alloc_skb(size + NET_IP_ALIGN, gfp_flags);
+       if (skb == NULL) {
+               DBG(dev, "no rx skb\n");
+               goto enomem;
+       }
+
+       /* Some platforms perform better when IP packets are aligned,
+        * but on at least one, checksumming fails otherwise.  Note:
+        * RNDIS headers involve variable numbers of LE32 values.
+        */
+       skb_reserve(skb, NET_IP_ALIGN);
+
+       req->buf = skb->data;
+       req->length = size;
+       req->complete = rx_complete;
+       req->context = skb;
+
+       retval = usb_ep_queue(out, req, gfp_flags);
+       if (retval == -ENOMEM)
+enomem:
+               defer_kevent(dev, WORK_RX_MEMORY);
+       if (retval) {
+               DBG(dev, "rx submit --> %d\n", retval);
+               if (skb)
+                       dev_kfree_skb_any(skb);
+               spin_lock_irqsave(&dev->req_lock, flags);
+               list_add(&req->list, &dev->rx_reqs);
+               spin_unlock_irqrestore(&dev->req_lock, flags);
+       }
+       return retval;
+}
+
+static void rx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct sk_buff  *skb = req->context, *skb2;
+       struct eth_dev  *dev = ep->driver_data;
+       int             status = req->status;
+
+       switch (status) {
+
+       /* normal completion */
+       case 0:
+               skb_put(skb, req->actual);
+
+               if (dev->unwrap) {
+                       unsigned long   flags;
+
+                       spin_lock_irqsave(&dev->lock, flags);
+                       if (dev->port_usb) {
+                               status = dev->unwrap(dev->port_usb,
+                                                       skb,
+                                                       &dev->rx_frames);
+                       } else {
+                               dev_kfree_skb_any(skb);
+                               status = -ENOTCONN;
+                       }
+                       spin_unlock_irqrestore(&dev->lock, flags);
+               } else {
+                       skb_queue_tail(&dev->rx_frames, skb);
+               }
+               skb = NULL;
+
+               skb2 = skb_dequeue(&dev->rx_frames);
+               while (skb2) {
+                       if (status < 0
+                                       || ETH_HLEN > skb2->len
+                                       || skb2->len > VLAN_ETH_FRAME_LEN) {
+                               dev->net->stats.rx_errors++;
+                               dev->net->stats.rx_length_errors++;
+                               DBG(dev, "rx length %d\n", skb2->len);
+                               dev_kfree_skb_any(skb2);
+                               goto next_frame;
+                       }
+                       skb2->protocol = eth_type_trans(skb2, dev->net);
+                       dev->net->stats.rx_packets++;
+                       dev->net->stats.rx_bytes += skb2->len;
+
+                       /* no buffer copies needed, unless hardware can't
+                        * use skb buffers.
+                        */
+                       status = netif_rx(skb2);
+next_frame:
+                       skb2 = skb_dequeue(&dev->rx_frames);
+               }
+               break;
+
+       /* software-driven interface shutdown */
+       case -ECONNRESET:               /* unlink */
+       case -ESHUTDOWN:                /* disconnect etc */
+               VDBG(dev, "rx shutdown, code %d\n", status);
+               goto quiesce;
+
+       /* for hardware automagic (such as pxa) */
+       case -ECONNABORTED:             /* endpoint reset */
+               DBG(dev, "rx %s reset\n", ep->name);
+               defer_kevent(dev, WORK_RX_MEMORY);
+quiesce:
+               dev_kfree_skb_any(skb);
+               goto clean;
+
+       /* data overrun */
+       case -EOVERFLOW:
+               dev->net->stats.rx_over_errors++;
+               /* FALLTHROUGH */
+
+       default:
+               dev->net->stats.rx_errors++;
+               DBG(dev, "rx status %d\n", status);
+               break;
+       }
+
+       if (skb)
+               dev_kfree_skb_any(skb);
+       if (!netif_running(dev->net)) {
+clean:
+               spin_lock(&dev->req_lock);
+               list_add(&req->list, &dev->rx_reqs);
+               spin_unlock(&dev->req_lock);
+               req = NULL;
+       }
+       if (req)
+               rx_submit(dev, req, GFP_ATOMIC);
+}
+
+static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n)
+{
+       unsigned                i;
+       struct usb_request      *req;
+
+       if (!n)
+               return -ENOMEM;
+
+       /* queue/recycle up to N requests */
+       i = n;
+       list_for_each_entry(req, list, list) {
+               if (i-- == 0)
+                       goto extra;
+       }
+       while (i--) {
+               req = usb_ep_alloc_request(ep, GFP_ATOMIC);
+               if (!req)
+                       return list_empty(list) ? -ENOMEM : 0;
+               list_add(&req->list, list);
+       }
+       return 0;
+
+extra:
+       /* free extras */
+       for (;;) {
+               struct list_head        *next;
+
+               next = req->list.next;
+               list_del(&req->list);
+               usb_ep_free_request(ep, req);
+
+               if (next == list)
+                       break;
+
+               req = container_of(next, struct usb_request, list);
+       }
+       return 0;
+}
+
+static int alloc_requests(struct eth_dev *dev, struct gether *link, unsigned n)
+{
+       int     status;
+
+       spin_lock(&dev->req_lock);
+       status = prealloc(&dev->tx_reqs, link->in_ep, n);
+       if (status < 0)
+               goto fail;
+       status = prealloc(&dev->rx_reqs, link->out_ep, n);
+       if (status < 0)
+               goto fail;
+       goto done;
+fail:
+       DBG(dev, "can't alloc requests\n");
+done:
+       spin_unlock(&dev->req_lock);
+       return status;
+}
+
+static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)
+{
+       struct usb_request      *req;
+       unsigned long           flags;
+
+       /* fill unused rxq slots with some skb */
+       spin_lock_irqsave(&dev->req_lock, flags);
+       while (!list_empty(&dev->rx_reqs)) {
+               req = container_of(dev->rx_reqs.next,
+                               struct usb_request, list);
+               list_del_init(&req->list);
+               spin_unlock_irqrestore(&dev->req_lock, flags);
+
+               if (rx_submit(dev, req, gfp_flags) < 0) {
+                       defer_kevent(dev, WORK_RX_MEMORY);
+                       return;
+               }
+
+               spin_lock_irqsave(&dev->req_lock, flags);
+       }
+       spin_unlock_irqrestore(&dev->req_lock, flags);
+}
+
+static void eth_work(struct work_struct *work)
+{
+       struct eth_dev  *dev = container_of(work, struct eth_dev, work);
+
+       if (test_and_clear_bit(WORK_RX_MEMORY, &dev->todo)) {
+               if (netif_running(dev->net))
+                       rx_fill(dev, GFP_KERNEL);
+       }
+
+       if (dev->todo)
+               DBG(dev, "work done, flags = 0x%lx\n", dev->todo);
+}
+
+static void tx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct sk_buff  *skb = req->context;
+       struct eth_dev  *dev = ep->driver_data;
+
+       switch (req->status) {
+       default:
+               dev->net->stats.tx_errors++;
+               VDBG(dev, "tx err %d\n", req->status);
+               /* FALLTHROUGH */
+       case -ECONNRESET:               /* unlink */
+       case -ESHUTDOWN:                /* disconnect etc */
+               break;
+       case 0:
+               dev->net->stats.tx_bytes += skb->len;
+       }
+       dev->net->stats.tx_packets++;
+
+       spin_lock(&dev->req_lock);
+       list_add(&req->list, &dev->tx_reqs);
+       spin_unlock(&dev->req_lock);
+       dev_kfree_skb_any(skb);
+
+       atomic_dec(&dev->tx_qlen);
+       if (netif_carrier_ok(dev->net))
+               netif_wake_queue(dev->net);
+}
+
+static inline int is_promisc(u16 cdc_filter)
+{
+       return cdc_filter & USB_CDC_PACKET_TYPE_PROMISCUOUS;
+}
+
+static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
+                                       struct net_device *net)
+{
+       struct eth_dev          *dev = netdev_priv(net);
+       int                     length = 0;
+       int                     retval;
+       struct usb_request      *req = NULL;
+       unsigned long           flags;
+       struct usb_ep           *in;
+       u16                     cdc_filter;
+
+       spin_lock_irqsave(&dev->lock, flags);
+       if (dev->port_usb) {
+               in = dev->port_usb->in_ep;
+               cdc_filter = dev->port_usb->cdc_filter;
+       } else {
+               in = NULL;
+               cdc_filter = 0;
+       }
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       if (skb && !in) {
+               dev_kfree_skb_any(skb);
+               return NETDEV_TX_OK;
+       }
+
+       /* apply outgoing CDC or RNDIS filters */
+       if (skb && !is_promisc(cdc_filter)) {
+               u8              *dest = skb->data;
+
+               if (is_multicast_ether_addr(dest)) {
+                       u16     type;
+
+                       /* ignores USB_CDC_PACKET_TYPE_MULTICAST and host
+                        * SET_ETHERNET_MULTICAST_FILTERS requests
+                        */
+                       if (is_broadcast_ether_addr(dest))
+                               type = USB_CDC_PACKET_TYPE_BROADCAST;
+                       else
+                               type = USB_CDC_PACKET_TYPE_ALL_MULTICAST;
+                       if (!(cdc_filter & type)) {
+                               dev_kfree_skb_any(skb);
+                               return NETDEV_TX_OK;
+                       }
+               }
+               /* ignores USB_CDC_PACKET_TYPE_DIRECTED */
+       }
+
+       spin_lock_irqsave(&dev->req_lock, flags);
+       /*
+        * this freelist can be empty if an interrupt triggered disconnect()
+        * and reconfigured the gadget (shutting down this queue) after the
+        * network stack decided to xmit but before we got the spinlock.
+        */
+       if (list_empty(&dev->tx_reqs)) {
+               spin_unlock_irqrestore(&dev->req_lock, flags);
+               return NETDEV_TX_BUSY;
+       }
+
+       req = container_of(dev->tx_reqs.next, struct usb_request, list);
+       list_del(&req->list);
+
+       /* temporarily stop TX queue when the freelist empties */
+       if (list_empty(&dev->tx_reqs))
+               netif_stop_queue(net);
+       spin_unlock_irqrestore(&dev->req_lock, flags);
+
+       /* no buffer copies needed, unless the network stack did it
+        * or the hardware can't use skb buffers.
+        * or there's not enough space for extra headers we need
+        */
+       if (dev->wrap) {
+               unsigned long   flags;
+
+               spin_lock_irqsave(&dev->lock, flags);
+               if (dev->port_usb)
+                       skb = dev->wrap(dev->port_usb, skb);
+               spin_unlock_irqrestore(&dev->lock, flags);
+               if (!skb) {
+                       /* Multi frame CDC protocols may store the frame for
+                        * later which is not a dropped frame.
+                        */
+                       if (dev->port_usb->supports_multi_frame)
+                               goto multiframe;
+                       goto drop;
+               }
+       }
+
+       length = skb->len;
+       req->buf = skb->data;
+       req->context = skb;
+       req->complete = tx_complete;
+
+       /* NCM requires no zlp if transfer is dwNtbInMaxSize */
+       if (dev->port_usb->is_fixed &&
+           length == dev->port_usb->fixed_in_len &&
+           (length % in->maxpacket) == 0)
+               req->zero = 0;
+       else
+               req->zero = 1;
+
+       /* use zlp framing on tx for strict CDC-Ether conformance,
+        * though any robust network rx path ignores extra padding.
+        * and some hardware doesn't like to write zlps.
+        */
+       if (req->zero && !dev->zlp && (length % in->maxpacket) == 0)
+               length++;
+
+       req->length = length;
+
+       /* throttle high/super speed IRQ rate back slightly */
+       if (gadget_is_dualspeed(dev->gadget))
+               req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH ||
+                                    dev->gadget->speed == USB_SPEED_SUPER)
+                       ? ((atomic_read(&dev->tx_qlen) % dev->qmult) != 0)
+                       : 0;
+
+       retval = usb_ep_queue(in, req, GFP_ATOMIC);
+       switch (retval) {
+       default:
+               DBG(dev, "tx queue err %d\n", retval);
+               break;
+       case 0:
+               net->trans_start = jiffies;
+               atomic_inc(&dev->tx_qlen);
+       }
+
+       if (retval) {
+               dev_kfree_skb_any(skb);
+drop:
+               dev->net->stats.tx_dropped++;
+multiframe:
+               spin_lock_irqsave(&dev->req_lock, flags);
+               if (list_empty(&dev->tx_reqs))
+                       netif_start_queue(net);
+               list_add(&req->list, &dev->tx_reqs);
+               spin_unlock_irqrestore(&dev->req_lock, flags);
+       }
+       return NETDEV_TX_OK;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void eth_start(struct eth_dev *dev, gfp_t gfp_flags)
+{
+       DBG(dev, "%s\n", __func__);
+
+       /* fill the rx queue */
+       rx_fill(dev, gfp_flags);
+
+       /* and open the tx floodgates */
+       atomic_set(&dev->tx_qlen, 0);
+       netif_wake_queue(dev->net);
+}
+
+static int eth_open(struct net_device *net)
+{
+       struct eth_dev  *dev = netdev_priv(net);
+       struct gether   *link;
+
+       DBG(dev, "%s\n", __func__);
+       if (netif_carrier_ok(dev->net))
+               eth_start(dev, GFP_KERNEL);
+
+       spin_lock_irq(&dev->lock);
+       link = dev->port_usb;
+       if (link && link->open)
+               link->open(link);
+       spin_unlock_irq(&dev->lock);
+
+       return 0;
+}
+
+static int eth_stop(struct net_device *net)
+{
+       struct eth_dev  *dev = netdev_priv(net);
+       unsigned long   flags;
+
+       VDBG(dev, "%s\n", __func__);
+       netif_stop_queue(net);
+
+       DBG(dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n",
+               dev->net->stats.rx_packets, dev->net->stats.tx_packets,
+               dev->net->stats.rx_errors, dev->net->stats.tx_errors
+               );
+
+       /* ensure there are no more active requests */
+       spin_lock_irqsave(&dev->lock, flags);
+       if (dev->port_usb) {
+               struct gether   *link = dev->port_usb;
+               const struct usb_endpoint_descriptor *in;
+               const struct usb_endpoint_descriptor *out;
+
+               if (link->close)
+                       link->close(link);
+
+               /* NOTE:  we have no abort-queue primitive we could use
+                * to cancel all pending I/O.  Instead, we disable then
+                * reenable the endpoints ... this idiom may leave toggle
+                * wrong, but that's a self-correcting error.
+                *
+                * REVISIT:  we *COULD* just let the transfers complete at
+                * their own pace; the network stack can handle old packets.
+                * For the moment we leave this here, since it works.
+                */
+               in = link->in_ep->desc;
+               out = link->out_ep->desc;
+               usb_ep_disable(link->in_ep);
+               usb_ep_disable(link->out_ep);
+               if (netif_carrier_ok(net)) {
+                       DBG(dev, "host still using in/out endpoints\n");
+                       link->in_ep->desc = in;
+                       link->out_ep->desc = out;
+                       usb_ep_enable(link->in_ep);
+                       usb_ep_enable(link->out_ep);
+               }
+       }
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int get_ether_addr(const char *str, u8 *dev_addr)
+{
+       if (str) {
+               unsigned        i;
+
+               for (i = 0; i < 6; i++) {
+                       unsigned char num;
+
+                       if ((*str == '.') || (*str == ':'))
+                               str++;
+                       num = hex_to_bin(*str++) << 4;
+                       num |= hex_to_bin(*str++);
+                       dev_addr [i] = num;
+               }
+               if (is_valid_ether_addr(dev_addr))
+                       return 0;
+       }
+       eth_random_addr(dev_addr);
+       return 1;
+}
+
+static int get_ether_addr_str(u8 dev_addr[ETH_ALEN], char *str, int len)
+{
+       if (len < 18)
+               return -EINVAL;
+
+       snprintf(str, len, "%02x:%02x:%02x:%02x:%02x:%02x",
+                dev_addr[0], dev_addr[1], dev_addr[2],
+                dev_addr[3], dev_addr[4], dev_addr[5]);
+       return 18;
+}
+
+static const struct net_device_ops eth_netdev_ops = {
+       .ndo_open               = eth_open,
+       .ndo_stop               = eth_stop,
+       .ndo_start_xmit         = eth_start_xmit,
+       .ndo_change_mtu         = ueth_change_mtu,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
+};
+
+static struct device_type gadget_type = {
+       .name   = "gadget",
+};
+
+/**
+ * gether_setup_name - initialize one ethernet-over-usb link
+ * @g: gadget to associated with these links
+ * @ethaddr: NULL, or a buffer in which the ethernet address of the
+ *     host side of the link is recorded
+ * @netname: name for network device (for example, "usb")
+ * Context: may sleep
+ *
+ * This sets up the single network link that may be exported by a
+ * gadget driver using this framework.  The link layer addresses are
+ * set up using module parameters.
+ *
+ * Returns an eth_dev pointer on success, or an ERR_PTR on failure.
+ */
+struct eth_dev *gether_setup_name(struct usb_gadget *g,
+               const char *dev_addr, const char *host_addr,
+               u8 ethaddr[ETH_ALEN], unsigned qmult, const char *netname)
+{
+       struct eth_dev          *dev;
+       struct net_device       *net;
+       int                     status;
+
+       net = alloc_etherdev(sizeof *dev);
+       if (!net)
+               return ERR_PTR(-ENOMEM);
+
+       dev = netdev_priv(net);
+       spin_lock_init(&dev->lock);
+       spin_lock_init(&dev->req_lock);
+       INIT_WORK(&dev->work, eth_work);
+       INIT_LIST_HEAD(&dev->tx_reqs);
+       INIT_LIST_HEAD(&dev->rx_reqs);
+
+       skb_queue_head_init(&dev->rx_frames);
+
+       /* network device setup */
+       dev->net = net;
+       dev->qmult = qmult;
+       snprintf(net->name, sizeof(net->name), "%s%%d", netname);
+
+       if (get_ether_addr(dev_addr, net->dev_addr))
+               dev_warn(&g->dev,
+                       "using random %s ethernet address\n", "self");
+       if (get_ether_addr(host_addr, dev->host_mac))
+               dev_warn(&g->dev,
+                       "using random %s ethernet address\n", "host");
+
+       if (ethaddr)
+               memcpy(ethaddr, dev->host_mac, ETH_ALEN);
+
+       net->netdev_ops = &eth_netdev_ops;
+
+       net->ethtool_ops = &ops;
+
+       dev->gadget = g;
+       SET_NETDEV_DEV(net, &g->dev);
+       SET_NETDEV_DEVTYPE(net, &gadget_type);
+
+       status = register_netdev(net);
+       if (status < 0) {
+               dev_dbg(&g->dev, "register_netdev failed, %d\n", status);
+               free_netdev(net);
+               dev = ERR_PTR(status);
+       } else {
+               INFO(dev, "MAC %pM\n", net->dev_addr);
+               INFO(dev, "HOST MAC %pM\n", dev->host_mac);
+
+               /*
+                * two kinds of host-initiated state changes:
+                *  - iff DATA transfer is active, carrier is "on"
+                *  - tx queueing enabled if open *and* carrier is "on"
+                */
+               netif_carrier_off(net);
+       }
+
+       return dev;
+}
+EXPORT_SYMBOL_GPL(gether_setup_name);
+
+struct net_device *gether_setup_name_default(const char *netname)
+{
+       struct net_device       *net;
+       struct eth_dev          *dev;
+
+       net = alloc_etherdev(sizeof(*dev));
+       if (!net)
+               return ERR_PTR(-ENOMEM);
+
+       dev = netdev_priv(net);
+       spin_lock_init(&dev->lock);
+       spin_lock_init(&dev->req_lock);
+       INIT_WORK(&dev->work, eth_work);
+       INIT_LIST_HEAD(&dev->tx_reqs);
+       INIT_LIST_HEAD(&dev->rx_reqs);
+
+       skb_queue_head_init(&dev->rx_frames);
+
+       /* network device setup */
+       dev->net = net;
+       dev->qmult = QMULT_DEFAULT;
+       snprintf(net->name, sizeof(net->name), "%s%%d", netname);
+
+       eth_random_addr(dev->dev_mac);
+       pr_warn("using random %s ethernet address\n", "self");
+       eth_random_addr(dev->host_mac);
+       pr_warn("using random %s ethernet address\n", "host");
+
+       net->netdev_ops = &eth_netdev_ops;
+
+       net->ethtool_ops = &ops;
+       SET_NETDEV_DEVTYPE(net, &gadget_type);
+
+       return net;
+}
+EXPORT_SYMBOL_GPL(gether_setup_name_default);
+
+int gether_register_netdev(struct net_device *net)
+{
+       struct eth_dev *dev;
+       struct usb_gadget *g;
+       struct sockaddr sa;
+       int status;
+
+       if (!net->dev.parent)
+               return -EINVAL;
+       dev = netdev_priv(net);
+       g = dev->gadget;
+       status = register_netdev(net);
+       if (status < 0) {
+               dev_dbg(&g->dev, "register_netdev failed, %d\n", status);
+               return status;
+       } else {
+               INFO(dev, "HOST MAC %pM\n", dev->host_mac);
+
+               /* two kinds of host-initiated state changes:
+                *  - iff DATA transfer is active, carrier is "on"
+                *  - tx queueing enabled if open *and* carrier is "on"
+                */
+               netif_carrier_off(net);
+       }
+       sa.sa_family = net->type;
+       memcpy(sa.sa_data, dev->dev_mac, ETH_ALEN);
+       rtnl_lock();
+       status = dev_set_mac_address(net, &sa);
+       rtnl_unlock();
+       if (status)
+               pr_warn("cannot set self ethernet address: %d\n", status);
+       else
+               INFO(dev, "MAC %pM\n", dev->dev_mac);
+
+       return status;
+}
+EXPORT_SYMBOL_GPL(gether_register_netdev);
+
+void gether_set_gadget(struct net_device *net, struct usb_gadget *g)
+{
+       struct eth_dev *dev;
+
+       dev = netdev_priv(net);
+       dev->gadget = g;
+       SET_NETDEV_DEV(net, &g->dev);
+}
+EXPORT_SYMBOL_GPL(gether_set_gadget);
+
+int gether_set_dev_addr(struct net_device *net, const char *dev_addr)
+{
+       struct eth_dev *dev;
+       u8 new_addr[ETH_ALEN];
+
+       dev = netdev_priv(net);
+       if (get_ether_addr(dev_addr, new_addr))
+               return -EINVAL;
+       memcpy(dev->dev_mac, new_addr, ETH_ALEN);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(gether_set_dev_addr);
+
+int gether_get_dev_addr(struct net_device *net, char *dev_addr, int len)
+{
+       struct eth_dev *dev;
+
+       dev = netdev_priv(net);
+       return get_ether_addr_str(dev->dev_mac, dev_addr, len);
+}
+EXPORT_SYMBOL_GPL(gether_get_dev_addr);
+
+int gether_set_host_addr(struct net_device *net, const char *host_addr)
+{
+       struct eth_dev *dev;
+       u8 new_addr[ETH_ALEN];
+
+       dev = netdev_priv(net);
+       if (get_ether_addr(host_addr, new_addr))
+               return -EINVAL;
+       memcpy(dev->host_mac, new_addr, ETH_ALEN);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(gether_set_host_addr);
+
+int gether_get_host_addr(struct net_device *net, char *host_addr, int len)
+{
+       struct eth_dev *dev;
+
+       dev = netdev_priv(net);
+       return get_ether_addr_str(dev->host_mac, host_addr, len);
+}
+EXPORT_SYMBOL_GPL(gether_get_host_addr);
+
+int gether_get_host_addr_cdc(struct net_device *net, char *host_addr, int len)
+{
+       struct eth_dev *dev;
+
+       if (len < 13)
+               return -EINVAL;
+
+       dev = netdev_priv(net);
+       snprintf(host_addr, len, "%pm", dev->host_mac);
+
+       return strlen(host_addr);
+}
+EXPORT_SYMBOL_GPL(gether_get_host_addr_cdc);
+
+void gether_get_host_addr_u8(struct net_device *net, u8 host_mac[ETH_ALEN])
+{
+       struct eth_dev *dev;
+
+       dev = netdev_priv(net);
+       memcpy(host_mac, dev->host_mac, ETH_ALEN);
+}
+EXPORT_SYMBOL_GPL(gether_get_host_addr_u8);
+
+void gether_set_qmult(struct net_device *net, unsigned qmult)
+{
+       struct eth_dev *dev;
+
+       dev = netdev_priv(net);
+       dev->qmult = qmult;
+}
+EXPORT_SYMBOL_GPL(gether_set_qmult);
+
+unsigned gether_get_qmult(struct net_device *net)
+{
+       struct eth_dev *dev;
+
+       dev = netdev_priv(net);
+       return dev->qmult;
+}
+EXPORT_SYMBOL_GPL(gether_get_qmult);
+
+int gether_get_ifname(struct net_device *net, char *name, int len)
+{
+       rtnl_lock();
+       strlcpy(name, netdev_name(net), len);
+       rtnl_unlock();
+       return strlen(name);
+}
+EXPORT_SYMBOL_GPL(gether_get_ifname);
+
+/**
+ * gether_cleanup - remove Ethernet-over-USB device
+ * Context: may sleep
+ *
+ * This is called to free all resources allocated by @gether_setup().
+ */
+void gether_cleanup(struct eth_dev *dev)
+{
+       if (!dev)
+               return;
+
+       unregister_netdev(dev->net);
+       flush_work(&dev->work);
+       free_netdev(dev->net);
+}
+EXPORT_SYMBOL_GPL(gether_cleanup);
+
+/**
+ * gether_connect - notify network layer that USB link is active
+ * @link: the USB link, set up with endpoints, descriptors matching
+ *     current device speed, and any framing wrapper(s) set up.
+ * Context: irqs blocked
+ *
+ * This is called to activate endpoints and let the network layer know
+ * the connection is active ("carrier detect").  It may cause the I/O
+ * queues to open and start letting network packets flow, but will in
+ * any case activate the endpoints so that they respond properly to the
+ * USB host.
+ *
+ * Verify net_device pointer returned using IS_ERR().  If it doesn't
+ * indicate some error code (negative errno), ep->driver_data values
+ * have been overwritten.
+ */
+struct net_device *gether_connect(struct gether *link)
+{
+       struct eth_dev          *dev = link->ioport;
+       int                     result = 0;
+
+       if (!dev)
+               return ERR_PTR(-EINVAL);
+
+       link->in_ep->driver_data = dev;
+       result = usb_ep_enable(link->in_ep);
+       if (result != 0) {
+               DBG(dev, "enable %s --> %d\n",
+                       link->in_ep->name, result);
+               goto fail0;
+       }
+
+       link->out_ep->driver_data = dev;
+       result = usb_ep_enable(link->out_ep);
+       if (result != 0) {
+               DBG(dev, "enable %s --> %d\n",
+                       link->out_ep->name, result);
+               goto fail1;
+       }
+
+       if (result == 0)
+               result = alloc_requests(dev, link, qlen(dev->gadget,
+                                       dev->qmult));
+
+       if (result == 0) {
+               dev->zlp = link->is_zlp_ok;
+               DBG(dev, "qlen %d\n", qlen(dev->gadget, dev->qmult));
+
+               dev->header_len = link->header_len;
+               dev->unwrap = link->unwrap;
+               dev->wrap = link->wrap;
+
+               spin_lock(&dev->lock);
+               dev->port_usb = link;
+               if (netif_running(dev->net)) {
+                       if (link->open)
+                               link->open(link);
+               } else {
+                       if (link->close)
+                               link->close(link);
+               }
+               spin_unlock(&dev->lock);
+
+               netif_carrier_on(dev->net);
+               if (netif_running(dev->net))
+                       eth_start(dev, GFP_ATOMIC);
+
+       /* on error, disable any endpoints  */
+       } else {
+               (void) usb_ep_disable(link->out_ep);
+fail1:
+               (void) usb_ep_disable(link->in_ep);
+       }
+fail0:
+       /* caller is responsible for cleanup on error */
+       if (result < 0)
+               return ERR_PTR(result);
+       return dev->net;
+}
+EXPORT_SYMBOL_GPL(gether_connect);
+
+/**
+ * gether_disconnect - notify network layer that USB link is inactive
+ * @link: the USB link, on which gether_connect() was called
+ * Context: irqs blocked
+ *
+ * This is called to deactivate endpoints and let the network layer know
+ * the connection went inactive ("no carrier").
+ *
+ * On return, the state is as if gether_connect() had never been called.
+ * The endpoints are inactive, and accordingly without active USB I/O.
+ * Pointers to endpoint descriptors and endpoint private data are nulled.
+ */
+void gether_disconnect(struct gether *link)
+{
+       struct eth_dev          *dev = link->ioport;
+       struct usb_request      *req;
+
+       WARN_ON(!dev);
+       if (!dev)
+               return;
+
+       DBG(dev, "%s\n", __func__);
+
+       netif_stop_queue(dev->net);
+       netif_carrier_off(dev->net);
+
+       /* disable endpoints, forcing (synchronous) completion
+        * of all pending i/o.  then free the request objects
+        * and forget about the endpoints.
+        */
+       usb_ep_disable(link->in_ep);
+       spin_lock(&dev->req_lock);
+       while (!list_empty(&dev->tx_reqs)) {
+               req = container_of(dev->tx_reqs.next,
+                                       struct usb_request, list);
+               list_del(&req->list);
+
+               spin_unlock(&dev->req_lock);
+               usb_ep_free_request(link->in_ep, req);
+               spin_lock(&dev->req_lock);
+       }
+       spin_unlock(&dev->req_lock);
+       link->in_ep->driver_data = NULL;
+       link->in_ep->desc = NULL;
+
+       usb_ep_disable(link->out_ep);
+       spin_lock(&dev->req_lock);
+       while (!list_empty(&dev->rx_reqs)) {
+               req = container_of(dev->rx_reqs.next,
+                                       struct usb_request, list);
+               list_del(&req->list);
+
+               spin_unlock(&dev->req_lock);
+               usb_ep_free_request(link->out_ep, req);
+               spin_lock(&dev->req_lock);
+       }
+       spin_unlock(&dev->req_lock);
+       link->out_ep->driver_data = NULL;
+       link->out_ep->desc = NULL;
+
+       /* finish forgetting about this USB link episode */
+       dev->header_len = 0;
+       dev->unwrap = NULL;
+       dev->wrap = NULL;
+
+       spin_lock(&dev->lock);
+       dev->port_usb = NULL;
+       spin_unlock(&dev->lock);
+}
+EXPORT_SYMBOL_GPL(gether_disconnect);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/function/u_ether.h b/drivers/usb/gadget/function/u_ether.h
new file mode 100644 (file)
index 0000000..334b389
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * u_ether.h -- interface to USB gadget "ethernet link" utilities
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * 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.
+ */
+
+#ifndef __U_ETHER_H
+#define __U_ETHER_H
+
+#include <linux/err.h>
+#include <linux/if_ether.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/cdc.h>
+#include <linux/netdevice.h>
+
+#include "gadget_chips.h"
+
+#define QMULT_DEFAULT 5
+
+/*
+ * dev_addr: initial value
+ * changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx"
+ * host_addr: this address is invisible to ifconfig
+ */
+#define USB_ETHERNET_MODULE_PARAMETERS() \
+       static unsigned qmult = QMULT_DEFAULT;                          \
+       module_param(qmult, uint, S_IRUGO|S_IWUSR);                     \
+       MODULE_PARM_DESC(qmult, "queue length multiplier at high/super speed");\
+                                                                       \
+       static char *dev_addr;                                          \
+       module_param(dev_addr, charp, S_IRUGO);                         \
+       MODULE_PARM_DESC(dev_addr, "Device Ethernet Address");          \
+                                                                       \
+       static char *host_addr;                                         \
+       module_param(host_addr, charp, S_IRUGO);                        \
+       MODULE_PARM_DESC(host_addr, "Host Ethernet Address")
+
+struct eth_dev;
+
+/*
+ * This represents the USB side of an "ethernet" link, managed by a USB
+ * function which provides control and (maybe) framing.  Two functions
+ * in different configurations could share the same ethernet link/netdev,
+ * using different host interaction models.
+ *
+ * There is a current limitation that only one instance of this link may
+ * be present in any given configuration.  When that's a problem, network
+ * layer facilities can be used to package multiple logical links on this
+ * single "physical" one.
+ */
+struct gether {
+       struct usb_function             func;
+
+       /* updated by gether_{connect,disconnect} */
+       struct eth_dev                  *ioport;
+
+       /* endpoints handle full and/or high speeds */
+       struct usb_ep                   *in_ep;
+       struct usb_ep                   *out_ep;
+
+       bool                            is_zlp_ok;
+
+       u16                             cdc_filter;
+
+       /* hooks for added framing, as needed for RNDIS and EEM. */
+       u32                             header_len;
+       /* NCM requires fixed size bundles */
+       bool                            is_fixed;
+       u32                             fixed_out_len;
+       u32                             fixed_in_len;
+       bool                            supports_multi_frame;
+       struct sk_buff                  *(*wrap)(struct gether *port,
+                                               struct sk_buff *skb);
+       int                             (*unwrap)(struct gether *port,
+                                               struct sk_buff *skb,
+                                               struct sk_buff_head *list);
+
+       /* called on network open/close */
+       void                            (*open)(struct gether *);
+       void                            (*close)(struct gether *);
+};
+
+#define        DEFAULT_FILTER  (USB_CDC_PACKET_TYPE_BROADCAST \
+                       |USB_CDC_PACKET_TYPE_ALL_MULTICAST \
+                       |USB_CDC_PACKET_TYPE_PROMISCUOUS \
+                       |USB_CDC_PACKET_TYPE_DIRECTED)
+
+/* variant of gether_setup that allows customizing network device name */
+struct eth_dev *gether_setup_name(struct usb_gadget *g,
+               const char *dev_addr, const char *host_addr,
+               u8 ethaddr[ETH_ALEN], unsigned qmult, const char *netname);
+
+/* netdev setup/teardown as directed by the gadget driver */
+/* gether_setup - initialize one ethernet-over-usb link
+ * @g: gadget to associated with these links
+ * @ethaddr: NULL, or a buffer in which the ethernet address of the
+ *     host side of the link is recorded
+ * Context: may sleep
+ *
+ * This sets up the single network link that may be exported by a
+ * gadget driver using this framework.  The link layer addresses are
+ * set up using module parameters.
+ *
+ * Returns a eth_dev pointer on success, or an ERR_PTR on failure
+ */
+static inline struct eth_dev *gether_setup(struct usb_gadget *g,
+               const char *dev_addr, const char *host_addr,
+               u8 ethaddr[ETH_ALEN], unsigned qmult)
+{
+       return gether_setup_name(g, dev_addr, host_addr, ethaddr, qmult, "usb");
+}
+
+/*
+ * variant of gether_setup_default that allows customizing
+ * network device name
+ */
+struct net_device *gether_setup_name_default(const char *netname);
+
+/*
+ * gether_register_netdev - register the net device
+ * @net: net device to register
+ *
+ * Registers the net device associated with this ethernet-over-usb link
+ *
+ */
+int gether_register_netdev(struct net_device *net);
+
+/* gether_setup_default - initialize one ethernet-over-usb link
+ * Context: may sleep
+ *
+ * This sets up the single network link that may be exported by a
+ * gadget driver using this framework.  The link layer addresses
+ * are set to random values.
+ *
+ * Returns negative errno, or zero on success
+ */
+static inline struct net_device *gether_setup_default(void)
+{
+       return gether_setup_name_default("usb");
+}
+
+/**
+ * gether_set_gadget - initialize one ethernet-over-usb link with a gadget
+ * @net: device representing this link
+ * @g: the gadget to initialize with
+ *
+ * This associates one ethernet-over-usb link with a gadget.
+ */
+void gether_set_gadget(struct net_device *net, struct usb_gadget *g);
+
+/**
+ * gether_set_dev_addr - initialize an ethernet-over-usb link with eth address
+ * @net: device representing this link
+ * @dev_addr: eth address of this device
+ *
+ * This sets the device-side Ethernet address of this ethernet-over-usb link
+ * if dev_addr is correct.
+ * Returns negative errno if the new address is incorrect.
+ */
+int gether_set_dev_addr(struct net_device *net, const char *dev_addr);
+
+/**
+ * gether_get_dev_addr - get an ethernet-over-usb link eth address
+ * @net: device representing this link
+ * @dev_addr: place to store device's eth address
+ * @len: length of the @dev_addr buffer
+ *
+ * This gets the device-side Ethernet address of this ethernet-over-usb link.
+ * Returns zero on success, else negative errno.
+ */
+int gether_get_dev_addr(struct net_device *net, char *dev_addr, int len);
+
+/**
+ * gether_set_host_addr - initialize an ethernet-over-usb link with host address
+ * @net: device representing this link
+ * @host_addr: eth address of the host
+ *
+ * This sets the host-side Ethernet address of this ethernet-over-usb link
+ * if host_addr is correct.
+ * Returns negative errno if the new address is incorrect.
+ */
+int gether_set_host_addr(struct net_device *net, const char *host_addr);
+
+/**
+ * gether_get_host_addr - get an ethernet-over-usb link host address
+ * @net: device representing this link
+ * @host_addr: place to store eth address of the host
+ * @len: length of the @host_addr buffer
+ *
+ * This gets the host-side Ethernet address of this ethernet-over-usb link.
+ * Returns zero on success, else negative errno.
+ */
+int gether_get_host_addr(struct net_device *net, char *host_addr, int len);
+
+/**
+ * gether_get_host_addr_cdc - get an ethernet-over-usb link host address
+ * @net: device representing this link
+ * @host_addr: place to store eth address of the host
+ * @len: length of the @host_addr buffer
+ *
+ * This gets the CDC formatted host-side Ethernet address of this
+ * ethernet-over-usb link.
+ * Returns zero on success, else negative errno.
+ */
+int gether_get_host_addr_cdc(struct net_device *net, char *host_addr, int len);
+
+/**
+ * gether_get_host_addr_u8 - get an ethernet-over-usb link host address
+ * @net: device representing this link
+ * @host_mac: place to store the eth address of the host
+ *
+ * This gets the binary formatted host-side Ethernet address of this
+ * ethernet-over-usb link.
+ */
+void gether_get_host_addr_u8(struct net_device *net, u8 host_mac[ETH_ALEN]);
+
+/**
+ * gether_set_qmult - initialize an ethernet-over-usb link with a multiplier
+ * @net: device representing this link
+ * @qmult: queue multiplier
+ *
+ * This sets the queue length multiplier of this ethernet-over-usb link.
+ * For higher speeds use longer queues.
+ */
+void gether_set_qmult(struct net_device *net, unsigned qmult);
+
+/**
+ * gether_get_qmult - get an ethernet-over-usb link multiplier
+ * @net: device representing this link
+ *
+ * This gets the queue length multiplier of this ethernet-over-usb link.
+ */
+unsigned gether_get_qmult(struct net_device *net);
+
+/**
+ * gether_get_ifname - get an ethernet-over-usb link interface name
+ * @net: device representing this link
+ * @name: place to store the interface name
+ * @len: length of the @name buffer
+ *
+ * This gets the interface name of this ethernet-over-usb link.
+ * Returns zero on success, else negative errno.
+ */
+int gether_get_ifname(struct net_device *net, char *name, int len);
+
+void gether_cleanup(struct eth_dev *dev);
+
+/* connect/disconnect is handled by individual functions */
+struct net_device *gether_connect(struct gether *);
+void gether_disconnect(struct gether *);
+
+/* Some controllers can't support CDC Ethernet (ECM) ... */
+static inline bool can_support_ecm(struct usb_gadget *gadget)
+{
+       if (!gadget_supports_altsettings(gadget))
+               return false;
+
+       /* Everything else is *presumably* fine ... but this is a bit
+        * chancy, so be **CERTAIN** there are no hardware issues with
+        * your controller.  Add it above if it can't handle CDC.
+        */
+       return true;
+}
+
+#endif /* __U_ETHER_H */
diff --git a/drivers/usb/gadget/function/u_ether_configfs.h b/drivers/usb/gadget/function/u_ether_configfs.h
new file mode 100644 (file)
index 0000000..bcbd301
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * u_ether_configfs.h
+ *
+ * Utility definitions for configfs support in USB Ethernet functions
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __U_ETHER_CONFIGFS_H
+#define __U_ETHER_CONFIGFS_H
+
+#define USB_ETHERNET_CONFIGFS_ITEM(_f_)                                        \
+       CONFIGFS_ATTR_STRUCT(f_##_f_##_opts);                           \
+       CONFIGFS_ATTR_OPS(f_##_f_##_opts);                              \
+                                                                       \
+       static void _f_##_attr_release(struct config_item *item)        \
+       {                                                               \
+               struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item);  \
+                                                                       \
+               usb_put_function_instance(&opts->func_inst);            \
+       }                                                               \
+                                                                       \
+       static struct configfs_item_operations _f_##_item_ops = {       \
+               .release        = _f_##_attr_release,                   \
+               .show_attribute = f_##_f_##_opts_attr_show,             \
+               .store_attribute = f_##_f_##_opts_attr_store,           \
+       }
+
+#define USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(_f_)                  \
+       static ssize_t _f_##_opts_dev_addr_show(struct f_##_f_##_opts *opts, \
+                                               char *page)             \
+       {                                                               \
+               int result;                                             \
+                                                                       \
+               mutex_lock(&opts->lock);                                \
+               result = gether_get_dev_addr(opts->net, page, PAGE_SIZE); \
+               mutex_unlock(&opts->lock);                              \
+                                                                       \
+               return result;                                          \
+       }                                                               \
+                                                                       \
+       static ssize_t _f_##_opts_dev_addr_store(struct f_##_f_##_opts *opts, \
+                                                const char *page, size_t len)\
+       {                                                               \
+               int ret;                                                \
+                                                                       \
+               mutex_lock(&opts->lock);                                \
+               if (opts->refcnt) {                                     \
+                       mutex_unlock(&opts->lock);                      \
+                       return -EBUSY;                                  \
+               }                                                       \
+                                                                       \
+               ret = gether_set_dev_addr(opts->net, page);             \
+               mutex_unlock(&opts->lock);                              \
+               if (!ret)                                               \
+                       ret = len;                                      \
+               return ret;                                             \
+       }                                                               \
+                                                                       \
+       static struct f_##_f_##_opts_attribute f_##_f_##_opts_dev_addr = \
+               __CONFIGFS_ATTR(dev_addr, S_IRUGO | S_IWUSR,            \
+                               _f_##_opts_dev_addr_show,               \
+                               _f_##_opts_dev_addr_store)
+
+#define USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(_f_)                 \
+       static ssize_t _f_##_opts_host_addr_show(struct f_##_f_##_opts *opts, \
+                                                char *page)            \
+       {                                                               \
+               int result;                                             \
+                                                                       \
+               mutex_lock(&opts->lock);                                \
+               result = gether_get_host_addr(opts->net, page, PAGE_SIZE); \
+               mutex_unlock(&opts->lock);                              \
+                                                                       \
+               return result;                                          \
+       }                                                               \
+                                                                       \
+       static ssize_t _f_##_opts_host_addr_store(struct f_##_f_##_opts *opts, \
+                                                 const char *page, size_t len)\
+       {                                                               \
+               int ret;                                                \
+                                                                       \
+               mutex_lock(&opts->lock);                                \
+               if (opts->refcnt) {                                     \
+                       mutex_unlock(&opts->lock);                      \
+                       return -EBUSY;                                  \
+               }                                                       \
+                                                                       \
+               ret = gether_set_host_addr(opts->net, page);            \
+               mutex_unlock(&opts->lock);                              \
+               if (!ret)                                               \
+                       ret = len;                                      \
+               return ret;                                             \
+       }                                                               \
+                                                                       \
+       static struct f_##_f_##_opts_attribute f_##_f_##_opts_host_addr = \
+               __CONFIGFS_ATTR(host_addr, S_IRUGO | S_IWUSR,           \
+                               _f_##_opts_host_addr_show,              \
+                               _f_##_opts_host_addr_store)
+
+#define USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(_f_)                     \
+       static ssize_t _f_##_opts_qmult_show(struct f_##_f_##_opts *opts, \
+                                            char *page)                \
+       {                                                               \
+               unsigned qmult;                                         \
+                                                                       \
+               mutex_lock(&opts->lock);                                \
+               qmult = gether_get_qmult(opts->net);                    \
+               mutex_unlock(&opts->lock);                              \
+               return sprintf(page, "%d", qmult);                      \
+       }                                                               \
+                                                                       \
+       static ssize_t _f_##_opts_qmult_store(struct f_##_f_##_opts *opts, \
+                                             const char *page, size_t len)\
+       {                                                               \
+               u8 val;                                                 \
+               int ret;                                                \
+                                                                       \
+               mutex_lock(&opts->lock);                                \
+               if (opts->refcnt) {                                     \
+                       ret = -EBUSY;                                   \
+                       goto out;                                       \
+               }                                                       \
+                                                                       \
+               ret = kstrtou8(page, 0, &val);                          \
+               if (ret)                                                \
+                       goto out;                                       \
+                                                                       \
+               gether_set_qmult(opts->net, val);                       \
+               ret = len;                                              \
+out:                                                                   \
+               mutex_unlock(&opts->lock);                              \
+               return ret;                                             \
+       }                                                               \
+                                                                       \
+       static struct f_##_f_##_opts_attribute f_##_f_##_opts_qmult =   \
+               __CONFIGFS_ATTR(qmult, S_IRUGO | S_IWUSR,               \
+                               _f_##_opts_qmult_show,          \
+                               _f_##_opts_qmult_store)
+
+#define USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(_f_)                    \
+       static ssize_t _f_##_opts_ifname_show(struct f_##_f_##_opts *opts, \
+                                             char *page)               \
+       {                                                               \
+               int ret;                                                \
+                                                                       \
+               mutex_lock(&opts->lock);                                \
+               ret = gether_get_ifname(opts->net, page, PAGE_SIZE);    \
+               mutex_unlock(&opts->lock);                              \
+                                                                       \
+               return ret;                                             \
+       }                                                               \
+                                                                       \
+       static struct f_##_f_##_opts_attribute f_##_f_##_opts_ifname =  \
+               __CONFIGFS_ATTR_RO(ifname, _f_##_opts_ifname_show)
+
+#endif /* __U_ETHER_CONFIGFS_H */
diff --git a/drivers/usb/gadget/function/u_fs.h b/drivers/usb/gadget/function/u_fs.h
new file mode 100644 (file)
index 0000000..63d6e71
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * u_fs.h
+ *
+ * Utility definitions for the FunctionFS
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef U_FFS_H
+#define U_FFS_H
+
+#include <linux/usb/composite.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+
+#ifdef VERBOSE_DEBUG
+#ifndef pr_vdebug
+#  define pr_vdebug pr_debug
+#endif /* pr_vdebug */
+#  define ffs_dump_mem(prefix, ptr, len) \
+       print_hex_dump_bytes(pr_fmt(prefix ": "), DUMP_PREFIX_NONE, ptr, len)
+#else
+#ifndef pr_vdebug
+#  define pr_vdebug(...)                 do { } while (0)
+#endif /* pr_vdebug */
+#  define ffs_dump_mem(prefix, ptr, len) do { } while (0)
+#endif /* VERBOSE_DEBUG */
+
+#define ENTER()    pr_vdebug("%s()\n", __func__)
+
+struct f_fs_opts;
+
+struct ffs_dev {
+       const char *name;
+       bool name_allocated;
+       bool mounted;
+       bool desc_ready;
+       bool single;
+       struct ffs_data *ffs_data;
+       struct f_fs_opts *opts;
+       struct list_head entry;
+
+       int (*ffs_ready_callback)(struct ffs_data *ffs);
+       void (*ffs_closed_callback)(struct ffs_data *ffs);
+       void *(*ffs_acquire_dev_callback)(struct ffs_dev *dev);
+       void (*ffs_release_dev_callback)(struct ffs_dev *dev);
+};
+
+extern struct mutex ffs_lock;
+
+static inline void ffs_dev_lock(void)
+{
+       mutex_lock(&ffs_lock);
+}
+
+static inline void ffs_dev_unlock(void)
+{
+       mutex_unlock(&ffs_lock);
+}
+
+int ffs_name_dev(struct ffs_dev *dev, const char *name);
+int ffs_single_dev(struct ffs_dev *dev);
+
+struct ffs_epfile;
+struct ffs_function;
+
+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 first 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 epfiles 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_CANCELLED
+};
+
+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 endpoint related structures (basically
+        * usb_ep_queue(), usb_ep_dequeue(), etc. calls) except for
+        * endpoint zero.
+        */
+       spinlock_t                      eps_lock;
+
+       /*
+        * XXX REVISIT do we need our own request? Since we are not
+        * handling setup requests immediately 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 that 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 */
+
+       /* reference counter */
+       atomic_t                        ref;
+       /* how many files are opened (EP0 and others) */
+       atomic_t                        opened;
+
+       /* EP0 state */
+       enum ffs_state                  state;
+
+       /*
+        * Possible transitions:
+        * + 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_CANCELLED -- P: ev.waitq.lock
+        * + FFS_SETUP_CANCELLED -> FFS_NO_SETUP        -- cmpxchg
+        *
+        * This field should never be accessed directly and instead
+        * ffs_setup_state_clear_cancelled function should be used.
+        */
+       enum ffs_setup_state            setup_state;
+
+       /* 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.
+        * Intended 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() */
+       /*
+        * raw_descs is what you kfree, real_descs points inside of raw_descs,
+        * where full speed, high speed and super speed descriptors start.
+        * real_descs_length is the length of all those descriptors.
+        */
+       const void                      *raw_descs_data;
+       const void                      *raw_descs;
+       unsigned                        raw_descs_length;
+       unsigned                        fs_descs_count;
+       unsigned                        hs_descs_count;
+       unsigned                        ss_descs_count;
+       unsigned                        ms_os_descs_count;
+       unsigned                        ms_os_descs_ext_prop_count;
+       unsigned                        ms_os_descs_ext_prop_name_len;
+       unsigned                        ms_os_descs_ext_prop_data_len;
+       void                            *ms_os_descs_ext_prop_avail;
+       void                            *ms_os_descs_ext_prop_name_avail;
+       void                            *ms_os_descs_ext_prop_data_avail;
+
+       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;
+               kuid_t                          uid;
+               kgid_t                          gid;
+       }                               file_perms;
+
+       /*
+        * The endpoint files, filled by ffs_epfiles_create(),
+        * destroyed by ffs_epfiles_destroy().
+        */
+       struct ffs_epfile               *epfiles;
+};
+
+
+struct f_fs_opts {
+       struct usb_function_instance    func_inst;
+       struct ffs_dev                  *dev;
+       unsigned                        refcnt;
+       bool                            no_configfs;
+};
+
+static inline struct f_fs_opts *to_f_fs_opts(struct usb_function_instance *fi)
+{
+       return container_of(fi, struct f_fs_opts, func_inst);
+}
+
+#endif /* U_FFS_H */
diff --git a/drivers/usb/gadget/function/u_gether.h b/drivers/usb/gadget/function/u_gether.h
new file mode 100644 (file)
index 0000000..d407842
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * u_gether.h
+ *
+ * Utility definitions for the subset function
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef U_GETHER_H
+#define U_GETHER_H
+
+#include <linux/usb/composite.h>
+
+struct f_gether_opts {
+       struct usb_function_instance    func_inst;
+       struct net_device               *net;
+       bool                            bound;
+
+       /*
+        * Read/write access to configfs attributes is handled by configfs.
+        *
+        * This is to protect the data from concurrent access by read/write
+        * and create symlink/remove symlink.
+        */
+       struct mutex                    lock;
+       int                             refcnt;
+};
+
+#endif /* U_GETHER_H */
diff --git a/drivers/usb/gadget/function/u_ncm.h b/drivers/usb/gadget/function/u_ncm.h
new file mode 100644 (file)
index 0000000..ce0f3a7
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * u_ncm.h
+ *
+ * Utility definitions for the ncm function
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef U_NCM_H
+#define U_NCM_H
+
+#include <linux/usb/composite.h>
+
+struct f_ncm_opts {
+       struct usb_function_instance    func_inst;
+       struct net_device               *net;
+       bool                            bound;
+
+       /*
+        * Read/write access to configfs attributes is handled by configfs.
+        *
+        * This is to protect the data from concurrent access by read/write
+        * and create symlink/remove symlink.
+        */
+       struct mutex                    lock;
+       int                             refcnt;
+};
+
+#endif /* U_NCM_H */
diff --git a/drivers/usb/gadget/function/u_phonet.h b/drivers/usb/gadget/function/u_phonet.h
new file mode 100644 (file)
index 0000000..98ced18
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * u_phonet.h - interface to Phonet
+ *
+ * Copyright (C) 2007-2008 by Nokia Corporation
+ *
+ * This software is distributed under the terms of the GNU General
+ * Public License ("GPL") as published by the Free Software Foundation,
+ * either version 2 of that License or (at your option) any later version.
+ */
+
+#ifndef __U_PHONET_H
+#define __U_PHONET_H
+
+#include <linux/usb/composite.h>
+#include <linux/usb/cdc.h>
+
+struct f_phonet_opts {
+       struct usb_function_instance func_inst;
+       bool bound;
+       struct net_device *net;
+};
+
+struct net_device *gphonet_setup_default(void);
+void gphonet_set_gadget(struct net_device *net, struct usb_gadget *g);
+int gphonet_register_netdev(struct net_device *net);
+int phonet_bind_config(struct usb_configuration *c, struct net_device *dev);
+void gphonet_cleanup(struct net_device *dev);
+
+#endif /* __U_PHONET_H */
diff --git a/drivers/usb/gadget/function/u_rndis.h b/drivers/usb/gadget/function/u_rndis.h
new file mode 100644 (file)
index 0000000..e902aa4
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * u_rndis.h
+ *
+ * Utility definitions for the subset function
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef U_RNDIS_H
+#define U_RNDIS_H
+
+#include <linux/usb/composite.h>
+
+struct f_rndis_opts {
+       struct usb_function_instance    func_inst;
+       u32                             vendor_id;
+       const char                      *manufacturer;
+       struct net_device               *net;
+       bool                            bound;
+       bool                            borrowed_net;
+
+       struct usb_os_desc              rndis_os_desc;
+       char                            rndis_ext_compat_id[16];
+
+       /*
+        * Read/write access to configfs attributes is handled by configfs.
+        *
+        * This is to protect the data from concurrent access by read/write
+        * and create symlink/remove symlink.
+        */
+       struct mutex                    lock;
+       int                             refcnt;
+};
+
+int rndis_init(void);
+void rndis_exit(void);
+void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net);
+
+#endif /* U_RNDIS_H */
diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c
new file mode 100644 (file)
index 0000000..ad0aca8
--- /dev/null
@@ -0,0 +1,1347 @@
+/*
+ * u_serial.c - utilities for USB gadget "serial port"/TTY support
+ *
+ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2008 David Brownell
+ * Copyright (C) 2008 by Nokia Corporation
+ *
+ * This code also borrows from usbserial.c, which is
+ * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2000 Peter Berger (pberger@brimson.com)
+ * Copyright (C) 2000 Al Borchers (alborchers@steinerpoint.com)
+ *
+ * This software is distributed under the terms of the GNU General
+ * Public License ("GPL") as published by the Free Software Foundation,
+ * either version 2 of that License or (at your option) any later version.
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/module.h>
+
+#include "u_serial.h"
+
+
+/*
+ * This component encapsulates the TTY layer glue needed to provide basic
+ * "serial port" functionality through the USB gadget stack.  Each such
+ * port is exposed through a /dev/ttyGS* node.
+ *
+ * After this module has been loaded, the individual TTY port can be requested
+ * (gserial_alloc_line()) and it will stay available until they are removed
+ * (gserial_free_line()). Each one may be connected to a USB function
+ * (gserial_connect), or disconnected (with gserial_disconnect) when the USB
+ * host issues a config change event. Data can only flow when the port is
+ * connected to the host.
+ *
+ * A given TTY port can be made available in multiple configurations.
+ * For example, each one might expose a ttyGS0 node which provides a
+ * login application.  In one case that might use CDC ACM interface 0,
+ * while another configuration might use interface 3 for that.  The
+ * work to handle that (including descriptor management) is not part
+ * of this component.
+ *
+ * Configurations may expose more than one TTY port.  For example, if
+ * ttyGS0 provides login service, then ttyGS1 might provide dialer access
+ * for a telephone or fax link.  And ttyGS2 might be something that just
+ * needs a simple byte stream interface for some messaging protocol that
+ * is managed in userspace ... OBEX, PTP, and MTP have been mentioned.
+ */
+
+#define PREFIX "ttyGS"
+
+/*
+ * gserial is the lifecycle interface, used by USB functions
+ * gs_port is the I/O nexus, used by the tty driver
+ * tty_struct links to the tty/filesystem framework
+ *
+ * gserial <---> gs_port ... links will be null when the USB link is
+ * inactive; managed by gserial_{connect,disconnect}().  each gserial
+ * instance can wrap its own USB control protocol.
+ *     gserial->ioport == usb_ep->driver_data ... gs_port
+ *     gs_port->port_usb ... gserial
+ *
+ * gs_port <---> tty_struct ... links will be null when the TTY file
+ * isn't opened; managed by gs_open()/gs_close()
+ *     gserial->port_tty ... tty_struct
+ *     tty_struct->driver_data ... gserial
+ */
+
+/* RX and TX queues can buffer QUEUE_SIZE packets before they hit the
+ * next layer of buffering.  For TX that's a circular buffer; for RX
+ * consider it a NOP.  A third layer is provided by the TTY code.
+ */
+#define QUEUE_SIZE             16
+#define WRITE_BUF_SIZE         8192            /* TX only */
+
+/* circular buffer */
+struct gs_buf {
+       unsigned                buf_size;
+       char                    *buf_buf;
+       char                    *buf_get;
+       char                    *buf_put;
+};
+
+/*
+ * The port structure holds info for each port, one for each minor number
+ * (and thus for each /dev/ node).
+ */
+struct gs_port {
+       struct tty_port         port;
+       spinlock_t              port_lock;      /* guard port_* access */
+
+       struct gserial          *port_usb;
+
+       bool                    openclose;      /* open/close in progress */
+       u8                      port_num;
+
+       struct list_head        read_pool;
+       int read_started;
+       int read_allocated;
+       struct list_head        read_queue;
+       unsigned                n_read;
+       struct tasklet_struct   push;
+
+       struct list_head        write_pool;
+       int write_started;
+       int write_allocated;
+       struct gs_buf           port_write_buf;
+       wait_queue_head_t       drain_wait;     /* wait while writes drain */
+
+       /* REVISIT this state ... */
+       struct usb_cdc_line_coding port_line_coding;    /* 8-N-1 etc */
+};
+
+static struct portmaster {
+       struct mutex    lock;                   /* protect open/close */
+       struct gs_port  *port;
+} ports[MAX_U_SERIAL_PORTS];
+
+#define GS_CLOSE_TIMEOUT               15              /* seconds */
+
+
+
+#ifdef VERBOSE_DEBUG
+#ifndef pr_vdebug
+#define pr_vdebug(fmt, arg...) \
+       pr_debug(fmt, ##arg)
+#endif /* pr_vdebug */
+#else
+#ifndef pr_vdebug
+#define pr_vdebug(fmt, arg...) \
+       ({ if (0) pr_debug(fmt, ##arg); })
+#endif /* pr_vdebug */
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/* Circular Buffer */
+
+/*
+ * gs_buf_alloc
+ *
+ * Allocate a circular buffer and all associated memory.
+ */
+static int gs_buf_alloc(struct gs_buf *gb, unsigned size)
+{
+       gb->buf_buf = kmalloc(size, GFP_KERNEL);
+       if (gb->buf_buf == NULL)
+               return -ENOMEM;
+
+       gb->buf_size = size;
+       gb->buf_put = gb->buf_buf;
+       gb->buf_get = gb->buf_buf;
+
+       return 0;
+}
+
+/*
+ * gs_buf_free
+ *
+ * Free the buffer and all associated memory.
+ */
+static void gs_buf_free(struct gs_buf *gb)
+{
+       kfree(gb->buf_buf);
+       gb->buf_buf = NULL;
+}
+
+/*
+ * gs_buf_clear
+ *
+ * Clear out all data in the circular buffer.
+ */
+static void gs_buf_clear(struct gs_buf *gb)
+{
+       gb->buf_get = gb->buf_put;
+       /* equivalent to a get of all data available */
+}
+
+/*
+ * gs_buf_data_avail
+ *
+ * Return the number of bytes of data written into the circular
+ * buffer.
+ */
+static unsigned gs_buf_data_avail(struct gs_buf *gb)
+{
+       return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size;
+}
+
+/*
+ * gs_buf_space_avail
+ *
+ * Return the number of bytes of space available in the circular
+ * buffer.
+ */
+static unsigned gs_buf_space_avail(struct gs_buf *gb)
+{
+       return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size;
+}
+
+/*
+ * gs_buf_put
+ *
+ * Copy data data from a user buffer and put it into the circular buffer.
+ * Restrict to the amount of space available.
+ *
+ * Return the number of bytes copied.
+ */
+static unsigned
+gs_buf_put(struct gs_buf *gb, const char *buf, unsigned count)
+{
+       unsigned len;
+
+       len  = gs_buf_space_avail(gb);
+       if (count > len)
+               count = len;
+
+       if (count == 0)
+               return 0;
+
+       len = gb->buf_buf + gb->buf_size - gb->buf_put;
+       if (count > len) {
+               memcpy(gb->buf_put, buf, len);
+               memcpy(gb->buf_buf, buf+len, count - len);
+               gb->buf_put = gb->buf_buf + count - len;
+       } else {
+               memcpy(gb->buf_put, buf, count);
+               if (count < len)
+                       gb->buf_put += count;
+               else /* count == len */
+                       gb->buf_put = gb->buf_buf;
+       }
+
+       return count;
+}
+
+/*
+ * gs_buf_get
+ *
+ * Get data from the circular buffer and copy to the given buffer.
+ * Restrict to the amount of data available.
+ *
+ * Return the number of bytes copied.
+ */
+static unsigned
+gs_buf_get(struct gs_buf *gb, char *buf, unsigned count)
+{
+       unsigned len;
+
+       len = gs_buf_data_avail(gb);
+       if (count > len)
+               count = len;
+
+       if (count == 0)
+               return 0;
+
+       len = gb->buf_buf + gb->buf_size - gb->buf_get;
+       if (count > len) {
+               memcpy(buf, gb->buf_get, len);
+               memcpy(buf+len, gb->buf_buf, count - len);
+               gb->buf_get = gb->buf_buf + count - len;
+       } else {
+               memcpy(buf, gb->buf_get, count);
+               if (count < len)
+                       gb->buf_get += count;
+               else /* count == len */
+                       gb->buf_get = gb->buf_buf;
+       }
+
+       return count;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* I/O glue between TTY (upper) and USB function (lower) driver layers */
+
+/*
+ * gs_alloc_req
+ *
+ * Allocate a usb_request and its buffer.  Returns a pointer to the
+ * usb_request or NULL if there is an error.
+ */
+struct usb_request *
+gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags)
+{
+       struct usb_request *req;
+
+       req = usb_ep_alloc_request(ep, kmalloc_flags);
+
+       if (req != NULL) {
+               req->length = len;
+               req->buf = kmalloc(len, kmalloc_flags);
+               if (req->buf == NULL) {
+                       usb_ep_free_request(ep, req);
+                       return NULL;
+               }
+       }
+
+       return req;
+}
+EXPORT_SYMBOL_GPL(gs_alloc_req);
+
+/*
+ * gs_free_req
+ *
+ * Free a usb_request and its buffer.
+ */
+void gs_free_req(struct usb_ep *ep, struct usb_request *req)
+{
+       kfree(req->buf);
+       usb_ep_free_request(ep, req);
+}
+EXPORT_SYMBOL_GPL(gs_free_req);
+
+/*
+ * gs_send_packet
+ *
+ * If there is data to send, a packet is built in the given
+ * buffer and the size is returned.  If there is no data to
+ * send, 0 is returned.
+ *
+ * Called with port_lock held.
+ */
+static unsigned
+gs_send_packet(struct gs_port *port, char *packet, unsigned size)
+{
+       unsigned len;
+
+       len = gs_buf_data_avail(&port->port_write_buf);
+       if (len < size)
+               size = len;
+       if (size != 0)
+               size = gs_buf_get(&port->port_write_buf, packet, size);
+       return size;
+}
+
+/*
+ * gs_start_tx
+ *
+ * This function finds available write requests, calls
+ * gs_send_packet to fill these packets with data, and
+ * continues until either there are no more write requests
+ * available or no more data to send.  This function is
+ * run whenever data arrives or write requests are available.
+ *
+ * Context: caller owns port_lock; port_usb is non-null.
+ */
+static int gs_start_tx(struct gs_port *port)
+/*
+__releases(&port->port_lock)
+__acquires(&port->port_lock)
+*/
+{
+       struct list_head        *pool = &port->write_pool;
+       struct usb_ep           *in = port->port_usb->in;
+       int                     status = 0;
+       bool                    do_tty_wake = false;
+
+       while (!list_empty(pool)) {
+               struct usb_request      *req;
+               int                     len;
+
+               if (port->write_started >= QUEUE_SIZE)
+                       break;
+
+               req = list_entry(pool->next, struct usb_request, list);
+               len = gs_send_packet(port, req->buf, in->maxpacket);
+               if (len == 0) {
+                       wake_up_interruptible(&port->drain_wait);
+                       break;
+               }
+               do_tty_wake = true;
+
+               req->length = len;
+               list_del(&req->list);
+               req->zero = (gs_buf_data_avail(&port->port_write_buf) == 0);
+
+               pr_vdebug(PREFIX "%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n",
+                               port->port_num, len, *((u8 *)req->buf),
+                               *((u8 *)req->buf+1), *((u8 *)req->buf+2));
+
+               /* Drop lock while we call out of driver; completions
+                * could be issued while we do so.  Disconnection may
+                * happen too; maybe immediately before we queue this!
+                *
+                * NOTE that we may keep sending data for a while after
+                * the TTY closed (dev->ioport->port_tty is NULL).
+                */
+               spin_unlock(&port->port_lock);
+               status = usb_ep_queue(in, req, GFP_ATOMIC);
+               spin_lock(&port->port_lock);
+
+               if (status) {
+                       pr_debug("%s: %s %s err %d\n",
+                                       __func__, "queue", in->name, status);
+                       list_add(&req->list, pool);
+                       break;
+               }
+
+               port->write_started++;
+
+               /* abort immediately after disconnect */
+               if (!port->port_usb)
+                       break;
+       }
+
+       if (do_tty_wake && port->port.tty)
+               tty_wakeup(port->port.tty);
+       return status;
+}
+
+/*
+ * Context: caller owns port_lock, and port_usb is set
+ */
+static unsigned gs_start_rx(struct gs_port *port)
+/*
+__releases(&port->port_lock)
+__acquires(&port->port_lock)
+*/
+{
+       struct list_head        *pool = &port->read_pool;
+       struct usb_ep           *out = port->port_usb->out;
+
+       while (!list_empty(pool)) {
+               struct usb_request      *req;
+               int                     status;
+               struct tty_struct       *tty;
+
+               /* no more rx if closed */
+               tty = port->port.tty;
+               if (!tty)
+                       break;
+
+               if (port->read_started >= QUEUE_SIZE)
+                       break;
+
+               req = list_entry(pool->next, struct usb_request, list);
+               list_del(&req->list);
+               req->length = out->maxpacket;
+
+               /* drop lock while we call out; the controller driver
+                * may need to call us back (e.g. for disconnect)
+                */
+               spin_unlock(&port->port_lock);
+               status = usb_ep_queue(out, req, GFP_ATOMIC);
+               spin_lock(&port->port_lock);
+
+               if (status) {
+                       pr_debug("%s: %s %s err %d\n",
+                                       __func__, "queue", out->name, status);
+                       list_add(&req->list, pool);
+                       break;
+               }
+               port->read_started++;
+
+               /* abort immediately after disconnect */
+               if (!port->port_usb)
+                       break;
+       }
+       return port->read_started;
+}
+
+/*
+ * RX tasklet takes data out of the RX queue and hands it up to the TTY
+ * layer until it refuses to take any more data (or is throttled back).
+ * Then it issues reads for any further data.
+ *
+ * If the RX queue becomes full enough that no usb_request is queued,
+ * the OUT endpoint may begin NAKing as soon as its FIFO fills up.
+ * So QUEUE_SIZE packets plus however many the FIFO holds (usually two)
+ * can be buffered before the TTY layer's buffers (currently 64 KB).
+ */
+static void gs_rx_push(unsigned long _port)
+{
+       struct gs_port          *port = (void *)_port;
+       struct tty_struct       *tty;
+       struct list_head        *queue = &port->read_queue;
+       bool                    disconnect = false;
+       bool                    do_push = false;
+
+       /* hand any queued data to the tty */
+       spin_lock_irq(&port->port_lock);
+       tty = port->port.tty;
+       while (!list_empty(queue)) {
+               struct usb_request      *req;
+
+               req = list_first_entry(queue, struct usb_request, list);
+
+               /* leave data queued if tty was rx throttled */
+               if (tty && test_bit(TTY_THROTTLED, &tty->flags))
+                       break;
+
+               switch (req->status) {
+               case -ESHUTDOWN:
+                       disconnect = true;
+                       pr_vdebug(PREFIX "%d: shutdown\n", port->port_num);
+                       break;
+
+               default:
+                       /* presumably a transient fault */
+                       pr_warning(PREFIX "%d: unexpected RX status %d\n",
+                                       port->port_num, req->status);
+                       /* FALLTHROUGH */
+               case 0:
+                       /* normal completion */
+                       break;
+               }
+
+               /* push data to (open) tty */
+               if (req->actual) {
+                       char            *packet = req->buf;
+                       unsigned        size = req->actual;
+                       unsigned        n;
+                       int             count;
+
+                       /* we may have pushed part of this packet already... */
+                       n = port->n_read;
+                       if (n) {
+                               packet += n;
+                               size -= n;
+                       }
+
+                       count = tty_insert_flip_string(&port->port, packet,
+                                       size);
+                       if (count)
+                               do_push = true;
+                       if (count != size) {
+                               /* stop pushing; TTY layer can't handle more */
+                               port->n_read += count;
+                               pr_vdebug(PREFIX "%d: rx block %d/%d\n",
+                                               port->port_num,
+                                               count, req->actual);
+                               break;
+                       }
+                       port->n_read = 0;
+               }
+
+               list_move(&req->list, &port->read_pool);
+               port->read_started--;
+       }
+
+       /* Push from tty to ldisc; this is handled by a workqueue,
+        * so we won't get callbacks and can hold port_lock
+        */
+       if (do_push)
+               tty_flip_buffer_push(&port->port);
+
+
+       /* We want our data queue to become empty ASAP, keeping data
+        * in the tty and ldisc (not here).  If we couldn't push any
+        * this time around, there may be trouble unless there's an
+        * implicit tty_unthrottle() call on its way...
+        *
+        * REVISIT we should probably add a timer to keep the tasklet
+        * from starving ... but it's not clear that case ever happens.
+        */
+       if (!list_empty(queue) && tty) {
+               if (!test_bit(TTY_THROTTLED, &tty->flags)) {
+                       if (do_push)
+                               tasklet_schedule(&port->push);
+                       else
+                               pr_warning(PREFIX "%d: RX not scheduled?\n",
+                                       port->port_num);
+               }
+       }
+
+       /* If we're still connected, refill the USB RX queue. */
+       if (!disconnect && port->port_usb)
+               gs_start_rx(port);
+
+       spin_unlock_irq(&port->port_lock);
+}
+
+static void gs_read_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct gs_port  *port = ep->driver_data;
+
+       /* Queue all received data until the tty layer is ready for it. */
+       spin_lock(&port->port_lock);
+       list_add_tail(&req->list, &port->read_queue);
+       tasklet_schedule(&port->push);
+       spin_unlock(&port->port_lock);
+}
+
+static void gs_write_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct gs_port  *port = ep->driver_data;
+
+       spin_lock(&port->port_lock);
+       list_add(&req->list, &port->write_pool);
+       port->write_started--;
+
+       switch (req->status) {
+       default:
+               /* presumably a transient fault */
+               pr_warning("%s: unexpected %s status %d\n",
+                               __func__, ep->name, req->status);
+               /* FALL THROUGH */
+       case 0:
+               /* normal completion */
+               gs_start_tx(port);
+               break;
+
+       case -ESHUTDOWN:
+               /* disconnect */
+               pr_vdebug("%s: %s shutdown\n", __func__, ep->name);
+               break;
+       }
+
+       spin_unlock(&port->port_lock);
+}
+
+static void gs_free_requests(struct usb_ep *ep, struct list_head *head,
+                                                        int *allocated)
+{
+       struct usb_request      *req;
+
+       while (!list_empty(head)) {
+               req = list_entry(head->next, struct usb_request, list);
+               list_del(&req->list);
+               gs_free_req(ep, req);
+               if (allocated)
+                       (*allocated)--;
+       }
+}
+
+static int gs_alloc_requests(struct usb_ep *ep, struct list_head *head,
+               void (*fn)(struct usb_ep *, struct usb_request *),
+               int *allocated)
+{
+       int                     i;
+       struct usb_request      *req;
+       int n = allocated ? QUEUE_SIZE - *allocated : QUEUE_SIZE;
+
+       /* Pre-allocate up to QUEUE_SIZE transfers, but if we can't
+        * do quite that many this time, don't fail ... we just won't
+        * be as speedy as we might otherwise be.
+        */
+       for (i = 0; i < n; i++) {
+               req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC);
+               if (!req)
+                       return list_empty(head) ? -ENOMEM : 0;
+               req->complete = fn;
+               list_add_tail(&req->list, head);
+               if (allocated)
+                       (*allocated)++;
+       }
+       return 0;
+}
+
+/**
+ * gs_start_io - start USB I/O streams
+ * @dev: encapsulates endpoints to use
+ * Context: holding port_lock; port_tty and port_usb are non-null
+ *
+ * We only start I/O when something is connected to both sides of
+ * this port.  If nothing is listening on the host side, we may
+ * be pointlessly filling up our TX buffers and FIFO.
+ */
+static int gs_start_io(struct gs_port *port)
+{
+       struct list_head        *head = &port->read_pool;
+       struct usb_ep           *ep = port->port_usb->out;
+       int                     status;
+       unsigned                started;
+
+       /* Allocate RX and TX I/O buffers.  We can't easily do this much
+        * earlier (with GFP_KERNEL) because the requests are coupled to
+        * endpoints, as are the packet sizes we'll be using.  Different
+        * configurations may use different endpoints with a given port;
+        * and high speed vs full speed changes packet sizes too.
+        */
+       status = gs_alloc_requests(ep, head, gs_read_complete,
+               &port->read_allocated);
+       if (status)
+               return status;
+
+       status = gs_alloc_requests(port->port_usb->in, &port->write_pool,
+                       gs_write_complete, &port->write_allocated);
+       if (status) {
+               gs_free_requests(ep, head, &port->read_allocated);
+               return status;
+       }
+
+       /* queue read requests */
+       port->n_read = 0;
+       started = gs_start_rx(port);
+
+       /* unblock any pending writes into our circular buffer */
+       if (started) {
+               tty_wakeup(port->port.tty);
+       } else {
+               gs_free_requests(ep, head, &port->read_allocated);
+               gs_free_requests(port->port_usb->in, &port->write_pool,
+                       &port->write_allocated);
+               status = -EIO;
+       }
+
+       return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* TTY Driver */
+
+/*
+ * gs_open sets up the link between a gs_port and its associated TTY.
+ * That link is broken *only* by TTY close(), and all driver methods
+ * know that.
+ */
+static int gs_open(struct tty_struct *tty, struct file *file)
+{
+       int             port_num = tty->index;
+       struct gs_port  *port;
+       int             status;
+
+       do {
+               mutex_lock(&ports[port_num].lock);
+               port = ports[port_num].port;
+               if (!port)
+                       status = -ENODEV;
+               else {
+                       spin_lock_irq(&port->port_lock);
+
+                       /* already open?  Great. */
+                       if (port->port.count) {
+                               status = 0;
+                               port->port.count++;
+
+                       /* currently opening/closing? wait ... */
+                       } else if (port->openclose) {
+                               status = -EBUSY;
+
+                       /* ... else we do the work */
+                       } else {
+                               status = -EAGAIN;
+                               port->openclose = true;
+                       }
+                       spin_unlock_irq(&port->port_lock);
+               }
+               mutex_unlock(&ports[port_num].lock);
+
+               switch (status) {
+               default:
+                       /* fully handled */
+                       return status;
+               case -EAGAIN:
+                       /* must do the work */
+                       break;
+               case -EBUSY:
+                       /* wait for EAGAIN task to finish */
+                       msleep(1);
+                       /* REVISIT could have a waitchannel here, if
+                        * concurrent open performance is important
+                        */
+                       break;
+               }
+       } while (status != -EAGAIN);
+
+       /* Do the "real open" */
+       spin_lock_irq(&port->port_lock);
+
+       /* allocate circular buffer on first open */
+       if (port->port_write_buf.buf_buf == NULL) {
+
+               spin_unlock_irq(&port->port_lock);
+               status = gs_buf_alloc(&port->port_write_buf, WRITE_BUF_SIZE);
+               spin_lock_irq(&port->port_lock);
+
+               if (status) {
+                       pr_debug("gs_open: ttyGS%d (%p,%p) no buffer\n",
+                               port->port_num, tty, file);
+                       port->openclose = false;
+                       goto exit_unlock_port;
+               }
+       }
+
+       /* REVISIT if REMOVED (ports[].port NULL), abort the open
+        * to let rmmod work faster (but this way isn't wrong).
+        */
+
+       /* REVISIT maybe wait for "carrier detect" */
+
+       tty->driver_data = port;
+       port->port.tty = tty;
+
+       port->port.count = 1;
+       port->openclose = false;
+
+       /* if connected, start the I/O stream */
+       if (port->port_usb) {
+               struct gserial  *gser = port->port_usb;
+
+               pr_debug("gs_open: start ttyGS%d\n", port->port_num);
+               gs_start_io(port);
+
+               if (gser->connect)
+                       gser->connect(gser);
+       }
+
+       pr_debug("gs_open: ttyGS%d (%p,%p)\n", port->port_num, tty, file);
+
+       status = 0;
+
+exit_unlock_port:
+       spin_unlock_irq(&port->port_lock);
+       return status;
+}
+
+static int gs_writes_finished(struct gs_port *p)
+{
+       int cond;
+
+       /* return true on disconnect or empty buffer */
+       spin_lock_irq(&p->port_lock);
+       cond = (p->port_usb == NULL) || !gs_buf_data_avail(&p->port_write_buf);
+       spin_unlock_irq(&p->port_lock);
+
+       return cond;
+}
+
+static void gs_close(struct tty_struct *tty, struct file *file)
+{
+       struct gs_port *port = tty->driver_data;
+       struct gserial  *gser;
+
+       spin_lock_irq(&port->port_lock);
+
+       if (port->port.count != 1) {
+               if (port->port.count == 0)
+                       WARN_ON(1);
+               else
+                       --port->port.count;
+               goto exit;
+       }
+
+       pr_debug("gs_close: ttyGS%d (%p,%p) ...\n", port->port_num, tty, file);
+
+       /* mark port as closing but in use; we can drop port lock
+        * and sleep if necessary
+        */
+       port->openclose = true;
+       port->port.count = 0;
+
+       gser = port->port_usb;
+       if (gser && gser->disconnect)
+               gser->disconnect(gser);
+
+       /* wait for circular write buffer to drain, disconnect, or at
+        * most GS_CLOSE_TIMEOUT seconds; then discard the rest
+        */
+       if (gs_buf_data_avail(&port->port_write_buf) > 0 && gser) {
+               spin_unlock_irq(&port->port_lock);
+               wait_event_interruptible_timeout(port->drain_wait,
+                                       gs_writes_finished(port),
+                                       GS_CLOSE_TIMEOUT * HZ);
+               spin_lock_irq(&port->port_lock);
+               gser = port->port_usb;
+       }
+
+       /* Iff we're disconnected, there can be no I/O in flight so it's
+        * ok to free the circular buffer; else just scrub it.  And don't
+        * let the push tasklet fire again until we're re-opened.
+        */
+       if (gser == NULL)
+               gs_buf_free(&port->port_write_buf);
+       else
+               gs_buf_clear(&port->port_write_buf);
+
+       tty->driver_data = NULL;
+       port->port.tty = NULL;
+
+       port->openclose = false;
+
+       pr_debug("gs_close: ttyGS%d (%p,%p) done!\n",
+                       port->port_num, tty, file);
+
+       wake_up(&port->port.close_wait);
+exit:
+       spin_unlock_irq(&port->port_lock);
+}
+
+static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count)
+{
+       struct gs_port  *port = tty->driver_data;
+       unsigned long   flags;
+       int             status;
+
+       pr_vdebug("gs_write: ttyGS%d (%p) writing %d bytes\n",
+                       port->port_num, tty, count);
+
+       spin_lock_irqsave(&port->port_lock, flags);
+       if (count)
+               count = gs_buf_put(&port->port_write_buf, buf, count);
+       /* treat count == 0 as flush_chars() */
+       if (port->port_usb)
+               status = gs_start_tx(port);
+       spin_unlock_irqrestore(&port->port_lock, flags);
+
+       return count;
+}
+
+static int gs_put_char(struct tty_struct *tty, unsigned char ch)
+{
+       struct gs_port  *port = tty->driver_data;
+       unsigned long   flags;
+       int             status;
+
+       pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %pf\n",
+               port->port_num, tty, ch, __builtin_return_address(0));
+
+       spin_lock_irqsave(&port->port_lock, flags);
+       status = gs_buf_put(&port->port_write_buf, &ch, 1);
+       spin_unlock_irqrestore(&port->port_lock, flags);
+
+       return status;
+}
+
+static void gs_flush_chars(struct tty_struct *tty)
+{
+       struct gs_port  *port = tty->driver_data;
+       unsigned long   flags;
+
+       pr_vdebug("gs_flush_chars: (%d,%p)\n", port->port_num, tty);
+
+       spin_lock_irqsave(&port->port_lock, flags);
+       if (port->port_usb)
+               gs_start_tx(port);
+       spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+static int gs_write_room(struct tty_struct *tty)
+{
+       struct gs_port  *port = tty->driver_data;
+       unsigned long   flags;
+       int             room = 0;
+
+       spin_lock_irqsave(&port->port_lock, flags);
+       if (port->port_usb)
+               room = gs_buf_space_avail(&port->port_write_buf);
+       spin_unlock_irqrestore(&port->port_lock, flags);
+
+       pr_vdebug("gs_write_room: (%d,%p) room=%d\n",
+               port->port_num, tty, room);
+
+       return room;
+}
+
+static int gs_chars_in_buffer(struct tty_struct *tty)
+{
+       struct gs_port  *port = tty->driver_data;
+       unsigned long   flags;
+       int             chars = 0;
+
+       spin_lock_irqsave(&port->port_lock, flags);
+       chars = gs_buf_data_avail(&port->port_write_buf);
+       spin_unlock_irqrestore(&port->port_lock, flags);
+
+       pr_vdebug("gs_chars_in_buffer: (%d,%p) chars=%d\n",
+               port->port_num, tty, chars);
+
+       return chars;
+}
+
+/* undo side effects of setting TTY_THROTTLED */
+static void gs_unthrottle(struct tty_struct *tty)
+{
+       struct gs_port          *port = tty->driver_data;
+       unsigned long           flags;
+
+       spin_lock_irqsave(&port->port_lock, flags);
+       if (port->port_usb) {
+               /* Kickstart read queue processing.  We don't do xon/xoff,
+                * rts/cts, or other handshaking with the host, but if the
+                * read queue backs up enough we'll be NAKing OUT packets.
+                */
+               tasklet_schedule(&port->push);
+               pr_vdebug(PREFIX "%d: unthrottle\n", port->port_num);
+       }
+       spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+static int gs_break_ctl(struct tty_struct *tty, int duration)
+{
+       struct gs_port  *port = tty->driver_data;
+       int             status = 0;
+       struct gserial  *gser;
+
+       pr_vdebug("gs_break_ctl: ttyGS%d, send break (%d) \n",
+                       port->port_num, duration);
+
+       spin_lock_irq(&port->port_lock);
+       gser = port->port_usb;
+       if (gser && gser->send_break)
+               status = gser->send_break(gser, duration);
+       spin_unlock_irq(&port->port_lock);
+
+       return status;
+}
+
+static const struct tty_operations gs_tty_ops = {
+       .open =                 gs_open,
+       .close =                gs_close,
+       .write =                gs_write,
+       .put_char =             gs_put_char,
+       .flush_chars =          gs_flush_chars,
+       .write_room =           gs_write_room,
+       .chars_in_buffer =      gs_chars_in_buffer,
+       .unthrottle =           gs_unthrottle,
+       .break_ctl =            gs_break_ctl,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static struct tty_driver *gs_tty_driver;
+
+static int
+gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding)
+{
+       struct gs_port  *port;
+       int             ret = 0;
+
+       mutex_lock(&ports[port_num].lock);
+       if (ports[port_num].port) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       port = kzalloc(sizeof(struct gs_port), GFP_KERNEL);
+       if (port == NULL) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       tty_port_init(&port->port);
+       spin_lock_init(&port->port_lock);
+       init_waitqueue_head(&port->drain_wait);
+
+       tasklet_init(&port->push, gs_rx_push, (unsigned long) port);
+
+       INIT_LIST_HEAD(&port->read_pool);
+       INIT_LIST_HEAD(&port->read_queue);
+       INIT_LIST_HEAD(&port->write_pool);
+
+       port->port_num = port_num;
+       port->port_line_coding = *coding;
+
+       ports[port_num].port = port;
+out:
+       mutex_unlock(&ports[port_num].lock);
+       return ret;
+}
+
+static int gs_closed(struct gs_port *port)
+{
+       int cond;
+
+       spin_lock_irq(&port->port_lock);
+       cond = (port->port.count == 0) && !port->openclose;
+       spin_unlock_irq(&port->port_lock);
+       return cond;
+}
+
+static void gserial_free_port(struct gs_port *port)
+{
+       tasklet_kill(&port->push);
+       /* wait for old opens to finish */
+       wait_event(port->port.close_wait, gs_closed(port));
+       WARN_ON(port->port_usb != NULL);
+       tty_port_destroy(&port->port);
+       kfree(port);
+}
+
+void gserial_free_line(unsigned char port_num)
+{
+       struct gs_port  *port;
+
+       mutex_lock(&ports[port_num].lock);
+       if (WARN_ON(!ports[port_num].port)) {
+               mutex_unlock(&ports[port_num].lock);
+               return;
+       }
+       port = ports[port_num].port;
+       ports[port_num].port = NULL;
+       mutex_unlock(&ports[port_num].lock);
+
+       gserial_free_port(port);
+       tty_unregister_device(gs_tty_driver, port_num);
+}
+EXPORT_SYMBOL_GPL(gserial_free_line);
+
+int gserial_alloc_line(unsigned char *line_num)
+{
+       struct usb_cdc_line_coding      coding;
+       struct device                   *tty_dev;
+       int                             ret;
+       int                             port_num;
+
+       coding.dwDTERate = cpu_to_le32(9600);
+       coding.bCharFormat = 8;
+       coding.bParityType = USB_CDC_NO_PARITY;
+       coding.bDataBits = USB_CDC_1_STOP_BITS;
+
+       for (port_num = 0; port_num < MAX_U_SERIAL_PORTS; port_num++) {
+               ret = gs_port_alloc(port_num, &coding);
+               if (ret == -EBUSY)
+                       continue;
+               if (ret)
+                       return ret;
+               break;
+       }
+       if (ret)
+               return ret;
+
+       /* ... and sysfs class devices, so mdev/udev make /dev/ttyGS* */
+
+       tty_dev = tty_port_register_device(&ports[port_num].port->port,
+                       gs_tty_driver, port_num, NULL);
+       if (IS_ERR(tty_dev)) {
+               struct gs_port  *port;
+               pr_err("%s: failed to register tty for port %d, err %ld\n",
+                               __func__, port_num, PTR_ERR(tty_dev));
+
+               ret = PTR_ERR(tty_dev);
+               port = ports[port_num].port;
+               ports[port_num].port = NULL;
+               gserial_free_port(port);
+               goto err;
+       }
+       *line_num = port_num;
+err:
+       return ret;
+}
+EXPORT_SYMBOL_GPL(gserial_alloc_line);
+
+/**
+ * gserial_connect - notify TTY I/O glue that USB link is active
+ * @gser: the function, set up with endpoints and descriptors
+ * @port_num: which port is active
+ * Context: any (usually from irq)
+ *
+ * This is called activate endpoints and let the TTY layer know that
+ * the connection is active ... not unlike "carrier detect".  It won't
+ * necessarily start I/O queues; unless the TTY is held open by any
+ * task, there would be no point.  However, the endpoints will be
+ * activated so the USB host can perform I/O, subject to basic USB
+ * hardware flow control.
+ *
+ * Caller needs to have set up the endpoints and USB function in @dev
+ * before calling this, as well as the appropriate (speed-specific)
+ * endpoint descriptors, and also have allocate @port_num by calling
+ * @gserial_alloc_line().
+ *
+ * Returns negative errno or zero.
+ * On success, ep->driver_data will be overwritten.
+ */
+int gserial_connect(struct gserial *gser, u8 port_num)
+{
+       struct gs_port  *port;
+       unsigned long   flags;
+       int             status;
+
+       if (port_num >= MAX_U_SERIAL_PORTS)
+               return -ENXIO;
+
+       port = ports[port_num].port;
+       if (!port) {
+               pr_err("serial line %d not allocated.\n", port_num);
+               return -EINVAL;
+       }
+       if (port->port_usb) {
+               pr_err("serial line %d is in use.\n", port_num);
+               return -EBUSY;
+       }
+
+       /* activate the endpoints */
+       status = usb_ep_enable(gser->in);
+       if (status < 0)
+               return status;
+       gser->in->driver_data = port;
+
+       status = usb_ep_enable(gser->out);
+       if (status < 0)
+               goto fail_out;
+       gser->out->driver_data = port;
+
+       /* then tell the tty glue that I/O can work */
+       spin_lock_irqsave(&port->port_lock, flags);
+       gser->ioport = port;
+       port->port_usb = gser;
+
+       /* REVISIT unclear how best to handle this state...
+        * we don't really couple it with the Linux TTY.
+        */
+       gser->port_line_coding = port->port_line_coding;
+
+       /* REVISIT if waiting on "carrier detect", signal. */
+
+       /* if it's already open, start I/O ... and notify the serial
+        * protocol about open/close status (connect/disconnect).
+        */
+       if (port->port.count) {
+               pr_debug("gserial_connect: start ttyGS%d\n", port->port_num);
+               gs_start_io(port);
+               if (gser->connect)
+                       gser->connect(gser);
+       } else {
+               if (gser->disconnect)
+                       gser->disconnect(gser);
+       }
+
+       spin_unlock_irqrestore(&port->port_lock, flags);
+
+       return status;
+
+fail_out:
+       usb_ep_disable(gser->in);
+       gser->in->driver_data = NULL;
+       return status;
+}
+EXPORT_SYMBOL_GPL(gserial_connect);
+/**
+ * gserial_disconnect - notify TTY I/O glue that USB link is inactive
+ * @gser: the function, on which gserial_connect() was called
+ * Context: any (usually from irq)
+ *
+ * This is called to deactivate endpoints and let the TTY layer know
+ * that the connection went inactive ... not unlike "hangup".
+ *
+ * On return, the state is as if gserial_connect() had never been called;
+ * there is no active USB I/O on these endpoints.
+ */
+void gserial_disconnect(struct gserial *gser)
+{
+       struct gs_port  *port = gser->ioport;
+       unsigned long   flags;
+
+       if (!port)
+               return;
+
+       /* tell the TTY glue not to do I/O here any more */
+       spin_lock_irqsave(&port->port_lock, flags);
+
+       /* REVISIT as above: how best to track this? */
+       port->port_line_coding = gser->port_line_coding;
+
+       port->port_usb = NULL;
+       gser->ioport = NULL;
+       if (port->port.count > 0 || port->openclose) {
+               wake_up_interruptible(&port->drain_wait);
+               if (port->port.tty)
+                       tty_hangup(port->port.tty);
+       }
+       spin_unlock_irqrestore(&port->port_lock, flags);
+
+       /* disable endpoints, aborting down any active I/O */
+       usb_ep_disable(gser->out);
+       gser->out->driver_data = NULL;
+
+       usb_ep_disable(gser->in);
+       gser->in->driver_data = NULL;
+
+       /* finally, free any unused/unusable I/O buffers */
+       spin_lock_irqsave(&port->port_lock, flags);
+       if (port->port.count == 0 && !port->openclose)
+               gs_buf_free(&port->port_write_buf);
+       gs_free_requests(gser->out, &port->read_pool, NULL);
+       gs_free_requests(gser->out, &port->read_queue, NULL);
+       gs_free_requests(gser->in, &port->write_pool, NULL);
+
+       port->read_allocated = port->read_started =
+               port->write_allocated = port->write_started = 0;
+
+       spin_unlock_irqrestore(&port->port_lock, flags);
+}
+EXPORT_SYMBOL_GPL(gserial_disconnect);
+
+static int userial_init(void)
+{
+       unsigned                        i;
+       int                             status;
+
+       gs_tty_driver = alloc_tty_driver(MAX_U_SERIAL_PORTS);
+       if (!gs_tty_driver)
+               return -ENOMEM;
+
+       gs_tty_driver->driver_name = "g_serial";
+       gs_tty_driver->name = PREFIX;
+       /* uses dynamically assigned dev_t values */
+
+       gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+       gs_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+       gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+       gs_tty_driver->init_termios = tty_std_termios;
+
+       /* 9600-8-N-1 ... matches defaults expected by "usbser.sys" on
+        * MS-Windows.  Otherwise, most of these flags shouldn't affect
+        * anything unless we were to actually hook up to a serial line.
+        */
+       gs_tty_driver->init_termios.c_cflag =
+                       B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+       gs_tty_driver->init_termios.c_ispeed = 9600;
+       gs_tty_driver->init_termios.c_ospeed = 9600;
+
+       tty_set_operations(gs_tty_driver, &gs_tty_ops);
+       for (i = 0; i < MAX_U_SERIAL_PORTS; i++)
+               mutex_init(&ports[i].lock);
+
+       /* export the driver ... */
+       status = tty_register_driver(gs_tty_driver);
+       if (status) {
+               pr_err("%s: cannot register, err %d\n",
+                               __func__, status);
+               goto fail;
+       }
+
+       pr_debug("%s: registered %d ttyGS* device%s\n", __func__,
+                       MAX_U_SERIAL_PORTS,
+                       (MAX_U_SERIAL_PORTS == 1) ? "" : "s");
+
+       return status;
+fail:
+       put_tty_driver(gs_tty_driver);
+       gs_tty_driver = NULL;
+       return status;
+}
+module_init(userial_init);
+
+static void userial_cleanup(void)
+{
+       tty_unregister_driver(gs_tty_driver);
+       put_tty_driver(gs_tty_driver);
+       gs_tty_driver = NULL;
+}
+module_exit(userial_cleanup);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/u_serial.h b/drivers/usb/gadget/function/u_serial.h
new file mode 100644 (file)
index 0000000..c20210c
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * u_serial.h - interface to USB gadget "serial port"/TTY utilities
+ *
+ * Copyright (C) 2008 David Brownell
+ * Copyright (C) 2008 by Nokia Corporation
+ *
+ * This software is distributed under the terms of the GNU General
+ * Public License ("GPL") as published by the Free Software Foundation,
+ * either version 2 of that License or (at your option) any later version.
+ */
+
+#ifndef __U_SERIAL_H
+#define __U_SERIAL_H
+
+#include <linux/usb/composite.h>
+#include <linux/usb/cdc.h>
+
+#define MAX_U_SERIAL_PORTS     4
+
+struct f_serial_opts {
+       struct usb_function_instance func_inst;
+       u8 port_num;
+};
+
+/*
+ * One non-multiplexed "serial" I/O port ... there can be several of these
+ * on any given USB peripheral device, if it provides enough endpoints.
+ *
+ * The "u_serial" utility component exists to do one thing:  manage TTY
+ * style I/O using the USB peripheral endpoints listed here, including
+ * hookups to sysfs and /dev for each logical "tty" device.
+ *
+ * REVISIT at least ACM could support tiocmget() if needed.
+ *
+ * REVISIT someday, allow multiplexing several TTYs over these endpoints.
+ */
+struct gserial {
+       struct usb_function             func;
+
+       /* port is managed by gserial_{connect,disconnect} */
+       struct gs_port                  *ioport;
+
+       struct usb_ep                   *in;
+       struct usb_ep                   *out;
+
+       /* REVISIT avoid this CDC-ACM support harder ... */
+       struct usb_cdc_line_coding port_line_coding;    /* 9600-8-N-1 etc */
+
+       /* notification callbacks */
+       void (*connect)(struct gserial *p);
+       void (*disconnect)(struct gserial *p);
+       int (*send_break)(struct gserial *p, int duration);
+};
+
+/* utilities to allocate/free request and buffer */
+struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags);
+void gs_free_req(struct usb_ep *, struct usb_request *req);
+
+/* management of individual TTY ports */
+int gserial_alloc_line(unsigned char *port_line);
+void gserial_free_line(unsigned char port_line);
+
+/* connect/disconnect is handled by individual functions */
+int gserial_connect(struct gserial *, u8 port_num);
+void gserial_disconnect(struct gserial *);
+
+/* functions are bound to configurations by a config or gadget driver */
+int gser_bind_config(struct usb_configuration *c, u8 port_num);
+int obex_bind_config(struct usb_configuration *c, u8 port_num);
+
+#endif /* __U_SERIAL_H */
diff --git a/drivers/usb/gadget/function/u_uac1.c b/drivers/usb/gadget/function/u_uac1.c
new file mode 100644 (file)
index 0000000..7a55fea
--- /dev/null
@@ -0,0 +1,330 @@
+/*
+ * u_uac1.c -- ALSA audio utilities for Gadget stack
+ *
+ * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
+ * Copyright (C) 2008 Analog Devices, Inc
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/random.h>
+#include <linux/syscalls.h>
+
+#include "u_uac1.h"
+
+/*
+ * This component encapsulates the ALSA devices for USB audio gadget
+ */
+
+#define FILE_PCM_PLAYBACK      "/dev/snd/pcmC0D0p"
+#define FILE_PCM_CAPTURE       "/dev/snd/pcmC0D0c"
+#define FILE_CONTROL           "/dev/snd/controlC0"
+
+static char *fn_play = FILE_PCM_PLAYBACK;
+module_param(fn_play, charp, S_IRUGO);
+MODULE_PARM_DESC(fn_play, "Playback PCM device file name");
+
+static char *fn_cap = FILE_PCM_CAPTURE;
+module_param(fn_cap, charp, S_IRUGO);
+MODULE_PARM_DESC(fn_cap, "Capture PCM device file name");
+
+static char *fn_cntl = FILE_CONTROL;
+module_param(fn_cntl, charp, S_IRUGO);
+MODULE_PARM_DESC(fn_cntl, "Control device file name");
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * Some ALSA internal helper functions
+ */
+static int snd_interval_refine_set(struct snd_interval *i, unsigned int val)
+{
+       struct snd_interval t;
+       t.empty = 0;
+       t.min = t.max = val;
+       t.openmin = t.openmax = 0;
+       t.integer = 1;
+       return snd_interval_refine(i, &t);
+}
+
+static int _snd_pcm_hw_param_set(struct snd_pcm_hw_params *params,
+                                snd_pcm_hw_param_t var, unsigned int val,
+                                int dir)
+{
+       int changed;
+       if (hw_is_mask(var)) {
+               struct snd_mask *m = hw_param_mask(params, var);
+               if (val == 0 && dir < 0) {
+                       changed = -EINVAL;
+                       snd_mask_none(m);
+               } else {
+                       if (dir > 0)
+                               val++;
+                       else if (dir < 0)
+                               val--;
+                       changed = snd_mask_refine_set(
+                                       hw_param_mask(params, var), val);
+               }
+       } else if (hw_is_interval(var)) {
+               struct snd_interval *i = hw_param_interval(params, var);
+               if (val == 0 && dir < 0) {
+                       changed = -EINVAL;
+                       snd_interval_none(i);
+               } else if (dir == 0)
+                       changed = snd_interval_refine_set(i, val);
+               else {
+                       struct snd_interval t;
+                       t.openmin = 1;
+                       t.openmax = 1;
+                       t.empty = 0;
+                       t.integer = 0;
+                       if (dir < 0) {
+                               t.min = val - 1;
+                               t.max = val;
+                       } else {
+                               t.min = val;
+                               t.max = val+1;
+                       }
+                       changed = snd_interval_refine(i, &t);
+               }
+       } else
+               return -EINVAL;
+       if (changed) {
+               params->cmask |= 1 << var;
+               params->rmask |= 1 << var;
+       }
+       return changed;
+}
+/*-------------------------------------------------------------------------*/
+
+/**
+ * Set default hardware params
+ */
+static int playback_default_hw_params(struct gaudio_snd_dev *snd)
+{
+       struct snd_pcm_substream *substream = snd->substream;
+       struct snd_pcm_hw_params *params;
+       snd_pcm_sframes_t result;
+
+       /*
+       * SNDRV_PCM_ACCESS_RW_INTERLEAVED,
+       * SNDRV_PCM_FORMAT_S16_LE
+       * CHANNELS: 2
+       * RATE: 48000
+       */
+       snd->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
+       snd->format = SNDRV_PCM_FORMAT_S16_LE;
+       snd->channels = 2;
+       snd->rate = 48000;
+
+       params = kzalloc(sizeof(*params), GFP_KERNEL);
+       if (!params)
+               return -ENOMEM;
+
+       _snd_pcm_hw_params_any(params);
+       _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS,
+                       snd->access, 0);
+       _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT,
+                       snd->format, 0);
+       _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS,
+                       snd->channels, 0);
+       _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE,
+                       snd->rate, 0);
+
+       snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
+       snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, params);
+
+       result = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL);
+       if (result < 0) {
+               ERROR(snd->card,
+                       "Preparing sound card failed: %d\n", (int)result);
+               kfree(params);
+               return result;
+       }
+
+       /* Store the hardware parameters */
+       snd->access = params_access(params);
+       snd->format = params_format(params);
+       snd->channels = params_channels(params);
+       snd->rate = params_rate(params);
+
+       kfree(params);
+
+       INFO(snd->card,
+               "Hardware params: access %x, format %x, channels %d, rate %d\n",
+               snd->access, snd->format, snd->channels, snd->rate);
+
+       return 0;
+}
+
+/**
+ * Playback audio buffer data by ALSA PCM device
+ */
+static size_t u_audio_playback(struct gaudio *card, void *buf, size_t count)
+{
+       struct gaudio_snd_dev   *snd = &card->playback;
+       struct snd_pcm_substream *substream = snd->substream;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       mm_segment_t old_fs;
+       ssize_t result;
+       snd_pcm_sframes_t frames;
+
+try_again:
+       if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
+               runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+               result = snd_pcm_kernel_ioctl(substream,
+                               SNDRV_PCM_IOCTL_PREPARE, NULL);
+               if (result < 0) {
+                       ERROR(card, "Preparing sound card failed: %d\n",
+                                       (int)result);
+                       return result;
+               }
+       }
+
+       frames = bytes_to_frames(runtime, count);
+       old_fs = get_fs();
+       set_fs(KERNEL_DS);
+       result = snd_pcm_lib_write(snd->substream, (void __user *)buf, frames);
+       if (result != frames) {
+               ERROR(card, "Playback error: %d\n", (int)result);
+               set_fs(old_fs);
+               goto try_again;
+       }
+       set_fs(old_fs);
+
+       return 0;
+}
+
+static int u_audio_get_playback_channels(struct gaudio *card)
+{
+       return card->playback.channels;
+}
+
+static int u_audio_get_playback_rate(struct gaudio *card)
+{
+       return card->playback.rate;
+}
+
+/**
+ * Open ALSA PCM and control device files
+ * Initial the PCM or control device
+ */
+static int gaudio_open_snd_dev(struct gaudio *card)
+{
+       struct snd_pcm_file *pcm_file;
+       struct gaudio_snd_dev *snd;
+
+       if (!card)
+               return -ENODEV;
+
+       /* Open control device */
+       snd = &card->control;
+       snd->filp = filp_open(fn_cntl, O_RDWR, 0);
+       if (IS_ERR(snd->filp)) {
+               int ret = PTR_ERR(snd->filp);
+               ERROR(card, "unable to open sound control device file: %s\n",
+                               fn_cntl);
+               snd->filp = NULL;
+               return ret;
+       }
+       snd->card = card;
+
+       /* Open PCM playback device and setup substream */
+       snd = &card->playback;
+       snd->filp = filp_open(fn_play, O_WRONLY, 0);
+       if (IS_ERR(snd->filp)) {
+               int ret = PTR_ERR(snd->filp);
+
+               ERROR(card, "No such PCM playback device: %s\n", fn_play);
+               snd->filp = NULL;
+               return ret;
+       }
+       pcm_file = snd->filp->private_data;
+       snd->substream = pcm_file->substream;
+       snd->card = card;
+       playback_default_hw_params(snd);
+
+       /* Open PCM capture device and setup substream */
+       snd = &card->capture;
+       snd->filp = filp_open(fn_cap, O_RDONLY, 0);
+       if (IS_ERR(snd->filp)) {
+               ERROR(card, "No such PCM capture device: %s\n", fn_cap);
+               snd->substream = NULL;
+               snd->card = NULL;
+               snd->filp = NULL;
+       } else {
+               pcm_file = snd->filp->private_data;
+               snd->substream = pcm_file->substream;
+               snd->card = card;
+       }
+
+       return 0;
+}
+
+/**
+ * Close ALSA PCM and control device files
+ */
+static int gaudio_close_snd_dev(struct gaudio *gau)
+{
+       struct gaudio_snd_dev   *snd;
+
+       /* Close control device */
+       snd = &gau->control;
+       if (snd->filp)
+               filp_close(snd->filp, NULL);
+
+       /* Close PCM playback device and setup substream */
+       snd = &gau->playback;
+       if (snd->filp)
+               filp_close(snd->filp, NULL);
+
+       /* Close PCM capture device and setup substream */
+       snd = &gau->capture;
+       if (snd->filp)
+               filp_close(snd->filp, NULL);
+
+       return 0;
+}
+
+static struct gaudio *the_card;
+/**
+ * gaudio_setup - setup ALSA interface and preparing for USB transfer
+ *
+ * This sets up PCM, mixer or MIDI ALSA devices fore USB gadget using.
+ *
+ * Returns negative errno, or zero on success
+ */
+int __init gaudio_setup(struct gaudio *card)
+{
+       int     ret;
+
+       ret = gaudio_open_snd_dev(card);
+       if (ret)
+               ERROR(card, "we need at least one control device\n");
+       else if (!the_card)
+               the_card = card;
+
+       return ret;
+
+}
+
+/**
+ * gaudio_cleanup - remove ALSA device interface
+ *
+ * This is called to free all resources allocated by @gaudio_setup().
+ */
+void gaudio_cleanup(void)
+{
+       if (the_card) {
+               gaudio_close_snd_dev(the_card);
+               the_card = NULL;
+       }
+}
+
diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h
new file mode 100644 (file)
index 0000000..18c2e72
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * u_uac1.h -- interface to USB gadget "ALSA AUDIO" utilities
+ *
+ * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
+ * Copyright (C) 2008 Analog Devices, Inc
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef __U_AUDIO_H
+#define __U_AUDIO_H
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/composite.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include "gadget_chips.h"
+
+/*
+ * This represents the USB side of an audio card device, managed by a USB
+ * function which provides control and stream interfaces.
+ */
+
+struct gaudio_snd_dev {
+       struct gaudio                   *card;
+       struct file                     *filp;
+       struct snd_pcm_substream        *substream;
+       int                             access;
+       int                             format;
+       int                             channels;
+       int                             rate;
+};
+
+struct gaudio {
+       struct usb_function             func;
+       struct usb_gadget               *gadget;
+
+       /* ALSA sound device interfaces */
+       struct gaudio_snd_dev           control;
+       struct gaudio_snd_dev           playback;
+       struct gaudio_snd_dev           capture;
+
+       /* TODO */
+};
+
+int gaudio_setup(struct gaudio *card);
+void gaudio_cleanup(void);
+
+#endif /* __U_AUDIO_H */
diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h
new file mode 100644 (file)
index 0000000..7a9111d
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ *     uvc_gadget.h  --  USB Video Class Gadget driver
+ *
+ *     Copyright (C) 2009-2010
+ *         Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ */
+
+#ifndef _UVC_GADGET_H_
+#define _UVC_GADGET_H_
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+#include <linux/usb/ch9.h>
+
+#define UVC_EVENT_FIRST                        (V4L2_EVENT_PRIVATE_START + 0)
+#define UVC_EVENT_CONNECT              (V4L2_EVENT_PRIVATE_START + 0)
+#define UVC_EVENT_DISCONNECT           (V4L2_EVENT_PRIVATE_START + 1)
+#define UVC_EVENT_STREAMON             (V4L2_EVENT_PRIVATE_START + 2)
+#define UVC_EVENT_STREAMOFF            (V4L2_EVENT_PRIVATE_START + 3)
+#define UVC_EVENT_SETUP                        (V4L2_EVENT_PRIVATE_START + 4)
+#define UVC_EVENT_DATA                 (V4L2_EVENT_PRIVATE_START + 5)
+#define UVC_EVENT_LAST                 (V4L2_EVENT_PRIVATE_START + 5)
+
+struct uvc_request_data
+{
+       __s32 length;
+       __u8 data[60];
+};
+
+struct uvc_event
+{
+       union {
+               enum usb_device_speed speed;
+               struct usb_ctrlrequest req;
+               struct uvc_request_data data;
+       };
+};
+
+#define UVCIOC_SEND_RESPONSE           _IOW('U', 1, struct uvc_request_data)
+
+#define UVC_INTF_CONTROL               0
+#define UVC_INTF_STREAMING             1
+
+/* ------------------------------------------------------------------------
+ * Debugging, printing and logging
+ */
+
+#ifdef __KERNEL__
+
+#include <linux/usb.h> /* For usb_endpoint_* */
+#include <linux/usb/gadget.h>
+#include <linux/videodev2.h>
+#include <linux/version.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-device.h>
+
+#include "uvc_queue.h"
+
+#define UVC_TRACE_PROBE                                (1 << 0)
+#define UVC_TRACE_DESCR                                (1 << 1)
+#define UVC_TRACE_CONTROL                      (1 << 2)
+#define UVC_TRACE_FORMAT                       (1 << 3)
+#define UVC_TRACE_CAPTURE                      (1 << 4)
+#define UVC_TRACE_CALLS                                (1 << 5)
+#define UVC_TRACE_IOCTL                                (1 << 6)
+#define UVC_TRACE_FRAME                                (1 << 7)
+#define UVC_TRACE_SUSPEND                      (1 << 8)
+#define UVC_TRACE_STATUS                       (1 << 9)
+
+#define UVC_WARN_MINMAX                                0
+#define UVC_WARN_PROBE_DEF                     1
+
+extern unsigned int uvc_gadget_trace_param;
+
+#define uvc_trace(flag, msg...) \
+       do { \
+               if (uvc_gadget_trace_param & flag) \
+                       printk(KERN_DEBUG "uvcvideo: " msg); \
+       } while (0)
+
+#define uvc_warn_once(dev, warn, msg...) \
+       do { \
+               if (!test_and_set_bit(warn, &dev->warnings)) \
+                       printk(KERN_INFO "uvcvideo: " msg); \
+       } while (0)
+
+#define uvc_printk(level, msg...) \
+       printk(level "uvcvideo: " msg)
+
+/* ------------------------------------------------------------------------
+ * Driver specific constants
+ */
+
+#define DRIVER_VERSION                         "0.1.0"
+#define DRIVER_VERSION_NUMBER                  KERNEL_VERSION(0, 1, 0)
+
+#define UVC_NUM_REQUESTS                       4
+#define UVC_MAX_REQUEST_SIZE                   64
+#define UVC_MAX_EVENTS                         4
+
+/* ------------------------------------------------------------------------
+ * Structures
+ */
+
+struct uvc_video
+{
+       struct usb_ep *ep;
+
+       /* Frame parameters */
+       u8 bpp;
+       u32 fcc;
+       unsigned int width;
+       unsigned int height;
+       unsigned int imagesize;
+
+       /* Requests */
+       unsigned int req_size;
+       struct usb_request *req[UVC_NUM_REQUESTS];
+       __u8 *req_buffer[UVC_NUM_REQUESTS];
+       struct list_head req_free;
+       spinlock_t req_lock;
+
+       void (*encode) (struct usb_request *req, struct uvc_video *video,
+                       struct uvc_buffer *buf);
+
+       /* Context data used by the completion handler */
+       __u32 payload_size;
+       __u32 max_payload_size;
+
+       struct uvc_video_queue queue;
+       unsigned int fid;
+};
+
+enum uvc_state
+{
+       UVC_STATE_DISCONNECTED,
+       UVC_STATE_CONNECTED,
+       UVC_STATE_STREAMING,
+};
+
+struct uvc_device
+{
+       struct video_device *vdev;
+       struct v4l2_device v4l2_dev;
+       enum uvc_state state;
+       struct usb_function func;
+       struct uvc_video video;
+
+       /* Descriptors */
+       struct {
+               const struct uvc_descriptor_header * const *fs_control;
+               const struct uvc_descriptor_header * const *ss_control;
+               const struct uvc_descriptor_header * const *fs_streaming;
+               const struct uvc_descriptor_header * const *hs_streaming;
+               const struct uvc_descriptor_header * const *ss_streaming;
+       } desc;
+
+       unsigned int control_intf;
+       struct usb_ep *control_ep;
+       struct usb_request *control_req;
+       void *control_buf;
+
+       unsigned int streaming_intf;
+
+       /* Events */
+       unsigned int event_length;
+       unsigned int event_setup_out : 1;
+};
+
+static inline struct uvc_device *to_uvc(struct usb_function *f)
+{
+       return container_of(f, struct uvc_device, func);
+}
+
+struct uvc_file_handle
+{
+       struct v4l2_fh vfh;
+       struct uvc_video *device;
+};
+
+#define to_uvc_file_handle(handle) \
+       container_of(handle, struct uvc_file_handle, vfh)
+
+/* ------------------------------------------------------------------------
+ * Functions
+ */
+
+extern void uvc_function_setup_continue(struct uvc_device *uvc);
+extern void uvc_endpoint_stream(struct uvc_device *dev);
+
+extern void uvc_function_connect(struct uvc_device *uvc);
+extern void uvc_function_disconnect(struct uvc_device *uvc);
+
+#endif /* __KERNEL__ */
+
+#endif /* _UVC_GADGET_H_ */
+
diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c
new file mode 100644 (file)
index 0000000..1c29bc9
--- /dev/null
@@ -0,0 +1,407 @@
+/*
+ *     uvc_queue.c  --  USB Video Class driver - Buffers management
+ *
+ *     Copyright (C) 2005-2010
+ *         Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ */
+
+#include <linux/atomic.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+
+#include <media/v4l2-common.h>
+#include <media/videobuf2-vmalloc.h>
+
+#include "uvc.h"
+
+/* ------------------------------------------------------------------------
+ * Video buffers queue management.
+ *
+ * Video queues is initialized by uvc_queue_init(). The function performs
+ * basic initialization of the uvc_video_queue struct and never fails.
+ *
+ * Video buffers are managed by videobuf2. The driver uses a mutex to protect
+ * the videobuf2 queue operations by serializing calls to videobuf2 and a
+ * spinlock to protect the IRQ queue that holds the buffers to be processed by
+ * the driver.
+ */
+
+/* -----------------------------------------------------------------------------
+ * videobuf2 queue operations
+ */
+
+static int uvc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+                          unsigned int *nbuffers, unsigned int *nplanes,
+                          unsigned int sizes[], void *alloc_ctxs[])
+{
+       struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
+       struct uvc_video *video = container_of(queue, struct uvc_video, queue);
+
+       if (*nbuffers > UVC_MAX_VIDEO_BUFFERS)
+               *nbuffers = UVC_MAX_VIDEO_BUFFERS;
+
+       *nplanes = 1;
+
+       sizes[0] = video->imagesize;
+
+       return 0;
+}
+
+static int uvc_buffer_prepare(struct vb2_buffer *vb)
+{
+       struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
+       struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf);
+
+       if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+           vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) {
+               uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n");
+               return -EINVAL;
+       }
+
+       if (unlikely(queue->flags & UVC_QUEUE_DISCONNECTED))
+               return -ENODEV;
+
+       buf->state = UVC_BUF_STATE_QUEUED;
+       buf->mem = vb2_plane_vaddr(vb, 0);
+       buf->length = vb2_plane_size(vb, 0);
+       if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               buf->bytesused = 0;
+       else
+               buf->bytesused = vb2_get_plane_payload(vb, 0);
+
+       return 0;
+}
+
+static void uvc_buffer_queue(struct vb2_buffer *vb)
+{
+       struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
+       struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf);
+       unsigned long flags;
+
+       spin_lock_irqsave(&queue->irqlock, flags);
+
+       if (likely(!(queue->flags & UVC_QUEUE_DISCONNECTED))) {
+               list_add_tail(&buf->queue, &queue->irqqueue);
+       } else {
+               /* If the device is disconnected return the buffer to userspace
+                * directly. The next QBUF call will fail with -ENODEV.
+                */
+               buf->state = UVC_BUF_STATE_ERROR;
+               vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR);
+       }
+
+       spin_unlock_irqrestore(&queue->irqlock, flags);
+}
+
+static void uvc_wait_prepare(struct vb2_queue *vq)
+{
+       struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
+
+       mutex_unlock(&queue->mutex);
+}
+
+static void uvc_wait_finish(struct vb2_queue *vq)
+{
+       struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
+
+       mutex_lock(&queue->mutex);
+}
+
+static struct vb2_ops uvc_queue_qops = {
+       .queue_setup = uvc_queue_setup,
+       .buf_prepare = uvc_buffer_prepare,
+       .buf_queue = uvc_buffer_queue,
+       .wait_prepare = uvc_wait_prepare,
+       .wait_finish = uvc_wait_finish,
+};
+
+static int uvc_queue_init(struct uvc_video_queue *queue,
+                         enum v4l2_buf_type type)
+{
+       int ret;
+
+       queue->queue.type = type;
+       queue->queue.io_modes = VB2_MMAP | VB2_USERPTR;
+       queue->queue.drv_priv = queue;
+       queue->queue.buf_struct_size = sizeof(struct uvc_buffer);
+       queue->queue.ops = &uvc_queue_qops;
+       queue->queue.mem_ops = &vb2_vmalloc_memops;
+       queue->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC
+                                    | V4L2_BUF_FLAG_TSTAMP_SRC_EOF;
+       ret = vb2_queue_init(&queue->queue);
+       if (ret)
+               return ret;
+
+       mutex_init(&queue->mutex);
+       spin_lock_init(&queue->irqlock);
+       INIT_LIST_HEAD(&queue->irqqueue);
+       queue->flags = 0;
+
+       return 0;
+}
+
+/*
+ * Free the video buffers.
+ */
+static void uvc_free_buffers(struct uvc_video_queue *queue)
+{
+       mutex_lock(&queue->mutex);
+       vb2_queue_release(&queue->queue);
+       mutex_unlock(&queue->mutex);
+}
+
+/*
+ * Allocate the video buffers.
+ */
+static int uvc_alloc_buffers(struct uvc_video_queue *queue,
+                            struct v4l2_requestbuffers *rb)
+{
+       int ret;
+
+       mutex_lock(&queue->mutex);
+       ret = vb2_reqbufs(&queue->queue, rb);
+       mutex_unlock(&queue->mutex);
+
+       return ret ? ret : rb->count;
+}
+
+static int uvc_query_buffer(struct uvc_video_queue *queue,
+                           struct v4l2_buffer *buf)
+{
+       int ret;
+
+       mutex_lock(&queue->mutex);
+       ret = vb2_querybuf(&queue->queue, buf);
+       mutex_unlock(&queue->mutex);
+
+       return ret;
+}
+
+static int uvc_queue_buffer(struct uvc_video_queue *queue,
+                           struct v4l2_buffer *buf)
+{
+       unsigned long flags;
+       int ret;
+
+       mutex_lock(&queue->mutex);
+       ret = vb2_qbuf(&queue->queue, buf);
+       if (ret < 0)
+               goto done;
+
+       spin_lock_irqsave(&queue->irqlock, flags);
+       ret = (queue->flags & UVC_QUEUE_PAUSED) != 0;
+       queue->flags &= ~UVC_QUEUE_PAUSED;
+       spin_unlock_irqrestore(&queue->irqlock, flags);
+
+done:
+       mutex_unlock(&queue->mutex);
+       return ret;
+}
+
+/*
+ * Dequeue a video buffer. If nonblocking is false, block until a buffer is
+ * available.
+ */
+static int uvc_dequeue_buffer(struct uvc_video_queue *queue,
+                             struct v4l2_buffer *buf, int nonblocking)
+{
+       int ret;
+
+       mutex_lock(&queue->mutex);
+       ret = vb2_dqbuf(&queue->queue, buf, nonblocking);
+       mutex_unlock(&queue->mutex);
+
+       return ret;
+}
+
+/*
+ * Poll the video queue.
+ *
+ * This function implements video queue polling and is intended to be used by
+ * the device poll handler.
+ */
+static unsigned int uvc_queue_poll(struct uvc_video_queue *queue,
+                                  struct file *file, poll_table *wait)
+{
+       unsigned int ret;
+
+       mutex_lock(&queue->mutex);
+       ret = vb2_poll(&queue->queue, file, wait);
+       mutex_unlock(&queue->mutex);
+
+       return ret;
+}
+
+static int uvc_queue_mmap(struct uvc_video_queue *queue,
+                         struct vm_area_struct *vma)
+{
+       int ret;
+
+       mutex_lock(&queue->mutex);
+       ret = vb2_mmap(&queue->queue, vma);
+       mutex_unlock(&queue->mutex);
+
+       return ret;
+}
+
+#ifndef CONFIG_MMU
+/*
+ * Get unmapped area.
+ *
+ * NO-MMU arch need this function to make mmap() work correctly.
+ */
+static unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue,
+               unsigned long pgoff)
+{
+       unsigned long ret;
+
+       mutex_lock(&queue->mutex);
+       ret = vb2_get_unmapped_area(&queue->queue, 0, 0, pgoff, 0);
+       mutex_unlock(&queue->mutex);
+       return ret;
+}
+#endif
+
+/*
+ * Cancel the video buffers queue.
+ *
+ * Cancelling the queue marks all buffers on the irq queue as erroneous,
+ * wakes them up and removes them from the queue.
+ *
+ * If the disconnect parameter is set, further calls to uvc_queue_buffer will
+ * fail with -ENODEV.
+ *
+ * This function acquires the irq spinlock and can be called from interrupt
+ * context.
+ */
+static void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect)
+{
+       struct uvc_buffer *buf;
+       unsigned long flags;
+
+       spin_lock_irqsave(&queue->irqlock, flags);
+       while (!list_empty(&queue->irqqueue)) {
+               buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
+                                      queue);
+               list_del(&buf->queue);
+               buf->state = UVC_BUF_STATE_ERROR;
+               vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR);
+       }
+       /* This must be protected by the irqlock spinlock to avoid race
+        * conditions between uvc_queue_buffer and the disconnection event that
+        * could result in an interruptible wait in uvc_dequeue_buffer. Do not
+        * blindly replace this logic by checking for the UVC_DEV_DISCONNECTED
+        * state outside the queue code.
+        */
+       if (disconnect)
+               queue->flags |= UVC_QUEUE_DISCONNECTED;
+       spin_unlock_irqrestore(&queue->irqlock, flags);
+}
+
+/*
+ * Enable or disable the video buffers queue.
+ *
+ * The queue must be enabled before starting video acquisition and must be
+ * disabled after stopping it. This ensures that the video buffers queue
+ * state can be properly initialized before buffers are accessed from the
+ * interrupt handler.
+ *
+ * Enabling the video queue initializes parameters (such as sequence number,
+ * sync pattern, ...). If the queue is already enabled, return -EBUSY.
+ *
+ * Disabling the video queue cancels the queue and removes all buffers from
+ * the main queue.
+ *
+ * This function can't be called from interrupt context. Use
+ * uvc_queue_cancel() instead.
+ */
+static int uvc_queue_enable(struct uvc_video_queue *queue, int enable)
+{
+       unsigned long flags;
+       int ret = 0;
+
+       mutex_lock(&queue->mutex);
+       if (enable) {
+               ret = vb2_streamon(&queue->queue, queue->queue.type);
+               if (ret < 0)
+                       goto done;
+
+               queue->sequence = 0;
+               queue->buf_used = 0;
+       } else {
+               ret = vb2_streamoff(&queue->queue, queue->queue.type);
+               if (ret < 0)
+                       goto done;
+
+               spin_lock_irqsave(&queue->irqlock, flags);
+               INIT_LIST_HEAD(&queue->irqqueue);
+
+               /*
+                * FIXME: We need to clear the DISCONNECTED flag to ensure that
+                * applications will be able to queue buffers for the next
+                * streaming run. However, clearing it here doesn't guarantee
+                * that the device will be reconnected in the meantime.
+                */
+               queue->flags &= ~UVC_QUEUE_DISCONNECTED;
+               spin_unlock_irqrestore(&queue->irqlock, flags);
+       }
+
+done:
+       mutex_unlock(&queue->mutex);
+       return ret;
+}
+
+/* called with &queue_irqlock held.. */
+static struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
+                                               struct uvc_buffer *buf)
+{
+       struct uvc_buffer *nextbuf;
+
+       if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) &&
+            buf->length != buf->bytesused) {
+               buf->state = UVC_BUF_STATE_QUEUED;
+               vb2_set_plane_payload(&buf->buf, 0, 0);
+               return buf;
+       }
+
+       list_del(&buf->queue);
+       if (!list_empty(&queue->irqqueue))
+               nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
+                                          queue);
+       else
+               nextbuf = NULL;
+
+       buf->buf.v4l2_buf.field = V4L2_FIELD_NONE;
+       buf->buf.v4l2_buf.sequence = queue->sequence++;
+       v4l2_get_timestamp(&buf->buf.v4l2_buf.timestamp);
+
+       vb2_set_plane_payload(&buf->buf, 0, buf->bytesused);
+       vb2_buffer_done(&buf->buf, VB2_BUF_STATE_DONE);
+
+       return nextbuf;
+}
+
+static struct uvc_buffer *uvc_queue_head(struct uvc_video_queue *queue)
+{
+       struct uvc_buffer *buf = NULL;
+
+       if (!list_empty(&queue->irqqueue))
+               buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
+                                      queue);
+       else
+               queue->flags |= UVC_QUEUE_PAUSED;
+
+       return buf;
+}
+
diff --git a/drivers/usb/gadget/function/uvc_queue.h b/drivers/usb/gadget/function/uvc_queue.h
new file mode 100644 (file)
index 0000000..8e76ce9
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef _UVC_QUEUE_H_
+#define _UVC_QUEUE_H_
+
+#ifdef __KERNEL__
+
+#include <linux/kernel.h>
+#include <linux/poll.h>
+#include <linux/videodev2.h>
+#include <media/videobuf2-core.h>
+
+/* Maximum frame size in bytes, for sanity checking. */
+#define UVC_MAX_FRAME_SIZE     (16*1024*1024)
+/* Maximum number of video buffers. */
+#define UVC_MAX_VIDEO_BUFFERS  32
+
+/* ------------------------------------------------------------------------
+ * Structures.
+ */
+
+enum uvc_buffer_state {
+       UVC_BUF_STATE_IDLE      = 0,
+       UVC_BUF_STATE_QUEUED    = 1,
+       UVC_BUF_STATE_ACTIVE    = 2,
+       UVC_BUF_STATE_DONE      = 3,
+       UVC_BUF_STATE_ERROR     = 4,
+};
+
+struct uvc_buffer {
+       struct vb2_buffer buf;
+       struct list_head queue;
+
+       enum uvc_buffer_state state;
+       void *mem;
+       unsigned int length;
+       unsigned int bytesused;
+};
+
+#define UVC_QUEUE_DISCONNECTED         (1 << 0)
+#define UVC_QUEUE_DROP_INCOMPLETE      (1 << 1)
+#define UVC_QUEUE_PAUSED               (1 << 2)
+
+struct uvc_video_queue {
+       struct vb2_queue queue;
+       struct mutex mutex;     /* Protects queue */
+
+       unsigned int flags;
+       __u32 sequence;
+
+       unsigned int buf_used;
+
+       spinlock_t irqlock;     /* Protects flags and irqqueue */
+       struct list_head irqqueue;
+};
+
+static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
+{
+       return vb2_is_streaming(&queue->queue);
+}
+
+#endif /* __KERNEL__ */
+
+#endif /* _UVC_QUEUE_H_ */
+
diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c
new file mode 100644 (file)
index 0000000..ad48e81
--- /dev/null
@@ -0,0 +1,365 @@
+/*
+ *     uvc_v4l2.c  --  USB Video Class Gadget driver
+ *
+ *     Copyright (C) 2009-2010
+ *         Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+
+#include "uvc.h"
+#include "uvc_queue.h"
+
+/* --------------------------------------------------------------------------
+ * Requests handling
+ */
+
+static int
+uvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data)
+{
+       struct usb_composite_dev *cdev = uvc->func.config->cdev;
+       struct usb_request *req = uvc->control_req;
+
+       if (data->length < 0)
+               return usb_ep_set_halt(cdev->gadget->ep0);
+
+       req->length = min_t(unsigned int, uvc->event_length, data->length);
+       req->zero = data->length < uvc->event_length;
+
+       memcpy(req->buf, data->data, req->length);
+
+       return usb_ep_queue(cdev->gadget->ep0, req, GFP_KERNEL);
+}
+
+/* --------------------------------------------------------------------------
+ * V4L2
+ */
+
+struct uvc_format
+{
+       u8 bpp;
+       u32 fcc;
+};
+
+static struct uvc_format uvc_formats[] = {
+       { 16, V4L2_PIX_FMT_YUYV  },
+       { 0,  V4L2_PIX_FMT_MJPEG },
+};
+
+static int
+uvc_v4l2_get_format(struct uvc_video *video, struct v4l2_format *fmt)
+{
+       fmt->fmt.pix.pixelformat = video->fcc;
+       fmt->fmt.pix.width = video->width;
+       fmt->fmt.pix.height = video->height;
+       fmt->fmt.pix.field = V4L2_FIELD_NONE;
+       fmt->fmt.pix.bytesperline = video->bpp * video->width / 8;
+       fmt->fmt.pix.sizeimage = video->imagesize;
+       fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
+       fmt->fmt.pix.priv = 0;
+
+       return 0;
+}
+
+static int
+uvc_v4l2_set_format(struct uvc_video *video, struct v4l2_format *fmt)
+{
+       struct uvc_format *format;
+       unsigned int imagesize;
+       unsigned int bpl;
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(uvc_formats); ++i) {
+               format = &uvc_formats[i];
+               if (format->fcc == fmt->fmt.pix.pixelformat)
+                       break;
+       }
+
+       if (i == ARRAY_SIZE(uvc_formats)) {
+               printk(KERN_INFO "Unsupported format 0x%08x.\n",
+                       fmt->fmt.pix.pixelformat);
+               return -EINVAL;
+       }
+
+       bpl = format->bpp * fmt->fmt.pix.width / 8;
+       imagesize = bpl ? bpl * fmt->fmt.pix.height : fmt->fmt.pix.sizeimage;
+
+       video->fcc = format->fcc;
+       video->bpp = format->bpp;
+       video->width = fmt->fmt.pix.width;
+       video->height = fmt->fmt.pix.height;
+       video->imagesize = imagesize;
+
+       fmt->fmt.pix.field = V4L2_FIELD_NONE;
+       fmt->fmt.pix.bytesperline = bpl;
+       fmt->fmt.pix.sizeimage = imagesize;
+       fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
+       fmt->fmt.pix.priv = 0;
+
+       return 0;
+}
+
+static int
+uvc_v4l2_open(struct file *file)
+{
+       struct video_device *vdev = video_devdata(file);
+       struct uvc_device *uvc = video_get_drvdata(vdev);
+       struct uvc_file_handle *handle;
+
+       handle = kzalloc(sizeof(*handle), GFP_KERNEL);
+       if (handle == NULL)
+               return -ENOMEM;
+
+       v4l2_fh_init(&handle->vfh, vdev);
+       v4l2_fh_add(&handle->vfh);
+
+       handle->device = &uvc->video;
+       file->private_data = &handle->vfh;
+
+       uvc_function_connect(uvc);
+       return 0;
+}
+
+static int
+uvc_v4l2_release(struct file *file)
+{
+       struct video_device *vdev = video_devdata(file);
+       struct uvc_device *uvc = video_get_drvdata(vdev);
+       struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data);
+       struct uvc_video *video = handle->device;
+
+       uvc_function_disconnect(uvc);
+
+       uvc_video_enable(video, 0);
+       uvc_free_buffers(&video->queue);
+
+       file->private_data = NULL;
+       v4l2_fh_del(&handle->vfh);
+       v4l2_fh_exit(&handle->vfh);
+       kfree(handle);
+
+       return 0;
+}
+
+static long
+uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+{
+       struct video_device *vdev = video_devdata(file);
+       struct uvc_device *uvc = video_get_drvdata(vdev);
+       struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data);
+       struct usb_composite_dev *cdev = uvc->func.config->cdev;
+       struct uvc_video *video = &uvc->video;
+       int ret = 0;
+
+       switch (cmd) {
+       /* Query capabilities */
+       case VIDIOC_QUERYCAP:
+       {
+               struct v4l2_capability *cap = arg;
+
+               memset(cap, 0, sizeof *cap);
+               strlcpy(cap->driver, "g_uvc", sizeof(cap->driver));
+               strlcpy(cap->card, cdev->gadget->name, sizeof(cap->card));
+               strlcpy(cap->bus_info, dev_name(&cdev->gadget->dev),
+                       sizeof cap->bus_info);
+               cap->version = DRIVER_VERSION_NUMBER;
+               cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+               break;
+       }
+
+       /* Get & Set format */
+       case VIDIOC_G_FMT:
+       {
+               struct v4l2_format *fmt = arg;
+
+               if (fmt->type != video->queue.queue.type)
+                       return -EINVAL;
+
+               return uvc_v4l2_get_format(video, fmt);
+       }
+
+       case VIDIOC_S_FMT:
+       {
+               struct v4l2_format *fmt = arg;
+
+               if (fmt->type != video->queue.queue.type)
+                       return -EINVAL;
+
+               return uvc_v4l2_set_format(video, fmt);
+       }
+
+       /* Buffers & streaming */
+       case VIDIOC_REQBUFS:
+       {
+               struct v4l2_requestbuffers *rb = arg;
+
+               if (rb->type != video->queue.queue.type)
+                       return -EINVAL;
+
+               ret = uvc_alloc_buffers(&video->queue, rb);
+               if (ret < 0)
+                       return ret;
+
+               ret = 0;
+               break;
+       }
+
+       case VIDIOC_QUERYBUF:
+       {
+               struct v4l2_buffer *buf = arg;
+
+               return uvc_query_buffer(&video->queue, buf);
+       }
+
+       case VIDIOC_QBUF:
+               if ((ret = uvc_queue_buffer(&video->queue, arg)) < 0)
+                       return ret;
+
+               return uvc_video_pump(video);
+
+       case VIDIOC_DQBUF:
+               return uvc_dequeue_buffer(&video->queue, arg,
+                       file->f_flags & O_NONBLOCK);
+
+       case VIDIOC_STREAMON:
+       {
+               int *type = arg;
+
+               if (*type != video->queue.queue.type)
+                       return -EINVAL;
+
+               /* Enable UVC video. */
+               ret = uvc_video_enable(video, 1);
+               if (ret < 0)
+                       return ret;
+
+               /*
+                * Complete the alternate setting selection setup phase now that
+                * userspace is ready to provide video frames.
+                */
+               uvc_function_setup_continue(uvc);
+               uvc->state = UVC_STATE_STREAMING;
+
+               return 0;
+       }
+
+       case VIDIOC_STREAMOFF:
+       {
+               int *type = arg;
+
+               if (*type != video->queue.queue.type)
+                       return -EINVAL;
+
+               return uvc_video_enable(video, 0);
+       }
+
+       /* Events */
+       case VIDIOC_DQEVENT:
+       {
+               struct v4l2_event *event = arg;
+
+               ret = v4l2_event_dequeue(&handle->vfh, event,
+                                        file->f_flags & O_NONBLOCK);
+               if (ret == 0 && event->type == UVC_EVENT_SETUP) {
+                       struct uvc_event *uvc_event = (void *)&event->u.data;
+
+                       /* Tell the complete callback to generate an event for
+                        * the next request that will be enqueued by
+                        * uvc_event_write.
+                        */
+                       uvc->event_setup_out =
+                               !(uvc_event->req.bRequestType & USB_DIR_IN);
+                       uvc->event_length = uvc_event->req.wLength;
+               }
+
+               return ret;
+       }
+
+       case VIDIOC_SUBSCRIBE_EVENT:
+       {
+               struct v4l2_event_subscription *sub = arg;
+
+               if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST)
+                       return -EINVAL;
+
+               return v4l2_event_subscribe(&handle->vfh, arg, 2, NULL);
+       }
+
+       case VIDIOC_UNSUBSCRIBE_EVENT:
+               return v4l2_event_unsubscribe(&handle->vfh, arg);
+
+       case UVCIOC_SEND_RESPONSE:
+               ret = uvc_send_response(uvc, arg);
+               break;
+
+       default:
+               return -ENOIOCTLCMD;
+       }
+
+       return ret;
+}
+
+static long
+uvc_v4l2_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl);
+}
+
+static int
+uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       struct video_device *vdev = video_devdata(file);
+       struct uvc_device *uvc = video_get_drvdata(vdev);
+
+       return uvc_queue_mmap(&uvc->video.queue, vma);
+}
+
+static unsigned int
+uvc_v4l2_poll(struct file *file, poll_table *wait)
+{
+       struct video_device *vdev = video_devdata(file);
+       struct uvc_device *uvc = video_get_drvdata(vdev);
+
+       return uvc_queue_poll(&uvc->video.queue, file, wait);
+}
+
+#ifndef CONFIG_MMU
+static unsigned long uvc_v4l2_get_unmapped_area(struct file *file,
+               unsigned long addr, unsigned long len, unsigned long pgoff,
+               unsigned long flags)
+{
+       struct video_device *vdev = video_devdata(file);
+       struct uvc_device *uvc = video_get_drvdata(vdev);
+
+       return uvc_queue_get_unmapped_area(&uvc->video.queue, pgoff);
+}
+#endif
+
+static struct v4l2_file_operations uvc_v4l2_fops = {
+       .owner          = THIS_MODULE,
+       .open           = uvc_v4l2_open,
+       .release        = uvc_v4l2_release,
+       .ioctl          = uvc_v4l2_ioctl,
+       .mmap           = uvc_v4l2_mmap,
+       .poll           = uvc_v4l2_poll,
+#ifndef CONFIG_MMU
+       .get_unmapped_area = uvc_v4l2_get_unmapped_area,
+#endif
+};
+
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
new file mode 100644 (file)
index 0000000..71e896d
--- /dev/null
@@ -0,0 +1,394 @@
+/*
+ *     uvc_video.c  --  USB Video Class Gadget driver
+ *
+ *     Copyright (C) 2009-2010
+ *         Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include <media/v4l2-dev.h>
+
+#include "uvc.h"
+#include "uvc_queue.h"
+
+/* --------------------------------------------------------------------------
+ * Video codecs
+ */
+
+static int
+uvc_video_encode_header(struct uvc_video *video, struct uvc_buffer *buf,
+               u8 *data, int len)
+{
+       data[0] = 2;
+       data[1] = UVC_STREAM_EOH | video->fid;
+
+       if (buf->bytesused - video->queue.buf_used <= len - 2)
+               data[1] |= UVC_STREAM_EOF;
+
+       return 2;
+}
+
+static int
+uvc_video_encode_data(struct uvc_video *video, struct uvc_buffer *buf,
+               u8 *data, int len)
+{
+       struct uvc_video_queue *queue = &video->queue;
+       unsigned int nbytes;
+       void *mem;
+
+       /* Copy video data to the USB buffer. */
+       mem = buf->mem + queue->buf_used;
+       nbytes = min((unsigned int)len, buf->bytesused - queue->buf_used);
+
+       memcpy(data, mem, nbytes);
+       queue->buf_used += nbytes;
+
+       return nbytes;
+}
+
+static void
+uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video,
+               struct uvc_buffer *buf)
+{
+       void *mem = req->buf;
+       int len = video->req_size;
+       int ret;
+
+       /* Add a header at the beginning of the payload. */
+       if (video->payload_size == 0) {
+               ret = uvc_video_encode_header(video, buf, mem, len);
+               video->payload_size += ret;
+               mem += ret;
+               len -= ret;
+       }
+
+       /* Process video data. */
+       len = min((int)(video->max_payload_size - video->payload_size), len);
+       ret = uvc_video_encode_data(video, buf, mem, len);
+
+       video->payload_size += ret;
+       len -= ret;
+
+       req->length = video->req_size - len;
+       req->zero = video->payload_size == video->max_payload_size;
+
+       if (buf->bytesused == video->queue.buf_used) {
+               video->queue.buf_used = 0;
+               buf->state = UVC_BUF_STATE_DONE;
+               uvc_queue_next_buffer(&video->queue, buf);
+               video->fid ^= UVC_STREAM_FID;
+
+               video->payload_size = 0;
+       }
+
+       if (video->payload_size == video->max_payload_size ||
+           buf->bytesused == video->queue.buf_used)
+               video->payload_size = 0;
+}
+
+static void
+uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video,
+               struct uvc_buffer *buf)
+{
+       void *mem = req->buf;
+       int len = video->req_size;
+       int ret;
+
+       /* Add the header. */
+       ret = uvc_video_encode_header(video, buf, mem, len);
+       mem += ret;
+       len -= ret;
+
+       /* Process video data. */
+       ret = uvc_video_encode_data(video, buf, mem, len);
+       len -= ret;
+
+       req->length = video->req_size - len;
+
+       if (buf->bytesused == video->queue.buf_used) {
+               video->queue.buf_used = 0;
+               buf->state = UVC_BUF_STATE_DONE;
+               uvc_queue_next_buffer(&video->queue, buf);
+               video->fid ^= UVC_STREAM_FID;
+       }
+}
+
+/* --------------------------------------------------------------------------
+ * Request handling
+ */
+
+/*
+ * I somehow feel that synchronisation won't be easy to achieve here. We have
+ * three events that control USB requests submission:
+ *
+ * - USB request completion: the completion handler will resubmit the request
+ *   if a video buffer is available.
+ *
+ * - USB interface setting selection: in response to a SET_INTERFACE request,
+ *   the handler will start streaming if a video buffer is available and if
+ *   video is not currently streaming.
+ *
+ * - V4L2 buffer queueing: the driver will start streaming if video is not
+ *   currently streaming.
+ *
+ * Race conditions between those 3 events might lead to deadlocks or other
+ * nasty side effects.
+ *
+ * The "video currently streaming" condition can't be detected by the irqqueue
+ * being empty, as a request can still be in flight. A separate "queue paused"
+ * flag is thus needed.
+ *
+ * The paused flag will be set when we try to retrieve the irqqueue head if the
+ * queue is empty, and cleared when we queue a buffer.
+ *
+ * The USB request completion handler will get the buffer at the irqqueue head
+ * under protection of the queue spinlock. If the queue is empty, the streaming
+ * paused flag will be set. Right after releasing the spinlock a userspace
+ * application can queue a buffer. The flag will then cleared, and the ioctl
+ * handler will restart the video stream.
+ */
+static void
+uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct uvc_video *video = req->context;
+       struct uvc_video_queue *queue = &video->queue;
+       struct uvc_buffer *buf;
+       unsigned long flags;
+       int ret;
+
+       switch (req->status) {
+       case 0:
+               break;
+
+       case -ESHUTDOWN:        /* disconnect from host. */
+               printk(KERN_INFO "VS request cancelled.\n");
+               uvc_queue_cancel(queue, 1);
+               goto requeue;
+
+       default:
+               printk(KERN_INFO "VS request completed with status %d.\n",
+                       req->status);
+               uvc_queue_cancel(queue, 0);
+               goto requeue;
+       }
+
+       spin_lock_irqsave(&video->queue.irqlock, flags);
+       buf = uvc_queue_head(&video->queue);
+       if (buf == NULL) {
+               spin_unlock_irqrestore(&video->queue.irqlock, flags);
+               goto requeue;
+       }
+
+       video->encode(req, video, buf);
+
+       if ((ret = usb_ep_queue(ep, req, GFP_ATOMIC)) < 0) {
+               printk(KERN_INFO "Failed to queue request (%d).\n", ret);
+               usb_ep_set_halt(ep);
+               spin_unlock_irqrestore(&video->queue.irqlock, flags);
+               goto requeue;
+       }
+       spin_unlock_irqrestore(&video->queue.irqlock, flags);
+
+       return;
+
+requeue:
+       spin_lock_irqsave(&video->req_lock, flags);
+       list_add_tail(&req->list, &video->req_free);
+       spin_unlock_irqrestore(&video->req_lock, flags);
+}
+
+static int
+uvc_video_free_requests(struct uvc_video *video)
+{
+       unsigned int i;
+
+       for (i = 0; i < UVC_NUM_REQUESTS; ++i) {
+               if (video->req[i]) {
+                       usb_ep_free_request(video->ep, video->req[i]);
+                       video->req[i] = NULL;
+               }
+
+               if (video->req_buffer[i]) {
+                       kfree(video->req_buffer[i]);
+                       video->req_buffer[i] = NULL;
+               }
+       }
+
+       INIT_LIST_HEAD(&video->req_free);
+       video->req_size = 0;
+       return 0;
+}
+
+static int
+uvc_video_alloc_requests(struct uvc_video *video)
+{
+       unsigned int req_size;
+       unsigned int i;
+       int ret = -ENOMEM;
+
+       BUG_ON(video->req_size);
+
+       req_size = video->ep->maxpacket
+                * max_t(unsigned int, video->ep->maxburst, 1)
+                * (video->ep->mult + 1);
+
+       for (i = 0; i < UVC_NUM_REQUESTS; ++i) {
+               video->req_buffer[i] = kmalloc(req_size, GFP_KERNEL);
+               if (video->req_buffer[i] == NULL)
+                       goto error;
+
+               video->req[i] = usb_ep_alloc_request(video->ep, GFP_KERNEL);
+               if (video->req[i] == NULL)
+                       goto error;
+
+               video->req[i]->buf = video->req_buffer[i];
+               video->req[i]->length = 0;
+               video->req[i]->complete = uvc_video_complete;
+               video->req[i]->context = video;
+
+               list_add_tail(&video->req[i]->list, &video->req_free);
+       }
+
+       video->req_size = req_size;
+
+       return 0;
+
+error:
+       uvc_video_free_requests(video);
+       return ret;
+}
+
+/* --------------------------------------------------------------------------
+ * Video streaming
+ */
+
+/*
+ * uvc_video_pump - Pump video data into the USB requests
+ *
+ * This function fills the available USB requests (listed in req_free) with
+ * video data from the queued buffers.
+ */
+static int
+uvc_video_pump(struct uvc_video *video)
+{
+       struct usb_request *req;
+       struct uvc_buffer *buf;
+       unsigned long flags;
+       int ret;
+
+       /* FIXME TODO Race between uvc_video_pump and requests completion
+        * handler ???
+        */
+
+       while (1) {
+               /* Retrieve the first available USB request, protected by the
+                * request lock.
+                */
+               spin_lock_irqsave(&video->req_lock, flags);
+               if (list_empty(&video->req_free)) {
+                       spin_unlock_irqrestore(&video->req_lock, flags);
+                       return 0;
+               }
+               req = list_first_entry(&video->req_free, struct usb_request,
+                                       list);
+               list_del(&req->list);
+               spin_unlock_irqrestore(&video->req_lock, flags);
+
+               /* Retrieve the first available video buffer and fill the
+                * request, protected by the video queue irqlock.
+                */
+               spin_lock_irqsave(&video->queue.irqlock, flags);
+               buf = uvc_queue_head(&video->queue);
+               if (buf == NULL) {
+                       spin_unlock_irqrestore(&video->queue.irqlock, flags);
+                       break;
+               }
+
+               video->encode(req, video, buf);
+
+               /* Queue the USB request */
+               ret = usb_ep_queue(video->ep, req, GFP_ATOMIC);
+               if (ret < 0) {
+                       printk(KERN_INFO "Failed to queue request (%d)\n", ret);
+                       usb_ep_set_halt(video->ep);
+                       spin_unlock_irqrestore(&video->queue.irqlock, flags);
+                       break;
+               }
+               spin_unlock_irqrestore(&video->queue.irqlock, flags);
+       }
+
+       spin_lock_irqsave(&video->req_lock, flags);
+       list_add_tail(&req->list, &video->req_free);
+       spin_unlock_irqrestore(&video->req_lock, flags);
+       return 0;
+}
+
+/*
+ * Enable or disable the video stream.
+ */
+static int
+uvc_video_enable(struct uvc_video *video, int enable)
+{
+       unsigned int i;
+       int ret;
+
+       if (video->ep == NULL) {
+               printk(KERN_INFO "Video enable failed, device is "
+                       "uninitialized.\n");
+               return -ENODEV;
+       }
+
+       if (!enable) {
+               for (i = 0; i < UVC_NUM_REQUESTS; ++i)
+                       usb_ep_dequeue(video->ep, video->req[i]);
+
+               uvc_video_free_requests(video);
+               uvc_queue_enable(&video->queue, 0);
+               return 0;
+       }
+
+       if ((ret = uvc_queue_enable(&video->queue, 1)) < 0)
+               return ret;
+
+       if ((ret = uvc_video_alloc_requests(video)) < 0)
+               return ret;
+
+       if (video->max_payload_size) {
+               video->encode = uvc_video_encode_bulk;
+               video->payload_size = 0;
+       } else
+               video->encode = uvc_video_encode_isoc;
+
+       return uvc_video_pump(video);
+}
+
+/*
+ * Initialize the UVC video stream.
+ */
+static int
+uvc_video_init(struct uvc_video *video)
+{
+       INIT_LIST_HEAD(&video->req_free);
+       spin_lock_init(&video->req_lock);
+
+       video->fcc = V4L2_PIX_FMT_YUYV;
+       video->bpp = 16;
+       video->width = 320;
+       video->height = 240;
+       video->imagesize = 320 * 240 * 2;
+
+       /* Initialize the video buffers queue. */
+       uvc_queue_init(&video->queue, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+       return 0;
+}
+
diff --git a/drivers/usb/gadget/g_zero.h b/drivers/usb/gadget/g_zero.h
deleted file mode 100644 (file)
index 15f1809..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * This header declares the utility functions used by "Gadget Zero", plus
- * interfaces to its two single-configuration function drivers.
- */
-
-#ifndef __G_ZERO_H
-#define __G_ZERO_H
-
-#define GZERO_BULK_BUFLEN      4096
-#define GZERO_QLEN             32
-#define GZERO_ISOC_INTERVAL    4
-#define GZERO_ISOC_MAXPACKET   1024
-
-struct usb_zero_options {
-       unsigned pattern;
-       unsigned isoc_interval;
-       unsigned isoc_maxpacket;
-       unsigned isoc_mult;
-       unsigned isoc_maxburst;
-       unsigned bulk_buflen;
-       unsigned qlen;
-};
-
-struct f_ss_opts {
-       struct usb_function_instance func_inst;
-       unsigned pattern;
-       unsigned isoc_interval;
-       unsigned isoc_maxpacket;
-       unsigned isoc_mult;
-       unsigned isoc_maxburst;
-       unsigned bulk_buflen;
-
-       /*
-        * Read/write access to configfs attributes is handled by configfs.
-        *
-        * This is to protect the data from concurrent access by read/write
-        * and create symlink/remove symlink.
-        */
-       struct mutex                    lock;
-       int                             refcnt;
-};
-
-struct f_lb_opts {
-       struct usb_function_instance func_inst;
-       unsigned bulk_buflen;
-       unsigned qlen;
-
-       /*
-        * Read/write access to configfs attributes is handled by configfs.
-        *
-        * This is to protect the data from concurrent access by read/write
-        * and create symlink/remove symlink.
-        */
-       struct mutex                    lock;
-       int                             refcnt;
-};
-
-void lb_modexit(void);
-int lb_modinit(void);
-
-/* common utilities */
-void free_ep_req(struct usb_ep *ep, struct usb_request *req);
-void disable_endpoints(struct usb_composite_dev *cdev,
-               struct usb_ep *in, struct usb_ep *out,
-               struct usb_ep *iso_in, struct usb_ep *iso_out);
-
-#endif /* __G_ZERO_H */
index d4570744e1064abc9a7fb2bb4311eeac06836d0b..a11aad5635df415422feb5f0a32d8d269cc14b6d 100644 (file)
@@ -4,6 +4,7 @@
 
 ccflags-y                      := -I$(PWD)/drivers/usb/gadget/
 ccflags-y                      += -I$(PWD)/drivers/usb/gadget/udc/
+ccflags-y                      += -I$(PWD)/drivers/usb/gadget/function/
 
 g_zero-y                       := zero.o
 g_audio-y                      := audio.o
diff --git a/drivers/usb/gadget/ndis.h b/drivers/usb/gadget/ndis.h
deleted file mode 100644 (file)
index a19f72d..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * ndis.h
- *
- * ntddndis.h modified by Benedikt Spranger <b.spranger@pengutronix.de>
- *
- * Thanks to the cygwin development team,
- * espacially to Casper S. Hornstrup <chorns@users.sourceforge.net>
- *
- * THIS SOFTWARE IS NOT COPYRIGHTED
- *
- * This source code is offered for use in the public domain. You may
- * use, modify or distribute it freely.
- */
-
-#ifndef _LINUX_NDIS_H
-#define _LINUX_NDIS_H
-
-enum NDIS_DEVICE_POWER_STATE {
-       NdisDeviceStateUnspecified = 0,
-       NdisDeviceStateD0,
-       NdisDeviceStateD1,
-       NdisDeviceStateD2,
-       NdisDeviceStateD3,
-       NdisDeviceStateMaximum
-};
-
-struct NDIS_PM_WAKE_UP_CAPABILITIES {
-       enum NDIS_DEVICE_POWER_STATE  MinMagicPacketWakeUp;
-       enum NDIS_DEVICE_POWER_STATE  MinPatternWakeUp;
-       enum NDIS_DEVICE_POWER_STATE  MinLinkChangeWakeUp;
-};
-
-struct NDIS_PNP_CAPABILITIES {
-       __le32                                  Flags;
-       struct NDIS_PM_WAKE_UP_CAPABILITIES     WakeUpCapabilities;
-};
-
-struct NDIS_PM_PACKET_PATTERN {
-       __le32  Priority;
-       __le32  Reserved;
-       __le32  MaskSize;
-       __le32  PatternOffset;
-       __le32  PatternSize;
-       __le32  PatternFlags;
-};
-
-#endif /* _LINUX_NDIS_H */
diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c
deleted file mode 100644 (file)
index 95d2324..0000000
+++ /dev/null
@@ -1,1190 +0,0 @@
-/*
- * RNDIS MSG parser
- *
- * Authors:    Benedikt Spranger, Pengutronix
- *             Robert Schwebel, Pengutronix
- *
- *              This program is free software; you can redistribute it and/or
- *              modify it under the terms of the GNU General Public License
- *              version 2, as published by the Free Software Foundation.
- *
- *             This software was originally developed in conformance with
- *             Microsoft's Remote NDIS Specification License Agreement.
- *
- * 03/12/2004 Kai-Uwe Bloem <linux-development@auerswald.de>
- *             Fixed message length bug in init_response
- *
- * 03/25/2004 Kai-Uwe Bloem <linux-development@auerswald.de>
- *             Fixed rndis_rm_hdr length bug.
- *
- * Copyright (C) 2004 by David Brownell
- *             updates to merge with Linux 2.6, better match RNDIS spec
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/list.h>
-#include <linux/proc_fs.h>
-#include <linux/slab.h>
-#include <linux/seq_file.h>
-#include <linux/netdevice.h>
-
-#include <asm/io.h>
-#include <asm/byteorder.h>
-#include <asm/unaligned.h>
-
-#include "u_rndis.h"
-
-#undef VERBOSE_DEBUG
-
-#include "rndis.h"
-
-
-/* The driver for your USB chip needs to support ep0 OUT to work with
- * RNDIS, plus all three CDC Ethernet endpoints (interrupt not optional).
- *
- * Windows hosts need an INF file like Documentation/usb/linux.inf
- * and will be happier if you provide the host_addr module parameter.
- */
-
-#if 0
-static int rndis_debug = 0;
-module_param (rndis_debug, int, 0);
-MODULE_PARM_DESC (rndis_debug, "enable debugging");
-#else
-#define rndis_debug            0
-#endif
-
-#define RNDIS_MAX_CONFIGS      1
-
-
-static rndis_params rndis_per_dev_params[RNDIS_MAX_CONFIGS];
-
-/* Driver Version */
-static const __le32 rndis_driver_version = cpu_to_le32(1);
-
-/* Function Prototypes */
-static rndis_resp_t *rndis_add_response(int configNr, u32 length);
-
-
-/* supported OIDs */
-static const u32 oid_supported_list[] =
-{
-       /* the general stuff */
-       RNDIS_OID_GEN_SUPPORTED_LIST,
-       RNDIS_OID_GEN_HARDWARE_STATUS,
-       RNDIS_OID_GEN_MEDIA_SUPPORTED,
-       RNDIS_OID_GEN_MEDIA_IN_USE,
-       RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE,
-       RNDIS_OID_GEN_LINK_SPEED,
-       RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE,
-       RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE,
-       RNDIS_OID_GEN_VENDOR_ID,
-       RNDIS_OID_GEN_VENDOR_DESCRIPTION,
-       RNDIS_OID_GEN_VENDOR_DRIVER_VERSION,
-       RNDIS_OID_GEN_CURRENT_PACKET_FILTER,
-       RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE,
-       RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
-       RNDIS_OID_GEN_PHYSICAL_MEDIUM,
-
-       /* the statistical stuff */
-       RNDIS_OID_GEN_XMIT_OK,
-       RNDIS_OID_GEN_RCV_OK,
-       RNDIS_OID_GEN_XMIT_ERROR,
-       RNDIS_OID_GEN_RCV_ERROR,
-       RNDIS_OID_GEN_RCV_NO_BUFFER,
-#ifdef RNDIS_OPTIONAL_STATS
-       RNDIS_OID_GEN_DIRECTED_BYTES_XMIT,
-       RNDIS_OID_GEN_DIRECTED_FRAMES_XMIT,
-       RNDIS_OID_GEN_MULTICAST_BYTES_XMIT,
-       RNDIS_OID_GEN_MULTICAST_FRAMES_XMIT,
-       RNDIS_OID_GEN_BROADCAST_BYTES_XMIT,
-       RNDIS_OID_GEN_BROADCAST_FRAMES_XMIT,
-       RNDIS_OID_GEN_DIRECTED_BYTES_RCV,
-       RNDIS_OID_GEN_DIRECTED_FRAMES_RCV,
-       RNDIS_OID_GEN_MULTICAST_BYTES_RCV,
-       RNDIS_OID_GEN_MULTICAST_FRAMES_RCV,
-       RNDIS_OID_GEN_BROADCAST_BYTES_RCV,
-       RNDIS_OID_GEN_BROADCAST_FRAMES_RCV,
-       RNDIS_OID_GEN_RCV_CRC_ERROR,
-       RNDIS_OID_GEN_TRANSMIT_QUEUE_LENGTH,
-#endif /* RNDIS_OPTIONAL_STATS */
-
-       /* mandatory 802.3 */
-       /* the general stuff */
-       RNDIS_OID_802_3_PERMANENT_ADDRESS,
-       RNDIS_OID_802_3_CURRENT_ADDRESS,
-       RNDIS_OID_802_3_MULTICAST_LIST,
-       RNDIS_OID_802_3_MAC_OPTIONS,
-       RNDIS_OID_802_3_MAXIMUM_LIST_SIZE,
-
-       /* the statistical stuff */
-       RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT,
-       RNDIS_OID_802_3_XMIT_ONE_COLLISION,
-       RNDIS_OID_802_3_XMIT_MORE_COLLISIONS,
-#ifdef RNDIS_OPTIONAL_STATS
-       RNDIS_OID_802_3_XMIT_DEFERRED,
-       RNDIS_OID_802_3_XMIT_MAX_COLLISIONS,
-       RNDIS_OID_802_3_RCV_OVERRUN,
-       RNDIS_OID_802_3_XMIT_UNDERRUN,
-       RNDIS_OID_802_3_XMIT_HEARTBEAT_FAILURE,
-       RNDIS_OID_802_3_XMIT_TIMES_CRS_LOST,
-       RNDIS_OID_802_3_XMIT_LATE_COLLISIONS,
-#endif /* RNDIS_OPTIONAL_STATS */
-
-#ifdef RNDIS_PM
-       /* PM and wakeup are "mandatory" for USB, but the RNDIS specs
-        * don't say what they mean ... and the NDIS specs are often
-        * confusing and/or ambiguous in this context.  (That is, more
-        * so than their specs for the other OIDs.)
-        *
-        * FIXME someone who knows what these should do, please
-        * implement them!
-        */
-
-       /* power management */
-       OID_PNP_CAPABILITIES,
-       OID_PNP_QUERY_POWER,
-       OID_PNP_SET_POWER,
-
-#ifdef RNDIS_WAKEUP
-       /* wake up host */
-       OID_PNP_ENABLE_WAKE_UP,
-       OID_PNP_ADD_WAKE_UP_PATTERN,
-       OID_PNP_REMOVE_WAKE_UP_PATTERN,
-#endif /* RNDIS_WAKEUP */
-#endif /* RNDIS_PM */
-};
-
-
-/* NDIS Functions */
-static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
-                              unsigned buf_len, rndis_resp_t *r)
-{
-       int retval = -ENOTSUPP;
-       u32 length = 4; /* usually */
-       __le32 *outbuf;
-       int i, count;
-       rndis_query_cmplt_type *resp;
-       struct net_device *net;
-       struct rtnl_link_stats64 temp;
-       const struct rtnl_link_stats64 *stats;
-
-       if (!r) return -ENOMEM;
-       resp = (rndis_query_cmplt_type *)r->buf;
-
-       if (!resp) return -ENOMEM;
-
-       if (buf_len && rndis_debug > 1) {
-               pr_debug("query OID %08x value, len %d:\n", OID, buf_len);
-               for (i = 0; i < buf_len; i += 16) {
-                       pr_debug("%03d: %08x %08x %08x %08x\n", i,
-                               get_unaligned_le32(&buf[i]),
-                               get_unaligned_le32(&buf[i + 4]),
-                               get_unaligned_le32(&buf[i + 8]),
-                               get_unaligned_le32(&buf[i + 12]));
-               }
-       }
-
-       /* response goes here, right after the header */
-       outbuf = (__le32 *)&resp[1];
-       resp->InformationBufferOffset = cpu_to_le32(16);
-
-       net = rndis_per_dev_params[configNr].dev;
-       stats = dev_get_stats(net, &temp);
-
-       switch (OID) {
-
-       /* general oids (table 4-1) */
-
-       /* mandatory */
-       case RNDIS_OID_GEN_SUPPORTED_LIST:
-               pr_debug("%s: RNDIS_OID_GEN_SUPPORTED_LIST\n", __func__);
-               length = sizeof(oid_supported_list);
-               count  = length / sizeof(u32);
-               for (i = 0; i < count; i++)
-                       outbuf[i] = cpu_to_le32(oid_supported_list[i]);
-               retval = 0;
-               break;
-
-       /* mandatory */
-       case RNDIS_OID_GEN_HARDWARE_STATUS:
-               pr_debug("%s: RNDIS_OID_GEN_HARDWARE_STATUS\n", __func__);
-               /* Bogus question!
-                * Hardware must be ready to receive high level protocols.
-                * BTW:
-                * reddite ergo quae sunt Caesaris Caesari
-                * et quae sunt Dei Deo!
-                */
-               *outbuf = cpu_to_le32(0);
-               retval = 0;
-               break;
-
-       /* mandatory */
-       case RNDIS_OID_GEN_MEDIA_SUPPORTED:
-               pr_debug("%s: RNDIS_OID_GEN_MEDIA_SUPPORTED\n", __func__);
-               *outbuf = cpu_to_le32(rndis_per_dev_params[configNr].medium);
-               retval = 0;
-               break;
-
-       /* mandatory */
-       case RNDIS_OID_GEN_MEDIA_IN_USE:
-               pr_debug("%s: RNDIS_OID_GEN_MEDIA_IN_USE\n", __func__);
-               /* one medium, one transport... (maybe you do it better) */
-               *outbuf = cpu_to_le32(rndis_per_dev_params[configNr].medium);
-               retval = 0;
-               break;
-
-       /* mandatory */
-       case RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE:
-               pr_debug("%s: RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE\n", __func__);
-               if (rndis_per_dev_params[configNr].dev) {
-                       *outbuf = cpu_to_le32(
-                               rndis_per_dev_params[configNr].dev->mtu);
-                       retval = 0;
-               }
-               break;
-
-       /* mandatory */
-       case RNDIS_OID_GEN_LINK_SPEED:
-               if (rndis_debug > 1)
-                       pr_debug("%s: RNDIS_OID_GEN_LINK_SPEED\n", __func__);
-               if (rndis_per_dev_params[configNr].media_state
-                               == RNDIS_MEDIA_STATE_DISCONNECTED)
-                       *outbuf = cpu_to_le32(0);
-               else
-                       *outbuf = cpu_to_le32(
-                               rndis_per_dev_params[configNr].speed);
-               retval = 0;
-               break;
-
-       /* mandatory */
-       case RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE:
-               pr_debug("%s: RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE\n", __func__);
-               if (rndis_per_dev_params[configNr].dev) {
-                       *outbuf = cpu_to_le32(
-                               rndis_per_dev_params[configNr].dev->mtu);
-                       retval = 0;
-               }
-               break;
-
-       /* mandatory */
-       case RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE:
-               pr_debug("%s: RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE\n", __func__);
-               if (rndis_per_dev_params[configNr].dev) {
-                       *outbuf = cpu_to_le32(
-                               rndis_per_dev_params[configNr].dev->mtu);
-                       retval = 0;
-               }
-               break;
-
-       /* mandatory */
-       case RNDIS_OID_GEN_VENDOR_ID:
-               pr_debug("%s: RNDIS_OID_GEN_VENDOR_ID\n", __func__);
-               *outbuf = cpu_to_le32(
-                       rndis_per_dev_params[configNr].vendorID);
-               retval = 0;
-               break;
-
-       /* mandatory */
-       case RNDIS_OID_GEN_VENDOR_DESCRIPTION:
-               pr_debug("%s: RNDIS_OID_GEN_VENDOR_DESCRIPTION\n", __func__);
-               if (rndis_per_dev_params[configNr].vendorDescr) {
-                       length = strlen(rndis_per_dev_params[configNr].
-                                       vendorDescr);
-                       memcpy(outbuf,
-                               rndis_per_dev_params[configNr].vendorDescr,
-                               length);
-               } else {
-                       outbuf[0] = 0;
-               }
-               retval = 0;
-               break;
-
-       case RNDIS_OID_GEN_VENDOR_DRIVER_VERSION:
-               pr_debug("%s: RNDIS_OID_GEN_VENDOR_DRIVER_VERSION\n", __func__);
-               /* Created as LE */
-               *outbuf = rndis_driver_version;
-               retval = 0;
-               break;
-
-       /* mandatory */
-       case RNDIS_OID_GEN_CURRENT_PACKET_FILTER:
-               pr_debug("%s: RNDIS_OID_GEN_CURRENT_PACKET_FILTER\n", __func__);
-               *outbuf = cpu_to_le32(*rndis_per_dev_params[configNr].filter);
-               retval = 0;
-               break;
-
-       /* mandatory */
-       case RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE:
-               pr_debug("%s: RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE\n", __func__);
-               *outbuf = cpu_to_le32(RNDIS_MAX_TOTAL_SIZE);
-               retval = 0;
-               break;
-
-       /* mandatory */
-       case RNDIS_OID_GEN_MEDIA_CONNECT_STATUS:
-               if (rndis_debug > 1)
-                       pr_debug("%s: RNDIS_OID_GEN_MEDIA_CONNECT_STATUS\n", __func__);
-               *outbuf = cpu_to_le32(rndis_per_dev_params[configNr]
-                                               .media_state);
-               retval = 0;
-               break;
-
-       case RNDIS_OID_GEN_PHYSICAL_MEDIUM:
-               pr_debug("%s: RNDIS_OID_GEN_PHYSICAL_MEDIUM\n", __func__);
-               *outbuf = cpu_to_le32(0);
-               retval = 0;
-               break;
-
-       /* The RNDIS specification is incomplete/wrong.   Some versions
-        * of MS-Windows expect OIDs that aren't specified there.  Other
-        * versions emit undefined RNDIS messages. DOCUMENT ALL THESE!
-        */
-       case RNDIS_OID_GEN_MAC_OPTIONS:         /* from WinME */
-               pr_debug("%s: RNDIS_OID_GEN_MAC_OPTIONS\n", __func__);
-               *outbuf = cpu_to_le32(
-                         RNDIS_MAC_OPTION_RECEIVE_SERIALIZED
-                       | RNDIS_MAC_OPTION_FULL_DUPLEX);
-               retval = 0;
-               break;
-
-       /* statistics OIDs (table 4-2) */
-
-       /* mandatory */
-       case RNDIS_OID_GEN_XMIT_OK:
-               if (rndis_debug > 1)
-                       pr_debug("%s: RNDIS_OID_GEN_XMIT_OK\n", __func__);
-               if (stats) {
-                       *outbuf = cpu_to_le32(stats->tx_packets
-                               - stats->tx_errors - stats->tx_dropped);
-                       retval = 0;
-               }
-               break;
-
-       /* mandatory */
-       case RNDIS_OID_GEN_RCV_OK:
-               if (rndis_debug > 1)
-                       pr_debug("%s: RNDIS_OID_GEN_RCV_OK\n", __func__);
-               if (stats) {
-                       *outbuf = cpu_to_le32(stats->rx_packets
-                               - stats->rx_errors - stats->rx_dropped);
-                       retval = 0;
-               }
-               break;
-
-       /* mandatory */
-       case RNDIS_OID_GEN_XMIT_ERROR:
-               if (rndis_debug > 1)
-                       pr_debug("%s: RNDIS_OID_GEN_XMIT_ERROR\n", __func__);
-               if (stats) {
-                       *outbuf = cpu_to_le32(stats->tx_errors);
-                       retval = 0;
-               }
-               break;
-
-       /* mandatory */
-       case RNDIS_OID_GEN_RCV_ERROR:
-               if (rndis_debug > 1)
-                       pr_debug("%s: RNDIS_OID_GEN_RCV_ERROR\n", __func__);
-               if (stats) {
-                       *outbuf = cpu_to_le32(stats->rx_errors);
-                       retval = 0;
-               }
-               break;
-
-       /* mandatory */
-       case RNDIS_OID_GEN_RCV_NO_BUFFER:
-               pr_debug("%s: RNDIS_OID_GEN_RCV_NO_BUFFER\n", __func__);
-               if (stats) {
-                       *outbuf = cpu_to_le32(stats->rx_dropped);
-                       retval = 0;
-               }
-               break;
-
-       /* ieee802.3 OIDs (table 4-3) */
-
-       /* mandatory */
-       case RNDIS_OID_802_3_PERMANENT_ADDRESS:
-               pr_debug("%s: RNDIS_OID_802_3_PERMANENT_ADDRESS\n", __func__);
-               if (rndis_per_dev_params[configNr].dev) {
-                       length = ETH_ALEN;
-                       memcpy(outbuf,
-                               rndis_per_dev_params[configNr].host_mac,
-                               length);
-                       retval = 0;
-               }
-               break;
-
-       /* mandatory */
-       case RNDIS_OID_802_3_CURRENT_ADDRESS:
-               pr_debug("%s: RNDIS_OID_802_3_CURRENT_ADDRESS\n", __func__);
-               if (rndis_per_dev_params[configNr].dev) {
-                       length = ETH_ALEN;
-                       memcpy(outbuf,
-                               rndis_per_dev_params [configNr].host_mac,
-                               length);
-                       retval = 0;
-               }
-               break;
-
-       /* mandatory */
-       case RNDIS_OID_802_3_MULTICAST_LIST:
-               pr_debug("%s: RNDIS_OID_802_3_MULTICAST_LIST\n", __func__);
-               /* Multicast base address only */
-               *outbuf = cpu_to_le32(0xE0000000);
-               retval = 0;
-               break;
-
-       /* mandatory */
-       case RNDIS_OID_802_3_MAXIMUM_LIST_SIZE:
-               pr_debug("%s: RNDIS_OID_802_3_MAXIMUM_LIST_SIZE\n", __func__);
-               /* Multicast base address only */
-               *outbuf = cpu_to_le32(1);
-               retval = 0;
-               break;
-
-       case RNDIS_OID_802_3_MAC_OPTIONS:
-               pr_debug("%s: RNDIS_OID_802_3_MAC_OPTIONS\n", __func__);
-               *outbuf = cpu_to_le32(0);
-               retval = 0;
-               break;
-
-       /* ieee802.3 statistics OIDs (table 4-4) */
-
-       /* mandatory */
-       case RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT:
-               pr_debug("%s: RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT\n", __func__);
-               if (stats) {
-                       *outbuf = cpu_to_le32(stats->rx_frame_errors);
-                       retval = 0;
-               }
-               break;
-
-       /* mandatory */
-       case RNDIS_OID_802_3_XMIT_ONE_COLLISION:
-               pr_debug("%s: RNDIS_OID_802_3_XMIT_ONE_COLLISION\n", __func__);
-               *outbuf = cpu_to_le32(0);
-               retval = 0;
-               break;
-
-       /* mandatory */
-       case RNDIS_OID_802_3_XMIT_MORE_COLLISIONS:
-               pr_debug("%s: RNDIS_OID_802_3_XMIT_MORE_COLLISIONS\n", __func__);
-               *outbuf = cpu_to_le32(0);
-               retval = 0;
-               break;
-
-       default:
-               pr_warning("%s: query unknown OID 0x%08X\n",
-                        __func__, OID);
-       }
-       if (retval < 0)
-               length = 0;
-
-       resp->InformationBufferLength = cpu_to_le32(length);
-       r->length = length + sizeof(*resp);
-       resp->MessageLength = cpu_to_le32(r->length);
-       return retval;
-}
-
-static int gen_ndis_set_resp(u8 configNr, u32 OID, u8 *buf, u32 buf_len,
-                            rndis_resp_t *r)
-{
-       rndis_set_cmplt_type *resp;
-       int i, retval = -ENOTSUPP;
-       struct rndis_params *params;
-
-       if (!r)
-               return -ENOMEM;
-       resp = (rndis_set_cmplt_type *)r->buf;
-       if (!resp)
-               return -ENOMEM;
-
-       if (buf_len && rndis_debug > 1) {
-               pr_debug("set OID %08x value, len %d:\n", OID, buf_len);
-               for (i = 0; i < buf_len; i += 16) {
-                       pr_debug("%03d: %08x %08x %08x %08x\n", i,
-                               get_unaligned_le32(&buf[i]),
-                               get_unaligned_le32(&buf[i + 4]),
-                               get_unaligned_le32(&buf[i + 8]),
-                               get_unaligned_le32(&buf[i + 12]));
-               }
-       }
-
-       params = &rndis_per_dev_params[configNr];
-       switch (OID) {
-       case RNDIS_OID_GEN_CURRENT_PACKET_FILTER:
-
-               /* these NDIS_PACKET_TYPE_* bitflags are shared with
-                * cdc_filter; it's not RNDIS-specific
-                * NDIS_PACKET_TYPE_x == USB_CDC_PACKET_TYPE_x for x in:
-                *      PROMISCUOUS, DIRECTED,
-                *      MULTICAST, ALL_MULTICAST, BROADCAST
-                */
-               *params->filter = (u16)get_unaligned_le32(buf);
-               pr_debug("%s: RNDIS_OID_GEN_CURRENT_PACKET_FILTER %08x\n",
-                       __func__, *params->filter);
-
-               /* this call has a significant side effect:  it's
-                * what makes the packet flow start and stop, like
-                * activating the CDC Ethernet altsetting.
-                */
-               retval = 0;
-               if (*params->filter) {
-                       params->state = RNDIS_DATA_INITIALIZED;
-                       netif_carrier_on(params->dev);
-                       if (netif_running(params->dev))
-                               netif_wake_queue(params->dev);
-               } else {
-                       params->state = RNDIS_INITIALIZED;
-                       netif_carrier_off(params->dev);
-                       netif_stop_queue(params->dev);
-               }
-               break;
-
-       case RNDIS_OID_802_3_MULTICAST_LIST:
-               /* I think we can ignore this */
-               pr_debug("%s: RNDIS_OID_802_3_MULTICAST_LIST\n", __func__);
-               retval = 0;
-               break;
-
-       default:
-               pr_warning("%s: set unknown OID 0x%08X, size %d\n",
-                        __func__, OID, buf_len);
-       }
-
-       return retval;
-}
-
-/*
- * Response Functions
- */
-
-static int rndis_init_response(int configNr, rndis_init_msg_type *buf)
-{
-       rndis_init_cmplt_type *resp;
-       rndis_resp_t *r;
-       struct rndis_params *params = rndis_per_dev_params + configNr;
-
-       if (!params->dev)
-               return -ENOTSUPP;
-
-       r = rndis_add_response(configNr, sizeof(rndis_init_cmplt_type));
-       if (!r)
-               return -ENOMEM;
-       resp = (rndis_init_cmplt_type *)r->buf;
-
-       resp->MessageType = cpu_to_le32(RNDIS_MSG_INIT_C);
-       resp->MessageLength = cpu_to_le32(52);
-       resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
-       resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
-       resp->MajorVersion = cpu_to_le32(RNDIS_MAJOR_VERSION);
-       resp->MinorVersion = cpu_to_le32(RNDIS_MINOR_VERSION);
-       resp->DeviceFlags = cpu_to_le32(RNDIS_DF_CONNECTIONLESS);
-       resp->Medium = cpu_to_le32(RNDIS_MEDIUM_802_3);
-       resp->MaxPacketsPerTransfer = cpu_to_le32(1);
-       resp->MaxTransferSize = cpu_to_le32(
-                 params->dev->mtu
-               + sizeof(struct ethhdr)
-               + sizeof(struct rndis_packet_msg_type)
-               + 22);
-       resp->PacketAlignmentFactor = cpu_to_le32(0);
-       resp->AFListOffset = cpu_to_le32(0);
-       resp->AFListSize = cpu_to_le32(0);
-
-       params->resp_avail(params->v);
-       return 0;
-}
-
-static int rndis_query_response(int configNr, rndis_query_msg_type *buf)
-{
-       rndis_query_cmplt_type *resp;
-       rndis_resp_t *r;
-       struct rndis_params *params = rndis_per_dev_params + configNr;
-
-       /* pr_debug("%s: OID = %08X\n", __func__, cpu_to_le32(buf->OID)); */
-       if (!params->dev)
-               return -ENOTSUPP;
-
-       /*
-        * we need more memory:
-        * gen_ndis_query_resp expects enough space for
-        * rndis_query_cmplt_type followed by data.
-        * oid_supported_list is the largest data reply
-        */
-       r = rndis_add_response(configNr,
-               sizeof(oid_supported_list) + sizeof(rndis_query_cmplt_type));
-       if (!r)
-               return -ENOMEM;
-       resp = (rndis_query_cmplt_type *)r->buf;
-
-       resp->MessageType = cpu_to_le32(RNDIS_MSG_QUERY_C);
-       resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
-
-       if (gen_ndis_query_resp(configNr, le32_to_cpu(buf->OID),
-                       le32_to_cpu(buf->InformationBufferOffset)
-                                       + 8 + (u8 *)buf,
-                       le32_to_cpu(buf->InformationBufferLength),
-                       r)) {
-               /* OID not supported */
-               resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED);
-               resp->MessageLength = cpu_to_le32(sizeof *resp);
-               resp->InformationBufferLength = cpu_to_le32(0);
-               resp->InformationBufferOffset = cpu_to_le32(0);
-       } else
-               resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
-
-       params->resp_avail(params->v);
-       return 0;
-}
-
-static int rndis_set_response(int configNr, rndis_set_msg_type *buf)
-{
-       u32 BufLength, BufOffset;
-       rndis_set_cmplt_type *resp;
-       rndis_resp_t *r;
-       struct rndis_params *params = rndis_per_dev_params + configNr;
-
-       r = rndis_add_response(configNr, sizeof(rndis_set_cmplt_type));
-       if (!r)
-               return -ENOMEM;
-       resp = (rndis_set_cmplt_type *)r->buf;
-
-       BufLength = le32_to_cpu(buf->InformationBufferLength);
-       BufOffset = le32_to_cpu(buf->InformationBufferOffset);
-
-#ifdef VERBOSE_DEBUG
-       pr_debug("%s: Length: %d\n", __func__, BufLength);
-       pr_debug("%s: Offset: %d\n", __func__, BufOffset);
-       pr_debug("%s: InfoBuffer: ", __func__);
-
-       for (i = 0; i < BufLength; i++) {
-               pr_debug("%02x ", *(((u8 *) buf) + i + 8 + BufOffset));
-       }
-
-       pr_debug("\n");
-#endif
-
-       resp->MessageType = cpu_to_le32(RNDIS_MSG_SET_C);
-       resp->MessageLength = cpu_to_le32(16);
-       resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
-       if (gen_ndis_set_resp(configNr, le32_to_cpu(buf->OID),
-                       ((u8 *)buf) + 8 + BufOffset, BufLength, r))
-               resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED);
-       else
-               resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
-
-       params->resp_avail(params->v);
-       return 0;
-}
-
-static int rndis_reset_response(int configNr, rndis_reset_msg_type *buf)
-{
-       rndis_reset_cmplt_type *resp;
-       rndis_resp_t *r;
-       struct rndis_params *params = rndis_per_dev_params + configNr;
-
-       r = rndis_add_response(configNr, sizeof(rndis_reset_cmplt_type));
-       if (!r)
-               return -ENOMEM;
-       resp = (rndis_reset_cmplt_type *)r->buf;
-
-       resp->MessageType = cpu_to_le32(RNDIS_MSG_RESET_C);
-       resp->MessageLength = cpu_to_le32(16);
-       resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
-       /* resent information */
-       resp->AddressingReset = cpu_to_le32(1);
-
-       params->resp_avail(params->v);
-       return 0;
-}
-
-static int rndis_keepalive_response(int configNr,
-                                   rndis_keepalive_msg_type *buf)
-{
-       rndis_keepalive_cmplt_type *resp;
-       rndis_resp_t *r;
-       struct rndis_params *params = rndis_per_dev_params + configNr;
-
-       /* host "should" check only in RNDIS_DATA_INITIALIZED state */
-
-       r = rndis_add_response(configNr, sizeof(rndis_keepalive_cmplt_type));
-       if (!r)
-               return -ENOMEM;
-       resp = (rndis_keepalive_cmplt_type *)r->buf;
-
-       resp->MessageType = cpu_to_le32(RNDIS_MSG_KEEPALIVE_C);
-       resp->MessageLength = cpu_to_le32(16);
-       resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
-       resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
-
-       params->resp_avail(params->v);
-       return 0;
-}
-
-
-/*
- * Device to Host Comunication
- */
-static int rndis_indicate_status_msg(int configNr, u32 status)
-{
-       rndis_indicate_status_msg_type *resp;
-       rndis_resp_t *r;
-       struct rndis_params *params = rndis_per_dev_params + configNr;
-
-       if (params->state == RNDIS_UNINITIALIZED)
-               return -ENOTSUPP;
-
-       r = rndis_add_response(configNr,
-                               sizeof(rndis_indicate_status_msg_type));
-       if (!r)
-               return -ENOMEM;
-       resp = (rndis_indicate_status_msg_type *)r->buf;
-
-       resp->MessageType = cpu_to_le32(RNDIS_MSG_INDICATE);
-       resp->MessageLength = cpu_to_le32(20);
-       resp->Status = cpu_to_le32(status);
-       resp->StatusBufferLength = cpu_to_le32(0);
-       resp->StatusBufferOffset = cpu_to_le32(0);
-
-       params->resp_avail(params->v);
-       return 0;
-}
-
-int rndis_signal_connect(int configNr)
-{
-       rndis_per_dev_params[configNr].media_state
-                       = RNDIS_MEDIA_STATE_CONNECTED;
-       return rndis_indicate_status_msg(configNr,
-                                         RNDIS_STATUS_MEDIA_CONNECT);
-}
-EXPORT_SYMBOL_GPL(rndis_signal_connect);
-
-int rndis_signal_disconnect(int configNr)
-{
-       rndis_per_dev_params[configNr].media_state
-                       = RNDIS_MEDIA_STATE_DISCONNECTED;
-       return rndis_indicate_status_msg(configNr,
-                                         RNDIS_STATUS_MEDIA_DISCONNECT);
-}
-EXPORT_SYMBOL_GPL(rndis_signal_disconnect);
-
-void rndis_uninit(int configNr)
-{
-       u8 *buf;
-       u32 length;
-
-       if (configNr >= RNDIS_MAX_CONFIGS)
-               return;
-       rndis_per_dev_params[configNr].state = RNDIS_UNINITIALIZED;
-
-       /* drain the response queue */
-       while ((buf = rndis_get_next_response(configNr, &length)))
-               rndis_free_response(configNr, buf);
-}
-EXPORT_SYMBOL_GPL(rndis_uninit);
-
-void rndis_set_host_mac(int configNr, const u8 *addr)
-{
-       rndis_per_dev_params[configNr].host_mac = addr;
-}
-EXPORT_SYMBOL_GPL(rndis_set_host_mac);
-
-/*
- * Message Parser
- */
-int rndis_msg_parser(u8 configNr, u8 *buf)
-{
-       u32 MsgType, MsgLength;
-       __le32 *tmp;
-       struct rndis_params *params;
-
-       if (!buf)
-               return -ENOMEM;
-
-       tmp = (__le32 *)buf;
-       MsgType   = get_unaligned_le32(tmp++);
-       MsgLength = get_unaligned_le32(tmp++);
-
-       if (configNr >= RNDIS_MAX_CONFIGS)
-               return -ENOTSUPP;
-       params = &rndis_per_dev_params[configNr];
-
-       /* NOTE: RNDIS is *EXTREMELY* chatty ... Windows constantly polls for
-        * rx/tx statistics and link status, in addition to KEEPALIVE traffic
-        * and normal HC level polling to see if there's any IN traffic.
-        */
-
-       /* For USB: responses may take up to 10 seconds */
-       switch (MsgType) {
-       case RNDIS_MSG_INIT:
-               pr_debug("%s: RNDIS_MSG_INIT\n",
-                       __func__);
-               params->state = RNDIS_INITIALIZED;
-               return rndis_init_response(configNr,
-                                       (rndis_init_msg_type *)buf);
-
-       case RNDIS_MSG_HALT:
-               pr_debug("%s: RNDIS_MSG_HALT\n",
-                       __func__);
-               params->state = RNDIS_UNINITIALIZED;
-               if (params->dev) {
-                       netif_carrier_off(params->dev);
-                       netif_stop_queue(params->dev);
-               }
-               return 0;
-
-       case RNDIS_MSG_QUERY:
-               return rndis_query_response(configNr,
-                                       (rndis_query_msg_type *)buf);
-
-       case RNDIS_MSG_SET:
-               return rndis_set_response(configNr,
-                                       (rndis_set_msg_type *)buf);
-
-       case RNDIS_MSG_RESET:
-               pr_debug("%s: RNDIS_MSG_RESET\n",
-                       __func__);
-               return rndis_reset_response(configNr,
-                                       (rndis_reset_msg_type *)buf);
-
-       case RNDIS_MSG_KEEPALIVE:
-               /* For USB: host does this every 5 seconds */
-               if (rndis_debug > 1)
-                       pr_debug("%s: RNDIS_MSG_KEEPALIVE\n",
-                               __func__);
-               return rndis_keepalive_response(configNr,
-                                                (rndis_keepalive_msg_type *)
-                                                buf);
-
-       default:
-               /* At least Windows XP emits some undefined RNDIS messages.
-                * In one case those messages seemed to relate to the host
-                * suspending itself.
-                */
-               pr_warning("%s: unknown RNDIS message 0x%08X len %d\n",
-                       __func__, MsgType, MsgLength);
-               print_hex_dump_bytes(__func__, DUMP_PREFIX_OFFSET,
-                                    buf, MsgLength);
-               break;
-       }
-
-       return -ENOTSUPP;
-}
-EXPORT_SYMBOL_GPL(rndis_msg_parser);
-
-int rndis_register(void (*resp_avail)(void *v), void *v)
-{
-       u8 i;
-
-       if (!resp_avail)
-               return -EINVAL;
-
-       for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
-               if (!rndis_per_dev_params[i].used) {
-                       rndis_per_dev_params[i].used = 1;
-                       rndis_per_dev_params[i].resp_avail = resp_avail;
-                       rndis_per_dev_params[i].v = v;
-                       pr_debug("%s: configNr = %d\n", __func__, i);
-                       return i;
-               }
-       }
-       pr_debug("failed\n");
-
-       return -ENODEV;
-}
-EXPORT_SYMBOL_GPL(rndis_register);
-
-void rndis_deregister(int configNr)
-{
-       pr_debug("%s:\n", __func__);
-
-       if (configNr >= RNDIS_MAX_CONFIGS) return;
-       rndis_per_dev_params[configNr].used = 0;
-}
-EXPORT_SYMBOL_GPL(rndis_deregister);
-
-int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter)
-{
-       pr_debug("%s:\n", __func__);
-       if (!dev)
-               return -EINVAL;
-       if (configNr >= RNDIS_MAX_CONFIGS) return -1;
-
-       rndis_per_dev_params[configNr].dev = dev;
-       rndis_per_dev_params[configNr].filter = cdc_filter;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(rndis_set_param_dev);
-
-int rndis_set_param_vendor(u8 configNr, u32 vendorID, const char *vendorDescr)
-{
-       pr_debug("%s:\n", __func__);
-       if (!vendorDescr) return -1;
-       if (configNr >= RNDIS_MAX_CONFIGS) return -1;
-
-       rndis_per_dev_params[configNr].vendorID = vendorID;
-       rndis_per_dev_params[configNr].vendorDescr = vendorDescr;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(rndis_set_param_vendor);
-
-int rndis_set_param_medium(u8 configNr, u32 medium, u32 speed)
-{
-       pr_debug("%s: %u %u\n", __func__, medium, speed);
-       if (configNr >= RNDIS_MAX_CONFIGS) return -1;
-
-       rndis_per_dev_params[configNr].medium = medium;
-       rndis_per_dev_params[configNr].speed = speed;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(rndis_set_param_medium);
-
-void rndis_add_hdr(struct sk_buff *skb)
-{
-       struct rndis_packet_msg_type *header;
-
-       if (!skb)
-               return;
-       header = (void *)skb_push(skb, sizeof(*header));
-       memset(header, 0, sizeof *header);
-       header->MessageType = cpu_to_le32(RNDIS_MSG_PACKET);
-       header->MessageLength = cpu_to_le32(skb->len);
-       header->DataOffset = cpu_to_le32(36);
-       header->DataLength = cpu_to_le32(skb->len - sizeof(*header));
-}
-EXPORT_SYMBOL_GPL(rndis_add_hdr);
-
-void rndis_free_response(int configNr, u8 *buf)
-{
-       rndis_resp_t *r;
-       struct list_head *act, *tmp;
-
-       list_for_each_safe(act, tmp,
-                       &(rndis_per_dev_params[configNr].resp_queue))
-       {
-               r = list_entry(act, rndis_resp_t, list);
-               if (r && r->buf == buf) {
-                       list_del(&r->list);
-                       kfree(r);
-               }
-       }
-}
-EXPORT_SYMBOL_GPL(rndis_free_response);
-
-u8 *rndis_get_next_response(int configNr, u32 *length)
-{
-       rndis_resp_t *r;
-       struct list_head *act, *tmp;
-
-       if (!length) return NULL;
-
-       list_for_each_safe(act, tmp,
-                       &(rndis_per_dev_params[configNr].resp_queue))
-       {
-               r = list_entry(act, rndis_resp_t, list);
-               if (!r->send) {
-                       r->send = 1;
-                       *length = r->length;
-                       return r->buf;
-               }
-       }
-
-       return NULL;
-}
-EXPORT_SYMBOL_GPL(rndis_get_next_response);
-
-static rndis_resp_t *rndis_add_response(int configNr, u32 length)
-{
-       rndis_resp_t *r;
-
-       /* NOTE: this gets copied into ether.c USB_BUFSIZ bytes ... */
-       r = kmalloc(sizeof(rndis_resp_t) + length, GFP_ATOMIC);
-       if (!r) return NULL;
-
-       r->buf = (u8 *)(r + 1);
-       r->length = length;
-       r->send = 0;
-
-       list_add_tail(&r->list,
-               &(rndis_per_dev_params[configNr].resp_queue));
-       return r;
-}
-
-int rndis_rm_hdr(struct gether *port,
-                       struct sk_buff *skb,
-                       struct sk_buff_head *list)
-{
-       /* tmp points to a struct rndis_packet_msg_type */
-       __le32 *tmp = (void *)skb->data;
-
-       /* MessageType, MessageLength */
-       if (cpu_to_le32(RNDIS_MSG_PACKET)
-                       != get_unaligned(tmp++)) {
-               dev_kfree_skb_any(skb);
-               return -EINVAL;
-       }
-       tmp++;
-
-       /* DataOffset, DataLength */
-       if (!skb_pull(skb, get_unaligned_le32(tmp++) + 8)) {
-               dev_kfree_skb_any(skb);
-               return -EOVERFLOW;
-       }
-       skb_trim(skb, get_unaligned_le32(tmp++));
-
-       skb_queue_tail(list, skb);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(rndis_rm_hdr);
-
-#ifdef CONFIG_USB_GADGET_DEBUG_FILES
-
-static int rndis_proc_show(struct seq_file *m, void *v)
-{
-       rndis_params *param = m->private;
-
-       seq_printf(m,
-                        "Config Nr. %d\n"
-                        "used      : %s\n"
-                        "state     : %s\n"
-                        "medium    : 0x%08X\n"
-                        "speed     : %d\n"
-                        "cable     : %s\n"
-                        "vendor ID : 0x%08X\n"
-                        "vendor    : %s\n",
-                        param->confignr, (param->used) ? "y" : "n",
-                        ({ char *s = "?";
-                        switch (param->state) {
-                        case RNDIS_UNINITIALIZED:
-                               s = "RNDIS_UNINITIALIZED"; break;
-                        case RNDIS_INITIALIZED:
-                               s = "RNDIS_INITIALIZED"; break;
-                        case RNDIS_DATA_INITIALIZED:
-                               s = "RNDIS_DATA_INITIALIZED"; break;
-                       } s; }),
-                        param->medium,
-                        (param->media_state) ? 0 : param->speed*100,
-                        (param->media_state) ? "disconnected" : "connected",
-                        param->vendorID, param->vendorDescr);
-       return 0;
-}
-
-static ssize_t rndis_proc_write(struct file *file, const char __user *buffer,
-                               size_t count, loff_t *ppos)
-{
-       rndis_params *p = PDE_DATA(file_inode(file));
-       u32 speed = 0;
-       int i, fl_speed = 0;
-
-       for (i = 0; i < count; i++) {
-               char c;
-               if (get_user(c, buffer))
-                       return -EFAULT;
-               switch (c) {
-               case '0':
-               case '1':
-               case '2':
-               case '3':
-               case '4':
-               case '5':
-               case '6':
-               case '7':
-               case '8':
-               case '9':
-                       fl_speed = 1;
-                       speed = speed * 10 + c - '0';
-                       break;
-               case 'C':
-               case 'c':
-                       rndis_signal_connect(p->confignr);
-                       break;
-               case 'D':
-               case 'd':
-                       rndis_signal_disconnect(p->confignr);
-                       break;
-               default:
-                       if (fl_speed) p->speed = speed;
-                       else pr_debug("%c is not valid\n", c);
-                       break;
-               }
-
-               buffer++;
-       }
-
-       return count;
-}
-
-static int rndis_proc_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, rndis_proc_show, PDE_DATA(inode));
-}
-
-static const struct file_operations rndis_proc_fops = {
-       .owner          = THIS_MODULE,
-       .open           = rndis_proc_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-       .write          = rndis_proc_write,
-};
-
-#define        NAME_TEMPLATE "driver/rndis-%03d"
-
-static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS];
-
-#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
-
-
-int rndis_init(void)
-{
-       u8 i;
-
-       for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
-#ifdef CONFIG_USB_GADGET_DEBUG_FILES
-               char name [20];
-
-               sprintf(name, NAME_TEMPLATE, i);
-               rndis_connect_state[i] = proc_create_data(name, 0660, NULL,
-                                       &rndis_proc_fops,
-                                       (void *)(rndis_per_dev_params + i));
-               if (!rndis_connect_state[i]) {
-                       pr_debug("%s: remove entries", __func__);
-                       while (i) {
-                               sprintf(name, NAME_TEMPLATE, --i);
-                               remove_proc_entry(name, NULL);
-                       }
-                       pr_debug("\n");
-                       return -EIO;
-               }
-#endif
-               rndis_per_dev_params[i].confignr = i;
-               rndis_per_dev_params[i].used = 0;
-               rndis_per_dev_params[i].state = RNDIS_UNINITIALIZED;
-               rndis_per_dev_params[i].media_state
-                               = RNDIS_MEDIA_STATE_DISCONNECTED;
-               INIT_LIST_HEAD(&(rndis_per_dev_params[i].resp_queue));
-       }
-
-       return 0;
-}
-
-void rndis_exit(void)
-{
-#ifdef CONFIG_USB_GADGET_DEBUG_FILES
-       u8 i;
-       char name[20];
-
-       for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
-               sprintf(name, NAME_TEMPLATE, i);
-               remove_proc_entry(name, NULL);
-       }
-#endif
-}
-
diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/rndis.h
deleted file mode 100644 (file)
index 0f4abb4..0000000
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * RNDIS       Definitions for Remote NDIS
- *
- * Authors:    Benedikt Spranger, Pengutronix
- *             Robert Schwebel, Pengutronix
- *
- *             This program is free software; you can redistribute it and/or
- *             modify it under the terms of the GNU General Public License
- *             version 2, as published by the Free Software Foundation.
- *
- *             This software was originally developed in conformance with
- *             Microsoft's Remote NDIS Specification License Agreement.
- */
-
-#ifndef _LINUX_RNDIS_H
-#define _LINUX_RNDIS_H
-
-#include <linux/rndis.h>
-#include "u_ether.h"
-#include "ndis.h"
-
-#define RNDIS_MAXIMUM_FRAME_SIZE       1518
-#define RNDIS_MAX_TOTAL_SIZE           1558
-
-typedef struct rndis_init_msg_type
-{
-       __le32  MessageType;
-       __le32  MessageLength;
-       __le32  RequestID;
-       __le32  MajorVersion;
-       __le32  MinorVersion;
-       __le32  MaxTransferSize;
-} rndis_init_msg_type;
-
-typedef struct rndis_init_cmplt_type
-{
-       __le32  MessageType;
-       __le32  MessageLength;
-       __le32  RequestID;
-       __le32  Status;
-       __le32  MajorVersion;
-       __le32  MinorVersion;
-       __le32  DeviceFlags;
-       __le32  Medium;
-       __le32  MaxPacketsPerTransfer;
-       __le32  MaxTransferSize;
-       __le32  PacketAlignmentFactor;
-       __le32  AFListOffset;
-       __le32  AFListSize;
-} rndis_init_cmplt_type;
-
-typedef struct rndis_halt_msg_type
-{
-       __le32  MessageType;
-       __le32  MessageLength;
-       __le32  RequestID;
-} rndis_halt_msg_type;
-
-typedef struct rndis_query_msg_type
-{
-       __le32  MessageType;
-       __le32  MessageLength;
-       __le32  RequestID;
-       __le32  OID;
-       __le32  InformationBufferLength;
-       __le32  InformationBufferOffset;
-       __le32  DeviceVcHandle;
-} rndis_query_msg_type;
-
-typedef struct rndis_query_cmplt_type
-{
-       __le32  MessageType;
-       __le32  MessageLength;
-       __le32  RequestID;
-       __le32  Status;
-       __le32  InformationBufferLength;
-       __le32  InformationBufferOffset;
-} rndis_query_cmplt_type;
-
-typedef struct rndis_set_msg_type
-{
-       __le32  MessageType;
-       __le32  MessageLength;
-       __le32  RequestID;
-       __le32  OID;
-       __le32  InformationBufferLength;
-       __le32  InformationBufferOffset;
-       __le32  DeviceVcHandle;
-} rndis_set_msg_type;
-
-typedef struct rndis_set_cmplt_type
-{
-       __le32  MessageType;
-       __le32  MessageLength;
-       __le32  RequestID;
-       __le32  Status;
-} rndis_set_cmplt_type;
-
-typedef struct rndis_reset_msg_type
-{
-       __le32  MessageType;
-       __le32  MessageLength;
-       __le32  Reserved;
-} rndis_reset_msg_type;
-
-typedef struct rndis_reset_cmplt_type
-{
-       __le32  MessageType;
-       __le32  MessageLength;
-       __le32  Status;
-       __le32  AddressingReset;
-} rndis_reset_cmplt_type;
-
-typedef struct rndis_indicate_status_msg_type
-{
-       __le32  MessageType;
-       __le32  MessageLength;
-       __le32  Status;
-       __le32  StatusBufferLength;
-       __le32  StatusBufferOffset;
-} rndis_indicate_status_msg_type;
-
-typedef struct rndis_keepalive_msg_type
-{
-       __le32  MessageType;
-       __le32  MessageLength;
-       __le32  RequestID;
-} rndis_keepalive_msg_type;
-
-typedef struct rndis_keepalive_cmplt_type
-{
-       __le32  MessageType;
-       __le32  MessageLength;
-       __le32  RequestID;
-       __le32  Status;
-} rndis_keepalive_cmplt_type;
-
-struct rndis_packet_msg_type
-{
-       __le32  MessageType;
-       __le32  MessageLength;
-       __le32  DataOffset;
-       __le32  DataLength;
-       __le32  OOBDataOffset;
-       __le32  OOBDataLength;
-       __le32  NumOOBDataElements;
-       __le32  PerPacketInfoOffset;
-       __le32  PerPacketInfoLength;
-       __le32  VcHandle;
-       __le32  Reserved;
-} __attribute__ ((packed));
-
-struct rndis_config_parameter
-{
-       __le32  ParameterNameOffset;
-       __le32  ParameterNameLength;
-       __le32  ParameterType;
-       __le32  ParameterValueOffset;
-       __le32  ParameterValueLength;
-};
-
-/* implementation specific */
-enum rndis_state
-{
-       RNDIS_UNINITIALIZED,
-       RNDIS_INITIALIZED,
-       RNDIS_DATA_INITIALIZED,
-};
-
-typedef struct rndis_resp_t
-{
-       struct list_head        list;
-       u8                      *buf;
-       u32                     length;
-       int                     send;
-} rndis_resp_t;
-
-typedef struct rndis_params
-{
-       u8                      confignr;
-       u8                      used;
-       u16                     saved_filter;
-       enum rndis_state        state;
-       u32                     medium;
-       u32                     speed;
-       u32                     media_state;
-
-       const u8                *host_mac;
-       u16                     *filter;
-       struct net_device       *dev;
-
-       u32                     vendorID;
-       const char              *vendorDescr;
-       void                    (*resp_avail)(void *v);
-       void                    *v;
-       struct list_head        resp_queue;
-} rndis_params;
-
-/* RNDIS Message parser and other useless functions */
-int  rndis_msg_parser (u8 configNr, u8 *buf);
-int  rndis_register(void (*resp_avail)(void *v), void *v);
-void rndis_deregister (int configNr);
-int  rndis_set_param_dev (u8 configNr, struct net_device *dev,
-                        u16 *cdc_filter);
-int  rndis_set_param_vendor (u8 configNr, u32 vendorID,
-                           const char *vendorDescr);
-int  rndis_set_param_medium (u8 configNr, u32 medium, u32 speed);
-void rndis_add_hdr (struct sk_buff *skb);
-int rndis_rm_hdr(struct gether *port, struct sk_buff *skb,
-                       struct sk_buff_head *list);
-u8   *rndis_get_next_response (int configNr, u32 *length);
-void rndis_free_response (int configNr, u8 *buf);
-
-void rndis_uninit (int configNr);
-int  rndis_signal_connect (int configNr);
-int  rndis_signal_disconnect (int configNr);
-int  rndis_state (int configNr);
-extern void rndis_set_host_mac (int configNr, const u8 *addr);
-
-#endif  /* _LINUX_RNDIS_H */
diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c
deleted file mode 100644 (file)
index 648f9e4..0000000
+++ /dev/null
@@ -1,504 +0,0 @@
-/*
- * storage_common.c -- Common definitions for mass storage functionality
- *
- * Copyright (C) 2003-2008 Alan Stern
- * Copyeight (C) 2009 Samsung Electronics
- * Author: Michal Nazarewicz (mina86@mina86.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-/*
- * This file requires the following identifiers used in USB strings to
- * be defined (each of type pointer to char):
- *  - fsg_string_interface    -- name of the interface
- */
-
-/*
- * When USB_GADGET_DEBUG_FILES is defined the module param num_buffers
- * sets the number of pipeline buffers (length of the fsg_buffhd array).
- * The valid range of num_buffers is: num >= 2 && num <= 4.
- */
-
-#include <linux/module.h>
-#include <linux/blkdev.h>
-#include <linux/file.h>
-#include <linux/fs.h>
-#include <linux/usb/composite.h>
-
-#include "storage_common.h"
-
-/* There is only one interface. */
-
-struct usb_interface_descriptor fsg_intf_desc = {
-       .bLength =              sizeof fsg_intf_desc,
-       .bDescriptorType =      USB_DT_INTERFACE,
-
-       .bNumEndpoints =        2,              /* Adjusted during fsg_bind() */
-       .bInterfaceClass =      USB_CLASS_MASS_STORAGE,
-       .bInterfaceSubClass =   USB_SC_SCSI,    /* Adjusted during fsg_bind() */
-       .bInterfaceProtocol =   USB_PR_BULK,    /* Adjusted during fsg_bind() */
-       .iInterface =           FSG_STRING_INTERFACE,
-};
-EXPORT_SYMBOL_GPL(fsg_intf_desc);
-
-/*
- * Three full-speed endpoint descriptors: bulk-in, bulk-out, and
- * interrupt-in.
- */
-
-struct usb_endpoint_descriptor fsg_fs_bulk_in_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       /* wMaxPacketSize set by autoconfiguration */
-};
-EXPORT_SYMBOL_GPL(fsg_fs_bulk_in_desc);
-
-struct usb_endpoint_descriptor fsg_fs_bulk_out_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_OUT,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       /* wMaxPacketSize set by autoconfiguration */
-};
-EXPORT_SYMBOL_GPL(fsg_fs_bulk_out_desc);
-
-struct usb_descriptor_header *fsg_fs_function[] = {
-       (struct usb_descriptor_header *) &fsg_intf_desc,
-       (struct usb_descriptor_header *) &fsg_fs_bulk_in_desc,
-       (struct usb_descriptor_header *) &fsg_fs_bulk_out_desc,
-       NULL,
-};
-EXPORT_SYMBOL_GPL(fsg_fs_function);
-
-
-/*
- * USB 2.0 devices need to expose both high speed and full speed
- * descriptors, unless they only run at full speed.
- *
- * That means alternate endpoint descriptors (bigger packets)
- * and a "device qualifier" ... plus more construction options
- * for the configuration descriptor.
- */
-struct usb_endpoint_descriptor fsg_hs_bulk_in_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(512),
-};
-EXPORT_SYMBOL_GPL(fsg_hs_bulk_in_desc);
-
-struct usb_endpoint_descriptor fsg_hs_bulk_out_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(512),
-       .bInterval =            1,      /* NAK every 1 uframe */
-};
-EXPORT_SYMBOL_GPL(fsg_hs_bulk_out_desc);
-
-
-struct usb_descriptor_header *fsg_hs_function[] = {
-       (struct usb_descriptor_header *) &fsg_intf_desc,
-       (struct usb_descriptor_header *) &fsg_hs_bulk_in_desc,
-       (struct usb_descriptor_header *) &fsg_hs_bulk_out_desc,
-       NULL,
-};
-EXPORT_SYMBOL_GPL(fsg_hs_function);
-
-struct usb_endpoint_descriptor fsg_ss_bulk_in_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(1024),
-};
-EXPORT_SYMBOL_GPL(fsg_ss_bulk_in_desc);
-
-struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = {
-       .bLength =              sizeof(fsg_ss_bulk_in_comp_desc),
-       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
-
-       /*.bMaxBurst =          DYNAMIC, */
-};
-EXPORT_SYMBOL_GPL(fsg_ss_bulk_in_comp_desc);
-
-struct usb_endpoint_descriptor fsg_ss_bulk_out_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(1024),
-};
-EXPORT_SYMBOL_GPL(fsg_ss_bulk_out_desc);
-
-struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = {
-       .bLength =              sizeof(fsg_ss_bulk_in_comp_desc),
-       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
-
-       /*.bMaxBurst =          DYNAMIC, */
-};
-EXPORT_SYMBOL_GPL(fsg_ss_bulk_out_comp_desc);
-
-struct usb_descriptor_header *fsg_ss_function[] = {
-       (struct usb_descriptor_header *) &fsg_intf_desc,
-       (struct usb_descriptor_header *) &fsg_ss_bulk_in_desc,
-       (struct usb_descriptor_header *) &fsg_ss_bulk_in_comp_desc,
-       (struct usb_descriptor_header *) &fsg_ss_bulk_out_desc,
-       (struct usb_descriptor_header *) &fsg_ss_bulk_out_comp_desc,
-       NULL,
-};
-EXPORT_SYMBOL_GPL(fsg_ss_function);
-
-
- /*-------------------------------------------------------------------------*/
-
-/*
- * If the next two routines are called while the gadget is registered,
- * the caller must own fsg->filesem for writing.
- */
-
-void fsg_lun_close(struct fsg_lun *curlun)
-{
-       if (curlun->filp) {
-               LDBG(curlun, "close backing file\n");
-               fput(curlun->filp);
-               curlun->filp = NULL;
-       }
-}
-EXPORT_SYMBOL_GPL(fsg_lun_close);
-
-int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
-{
-       int                             ro;
-       struct file                     *filp = NULL;
-       int                             rc = -EINVAL;
-       struct inode                    *inode = NULL;
-       loff_t                          size;
-       loff_t                          num_sectors;
-       loff_t                          min_sectors;
-       unsigned int                    blkbits;
-       unsigned int                    blksize;
-
-       /* R/W if we can, R/O if we must */
-       ro = curlun->initially_ro;
-       if (!ro) {
-               filp = filp_open(filename, O_RDWR | O_LARGEFILE, 0);
-               if (PTR_ERR(filp) == -EROFS || PTR_ERR(filp) == -EACCES)
-                       ro = 1;
-       }
-       if (ro)
-               filp = filp_open(filename, O_RDONLY | O_LARGEFILE, 0);
-       if (IS_ERR(filp)) {
-               LINFO(curlun, "unable to open backing file: %s\n", filename);
-               return PTR_ERR(filp);
-       }
-
-       if (!(filp->f_mode & FMODE_WRITE))
-               ro = 1;
-
-       inode = file_inode(filp);
-       if ((!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))) {
-               LINFO(curlun, "invalid file type: %s\n", filename);
-               goto out;
-       }
-
-       /*
-        * If we can't read the file, it's no good.
-        * If we can't write the file, use it read-only.
-        */
-       if (!(filp->f_mode & FMODE_CAN_READ)) {
-               LINFO(curlun, "file not readable: %s\n", filename);
-               goto out;
-       }
-       if (!(filp->f_mode & FMODE_CAN_WRITE))
-               ro = 1;
-
-       size = i_size_read(inode->i_mapping->host);
-       if (size < 0) {
-               LINFO(curlun, "unable to find file size: %s\n", filename);
-               rc = (int) size;
-               goto out;
-       }
-
-       if (curlun->cdrom) {
-               blksize = 2048;
-               blkbits = 11;
-       } else if (inode->i_bdev) {
-               blksize = bdev_logical_block_size(inode->i_bdev);
-               blkbits = blksize_bits(blksize);
-       } else {
-               blksize = 512;
-               blkbits = 9;
-       }
-
-       num_sectors = size >> blkbits; /* File size in logic-block-size blocks */
-       min_sectors = 1;
-       if (curlun->cdrom) {
-               min_sectors = 300;      /* Smallest track is 300 frames */
-               if (num_sectors >= 256*60*75) {
-                       num_sectors = 256*60*75 - 1;
-                       LINFO(curlun, "file too big: %s\n", filename);
-                       LINFO(curlun, "using only first %d blocks\n",
-                                       (int) num_sectors);
-               }
-       }
-       if (num_sectors < min_sectors) {
-               LINFO(curlun, "file too small: %s\n", filename);
-               rc = -ETOOSMALL;
-               goto out;
-       }
-
-       if (fsg_lun_is_open(curlun))
-               fsg_lun_close(curlun);
-
-       curlun->blksize = blksize;
-       curlun->blkbits = blkbits;
-       curlun->ro = ro;
-       curlun->filp = filp;
-       curlun->file_length = size;
-       curlun->num_sectors = num_sectors;
-       LDBG(curlun, "open backing file: %s\n", filename);
-       return 0;
-
-out:
-       fput(filp);
-       return rc;
-}
-EXPORT_SYMBOL_GPL(fsg_lun_open);
-
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * Sync the file data, don't bother with the metadata.
- * This code was copied from fs/buffer.c:sys_fdatasync().
- */
-int fsg_lun_fsync_sub(struct fsg_lun *curlun)
-{
-       struct file     *filp = curlun->filp;
-
-       if (curlun->ro || !filp)
-               return 0;
-       return vfs_fsync(filp, 1);
-}
-EXPORT_SYMBOL_GPL(fsg_lun_fsync_sub);
-
-void store_cdrom_address(u8 *dest, int msf, u32 addr)
-{
-       if (msf) {
-               /* Convert to Minutes-Seconds-Frames */
-               addr >>= 2;             /* Convert to 2048-byte frames */
-               addr += 2*75;           /* Lead-in occupies 2 seconds */
-               dest[3] = addr % 75;    /* Frames */
-               addr /= 75;
-               dest[2] = addr % 60;    /* Seconds */
-               addr /= 60;
-               dest[1] = addr;         /* Minutes */
-               dest[0] = 0;            /* Reserved */
-       } else {
-               /* Absolute sector */
-               put_unaligned_be32(addr, dest);
-       }
-}
-EXPORT_SYMBOL_GPL(store_cdrom_address);
-
-/*-------------------------------------------------------------------------*/
-
-
-ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf)
-{
-       return sprintf(buf, "%d\n", fsg_lun_is_open(curlun)
-                                 ? curlun->ro
-                                 : curlun->initially_ro);
-}
-EXPORT_SYMBOL_GPL(fsg_show_ro);
-
-ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf)
-{
-       return sprintf(buf, "%u\n", curlun->nofua);
-}
-EXPORT_SYMBOL_GPL(fsg_show_nofua);
-
-ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
-                     char *buf)
-{
-       char            *p;
-       ssize_t         rc;
-
-       down_read(filesem);
-       if (fsg_lun_is_open(curlun)) {  /* Get the complete pathname */
-               p = d_path(&curlun->filp->f_path, buf, PAGE_SIZE - 1);
-               if (IS_ERR(p))
-                       rc = PTR_ERR(p);
-               else {
-                       rc = strlen(p);
-                       memmove(buf, p, rc);
-                       buf[rc] = '\n';         /* Add a newline */
-                       buf[++rc] = 0;
-               }
-       } else {                                /* No file, return 0 bytes */
-               *buf = 0;
-               rc = 0;
-       }
-       up_read(filesem);
-       return rc;
-}
-EXPORT_SYMBOL_GPL(fsg_show_file);
-
-ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf)
-{
-       return sprintf(buf, "%u\n", curlun->cdrom);
-}
-EXPORT_SYMBOL_GPL(fsg_show_cdrom);
-
-ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf)
-{
-       return sprintf(buf, "%u\n", curlun->removable);
-}
-EXPORT_SYMBOL_GPL(fsg_show_removable);
-
-/*
- * The caller must hold fsg->filesem for reading when calling this function.
- */
-static ssize_t _fsg_store_ro(struct fsg_lun *curlun, bool ro)
-{
-       if (fsg_lun_is_open(curlun)) {
-               LDBG(curlun, "read-only status change prevented\n");
-               return -EBUSY;
-       }
-
-       curlun->ro = ro;
-       curlun->initially_ro = ro;
-       LDBG(curlun, "read-only status set to %d\n", curlun->ro);
-
-       return 0;
-}
-
-ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem,
-                    const char *buf, size_t count)
-{
-       ssize_t         rc;
-       bool            ro;
-
-       rc = strtobool(buf, &ro);
-       if (rc)
-               return rc;
-
-       /*
-        * Allow the write-enable status to change only while the
-        * backing file is closed.
-        */
-       down_read(filesem);
-       rc = _fsg_store_ro(curlun, ro);
-       if (!rc)
-               rc = count;
-       up_read(filesem);
-
-       return rc;
-}
-EXPORT_SYMBOL_GPL(fsg_store_ro);
-
-ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count)
-{
-       bool            nofua;
-       int             ret;
-
-       ret = strtobool(buf, &nofua);
-       if (ret)
-               return ret;
-
-       /* Sync data when switching from async mode to sync */
-       if (!nofua && curlun->nofua)
-               fsg_lun_fsync_sub(curlun);
-
-       curlun->nofua = nofua;
-
-       return count;
-}
-EXPORT_SYMBOL_GPL(fsg_store_nofua);
-
-ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
-                      const char *buf, size_t count)
-{
-       int             rc = 0;
-
-       if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) {
-               LDBG(curlun, "eject attempt prevented\n");
-               return -EBUSY;                          /* "Door is locked" */
-       }
-
-       /* Remove a trailing newline */
-       if (count > 0 && buf[count-1] == '\n')
-               ((char *) buf)[count-1] = 0;            /* Ugh! */
-
-       /* Load new medium */
-       down_write(filesem);
-       if (count > 0 && buf[0]) {
-               /* fsg_lun_open() will close existing file if any. */
-               rc = fsg_lun_open(curlun, buf);
-               if (rc == 0)
-                       curlun->unit_attention_data =
-                                       SS_NOT_READY_TO_READY_TRANSITION;
-       } else if (fsg_lun_is_open(curlun)) {
-               fsg_lun_close(curlun);
-               curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
-       }
-       up_write(filesem);
-       return (rc < 0 ? rc : count);
-}
-EXPORT_SYMBOL_GPL(fsg_store_file);
-
-ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem,
-                       const char *buf, size_t count)
-{
-       bool            cdrom;
-       int             ret;
-
-       ret = strtobool(buf, &cdrom);
-       if (ret)
-               return ret;
-
-       down_read(filesem);
-       ret = cdrom ? _fsg_store_ro(curlun, true) : 0;
-
-       if (!ret) {
-               curlun->cdrom = cdrom;
-               ret = count;
-       }
-       up_read(filesem);
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(fsg_store_cdrom);
-
-ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf,
-                           size_t count)
-{
-       bool            removable;
-       int             ret;
-
-       ret = strtobool(buf, &removable);
-       if (ret)
-               return ret;
-
-       curlun->removable = removable;
-
-       return count;
-}
-EXPORT_SYMBOL_GPL(fsg_store_removable);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/storage_common.h b/drivers/usb/gadget/storage_common.h
deleted file mode 100644 (file)
index 70c8914..0000000
+++ /dev/null
@@ -1,225 +0,0 @@
-#ifndef USB_STORAGE_COMMON_H
-#define USB_STORAGE_COMMON_H
-
-#include <linux/device.h>
-#include <linux/usb/storage.h>
-#include <scsi/scsi.h>
-#include <asm/unaligned.h>
-
-#ifndef DEBUG
-#undef VERBOSE_DEBUG
-#undef DUMP_MSGS
-#endif /* !DEBUG */
-
-#ifdef VERBOSE_DEBUG
-#define VLDBG  LDBG
-#else
-#define VLDBG(lun, fmt, args...) do { } while (0)
-#endif /* VERBOSE_DEBUG */
-
-#define _LMSG(func, lun, fmt, args...)                                 \
-       do {                                                            \
-               if ((lun)->name_pfx && *(lun)->name_pfx)                \
-                       func("%s/%s: " fmt, *(lun)->name_pfx,           \
-                                (lun)->name, ## args);                 \
-               else                                                    \
-                       func("%s: " fmt, (lun)->name, ## args);         \
-       } while (0)
-
-#define LDBG(lun, fmt, args...)                _LMSG(pr_debug, lun, fmt, ## args)
-#define LERROR(lun, fmt, args...)      _LMSG(pr_err, lun, fmt, ## args)
-#define LWARN(lun, fmt, args...)       _LMSG(pr_warn, lun, fmt, ## args)
-#define LINFO(lun, fmt, args...)       _LMSG(pr_info, lun, fmt, ## args)
-
-
-#ifdef DUMP_MSGS
-
-#  define dump_msg(fsg, /* const char * */ label,                      \
-                  /* const u8 * */ buf, /* unsigned */ length)         \
-do {                                                                   \
-       if (length < 512) {                                             \
-               DBG(fsg, "%s, length %u:\n", label, length);            \
-               print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET,      \
-                              16, 1, buf, length, 0);                  \
-       }                                                               \
-} while (0)
-
-#  define dump_cdb(fsg) do { } while (0)
-
-#else
-
-#  define dump_msg(fsg, /* const char * */ label, \
-                  /* const u8 * */ buf, /* unsigned */ length) do { } while (0)
-
-#  ifdef VERBOSE_DEBUG
-
-#    define dump_cdb(fsg)                                              \
-       print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE,      \
-                      16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0)         \
-
-#  else
-
-#    define dump_cdb(fsg) do { } while (0)
-
-#  endif /* VERBOSE_DEBUG */
-
-#endif /* DUMP_MSGS */
-
-/* Length of a SCSI Command Data Block */
-#define MAX_COMMAND_SIZE       16
-
-/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */
-#define SS_NO_SENSE                            0
-#define SS_COMMUNICATION_FAILURE               0x040800
-#define SS_INVALID_COMMAND                     0x052000
-#define SS_INVALID_FIELD_IN_CDB                        0x052400
-#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE  0x052100
-#define SS_LOGICAL_UNIT_NOT_SUPPORTED          0x052500
-#define SS_MEDIUM_NOT_PRESENT                  0x023a00
-#define SS_MEDIUM_REMOVAL_PREVENTED            0x055302
-#define SS_NOT_READY_TO_READY_TRANSITION       0x062800
-#define SS_RESET_OCCURRED                      0x062900
-#define SS_SAVING_PARAMETERS_NOT_SUPPORTED     0x053900
-#define SS_UNRECOVERED_READ_ERROR              0x031100
-#define SS_WRITE_ERROR                         0x030c02
-#define SS_WRITE_PROTECTED                     0x072700
-
-#define SK(x)          ((u8) ((x) >> 16))      /* Sense Key byte, etc. */
-#define ASC(x)         ((u8) ((x) >> 8))
-#define ASCQ(x)                ((u8) (x))
-
-struct fsg_lun {
-       struct file     *filp;
-       loff_t          file_length;
-       loff_t          num_sectors;
-
-       unsigned int    initially_ro:1;
-       unsigned int    ro:1;
-       unsigned int    removable:1;
-       unsigned int    cdrom:1;
-       unsigned int    prevent_medium_removal:1;
-       unsigned int    registered:1;
-       unsigned int    info_valid:1;
-       unsigned int    nofua:1;
-
-       u32             sense_data;
-       u32             sense_data_info;
-       u32             unit_attention_data;
-
-       unsigned int    blkbits; /* Bits of logical block size
-                                                      of bound block device */
-       unsigned int    blksize; /* logical block size of bound block device */
-       struct device   dev;
-       const char      *name;          /* "lun.name" */
-       const char      **name_pfx;     /* "function.name" */
-};
-
-static inline bool fsg_lun_is_open(struct fsg_lun *curlun)
-{
-       return curlun->filp != NULL;
-}
-
-/* Default size of buffer length. */
-#define FSG_BUFLEN     ((u32)16384)
-
-/* Maximal number of LUNs supported in mass storage function */
-#define FSG_MAX_LUNS   8
-
-enum fsg_buffer_state {
-       BUF_STATE_EMPTY = 0,
-       BUF_STATE_FULL,
-       BUF_STATE_BUSY
-};
-
-struct fsg_buffhd {
-       void                            *buf;
-       enum fsg_buffer_state           state;
-       struct fsg_buffhd               *next;
-
-       /*
-        * The NetChip 2280 is faster, and handles some protocol faults
-        * better, if we don't submit any short bulk-out read requests.
-        * So we will record the intended request length here.
-        */
-       unsigned int                    bulk_out_intended_length;
-
-       struct usb_request              *inreq;
-       int                             inreq_busy;
-       struct usb_request              *outreq;
-       int                             outreq_busy;
-};
-
-enum fsg_state {
-       /* This one isn't used anywhere */
-       FSG_STATE_COMMAND_PHASE = -10,
-       FSG_STATE_DATA_PHASE,
-       FSG_STATE_STATUS_PHASE,
-
-       FSG_STATE_IDLE = 0,
-       FSG_STATE_ABORT_BULK_OUT,
-       FSG_STATE_RESET,
-       FSG_STATE_INTERFACE_CHANGE,
-       FSG_STATE_CONFIG_CHANGE,
-       FSG_STATE_DISCONNECT,
-       FSG_STATE_EXIT,
-       FSG_STATE_TERMINATED
-};
-
-enum data_direction {
-       DATA_DIR_UNKNOWN = 0,
-       DATA_DIR_FROM_HOST,
-       DATA_DIR_TO_HOST,
-       DATA_DIR_NONE
-};
-
-static inline u32 get_unaligned_be24(u8 *buf)
-{
-       return 0xffffff & (u32) get_unaligned_be32(buf - 1);
-}
-
-static inline struct fsg_lun *fsg_lun_from_dev(struct device *dev)
-{
-       return container_of(dev, struct fsg_lun, dev);
-}
-
-enum {
-       FSG_STRING_INTERFACE
-};
-
-extern struct usb_interface_descriptor fsg_intf_desc;
-
-extern struct usb_endpoint_descriptor fsg_fs_bulk_in_desc;
-extern struct usb_endpoint_descriptor fsg_fs_bulk_out_desc;
-extern struct usb_descriptor_header *fsg_fs_function[];
-
-extern struct usb_endpoint_descriptor fsg_hs_bulk_in_desc;
-extern struct usb_endpoint_descriptor fsg_hs_bulk_out_desc;
-extern struct usb_descriptor_header *fsg_hs_function[];
-
-extern struct usb_endpoint_descriptor fsg_ss_bulk_in_desc;
-extern struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc;
-extern struct usb_endpoint_descriptor fsg_ss_bulk_out_desc;
-extern struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc;
-extern struct usb_descriptor_header *fsg_ss_function[];
-
-void fsg_lun_close(struct fsg_lun *curlun);
-int fsg_lun_open(struct fsg_lun *curlun, const char *filename);
-int fsg_lun_fsync_sub(struct fsg_lun *curlun);
-void store_cdrom_address(u8 *dest, int msf, u32 addr);
-ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf);
-ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf);
-ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
-                     char *buf);
-ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf);
-ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf);
-ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem,
-                    const char *buf, size_t count);
-ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count);
-ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
-                      const char *buf, size_t count);
-ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem,
-                       const char *buf, size_t count);
-ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf,
-                           size_t count);
-
-#endif /* USB_STORAGE_COMMON_H */
diff --git a/drivers/usb/gadget/u_ecm.h b/drivers/usb/gadget/u_ecm.h
deleted file mode 100644 (file)
index 262cc03..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * u_ecm.h
- *
- * Utility definitions for the ecm function
- *
- * Copyright (c) 2013 Samsung Electronics Co., Ltd.
- *             http://www.samsung.com
- *
- * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef U_ECM_H
-#define U_ECM_H
-
-#include <linux/usb/composite.h>
-
-struct f_ecm_opts {
-       struct usb_function_instance    func_inst;
-       struct net_device               *net;
-       bool                            bound;
-
-       /*
-        * Read/write access to configfs attributes is handled by configfs.
-        *
-        * This is to protect the data from concurrent access by read/write
-        * and create symlink/remove symlink.
-        */
-       struct mutex                    lock;
-       int                             refcnt;
-};
-
-#endif /* U_ECM_H */
diff --git a/drivers/usb/gadget/u_eem.h b/drivers/usb/gadget/u_eem.h
deleted file mode 100644 (file)
index e3ae978..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * u_eem.h
- *
- * Utility definitions for the eem function
- *
- * Copyright (c) 2013 Samsung Electronics Co., Ltd.
- *             http://www.samsung.com
- *
- * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef U_EEM_H
-#define U_EEM_H
-
-#include <linux/usb/composite.h>
-
-struct f_eem_opts {
-       struct usb_function_instance    func_inst;
-       struct net_device               *net;
-       bool                            bound;
-
-       /*
-        * Read/write access to configfs attributes is handled by configfs.
-        *
-        * This is to protect the data from concurrent access by read/write
-        * and create symlink/remove symlink.
-        */
-       struct mutex                    lock;
-       int                             refcnt;
-};
-
-#endif /* U_EEM_H */
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
deleted file mode 100644 (file)
index 6e6f876..0000000
+++ /dev/null
@@ -1,1179 +0,0 @@
-/*
- * u_ether.c -- Ethernet-over-USB link layer utilities for Gadget stack
- *
- * Copyright (C) 2003-2005,2008 David Brownell
- * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
- * Copyright (C) 2008 Nokia Corporation
- *
- * 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.
- */
-
-/* #define VERBOSE_DEBUG */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/gfp.h>
-#include <linux/device.h>
-#include <linux/ctype.h>
-#include <linux/etherdevice.h>
-#include <linux/ethtool.h>
-#include <linux/if_vlan.h>
-
-#include "u_ether.h"
-
-
-/*
- * This component encapsulates the Ethernet link glue needed to provide
- * one (!) network link through the USB gadget stack, normally "usb0".
- *
- * The control and data models are handled by the function driver which
- * connects to this code; such as CDC Ethernet (ECM or EEM),
- * "CDC Subset", or RNDIS.  That includes all descriptor and endpoint
- * management.
- *
- * Link level addressing is handled by this component using module
- * parameters; if no such parameters are provided, random link level
- * addresses are used.  Each end of the link uses one address.  The
- * host end address is exported in various ways, and is often recorded
- * in configuration databases.
- *
- * The driver which assembles each configuration using such a link is
- * responsible for ensuring that each configuration includes at most one
- * instance of is network link.  (The network layer provides ways for
- * this single "physical" link to be used by multiple virtual links.)
- */
-
-#define UETH__VERSION  "29-May-2008"
-
-struct eth_dev {
-       /* lock is held while accessing port_usb
-        */
-       spinlock_t              lock;
-       struct gether           *port_usb;
-
-       struct net_device       *net;
-       struct usb_gadget       *gadget;
-
-       spinlock_t              req_lock;       /* guard {rx,tx}_reqs */
-       struct list_head        tx_reqs, rx_reqs;
-       atomic_t                tx_qlen;
-
-       struct sk_buff_head     rx_frames;
-
-       unsigned                qmult;
-
-       unsigned                header_len;
-       struct sk_buff          *(*wrap)(struct gether *, struct sk_buff *skb);
-       int                     (*unwrap)(struct gether *,
-                                               struct sk_buff *skb,
-                                               struct sk_buff_head *list);
-
-       struct work_struct      work;
-
-       unsigned long           todo;
-#define        WORK_RX_MEMORY          0
-
-       bool                    zlp;
-       u8                      host_mac[ETH_ALEN];
-       u8                      dev_mac[ETH_ALEN];
-};
-
-/*-------------------------------------------------------------------------*/
-
-#define RX_EXTRA       20      /* bytes guarding against rx overflows */
-
-#define DEFAULT_QLEN   2       /* double buffering by default */
-
-/* for dual-speed hardware, use deeper queues at high/super speed */
-static inline int qlen(struct usb_gadget *gadget, unsigned qmult)
-{
-       if (gadget_is_dualspeed(gadget) && (gadget->speed == USB_SPEED_HIGH ||
-                                           gadget->speed == USB_SPEED_SUPER))
-               return qmult * DEFAULT_QLEN;
-       else
-               return DEFAULT_QLEN;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* REVISIT there must be a better way than having two sets
- * of debug calls ...
- */
-
-#undef DBG
-#undef VDBG
-#undef ERROR
-#undef INFO
-
-#define xprintk(d, level, fmt, args...) \
-       printk(level "%s: " fmt , (d)->net->name , ## args)
-
-#ifdef DEBUG
-#undef DEBUG
-#define DBG(dev, fmt, args...) \
-       xprintk(dev , KERN_DEBUG , fmt , ## args)
-#else
-#define DBG(dev, fmt, args...) \
-       do { } while (0)
-#endif /* DEBUG */
-
-#ifdef VERBOSE_DEBUG
-#define VDBG   DBG
-#else
-#define VDBG(dev, fmt, args...) \
-       do { } while (0)
-#endif /* DEBUG */
-
-#define ERROR(dev, fmt, args...) \
-       xprintk(dev , KERN_ERR , fmt , ## args)
-#define INFO(dev, fmt, args...) \
-       xprintk(dev , KERN_INFO , fmt , ## args)
-
-/*-------------------------------------------------------------------------*/
-
-/* NETWORK DRIVER HOOKUP (to the layer above this driver) */
-
-static int ueth_change_mtu(struct net_device *net, int new_mtu)
-{
-       struct eth_dev  *dev = netdev_priv(net);
-       unsigned long   flags;
-       int             status = 0;
-
-       /* don't change MTU on "live" link (peer won't know) */
-       spin_lock_irqsave(&dev->lock, flags);
-       if (dev->port_usb)
-               status = -EBUSY;
-       else if (new_mtu <= ETH_HLEN || new_mtu > ETH_FRAME_LEN)
-               status = -ERANGE;
-       else
-               net->mtu = new_mtu;
-       spin_unlock_irqrestore(&dev->lock, flags);
-
-       return status;
-}
-
-static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p)
-{
-       struct eth_dev *dev = netdev_priv(net);
-
-       strlcpy(p->driver, "g_ether", sizeof(p->driver));
-       strlcpy(p->version, UETH__VERSION, sizeof(p->version));
-       strlcpy(p->fw_version, dev->gadget->name, sizeof(p->fw_version));
-       strlcpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof(p->bus_info));
-}
-
-/* REVISIT can also support:
- *   - WOL (by tracking suspends and issuing remote wakeup)
- *   - msglevel (implies updated messaging)
- *   - ... probably more ethtool ops
- */
-
-static const struct ethtool_ops ops = {
-       .get_drvinfo = eth_get_drvinfo,
-       .get_link = ethtool_op_get_link,
-};
-
-static void defer_kevent(struct eth_dev *dev, int flag)
-{
-       if (test_and_set_bit(flag, &dev->todo))
-               return;
-       if (!schedule_work(&dev->work))
-               ERROR(dev, "kevent %d may have been dropped\n", flag);
-       else
-               DBG(dev, "kevent %d scheduled\n", flag);
-}
-
-static void rx_complete(struct usb_ep *ep, struct usb_request *req);
-
-static int
-rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
-{
-       struct sk_buff  *skb;
-       int             retval = -ENOMEM;
-       size_t          size = 0;
-       struct usb_ep   *out;
-       unsigned long   flags;
-
-       spin_lock_irqsave(&dev->lock, flags);
-       if (dev->port_usb)
-               out = dev->port_usb->out_ep;
-       else
-               out = NULL;
-       spin_unlock_irqrestore(&dev->lock, flags);
-
-       if (!out)
-               return -ENOTCONN;
-
-
-       /* Padding up to RX_EXTRA handles minor disagreements with host.
-        * Normally we use the USB "terminate on short read" convention;
-        * so allow up to (N*maxpacket), since that memory is normally
-        * already allocated.  Some hardware doesn't deal well with short
-        * reads (e.g. DMA must be N*maxpacket), so for now don't trim a
-        * byte off the end (to force hardware errors on overflow).
-        *
-        * RNDIS uses internal framing, and explicitly allows senders to
-        * pad to end-of-packet.  That's potentially nice for speed, but
-        * means receivers can't recover lost synch on their own (because
-        * new packets don't only start after a short RX).
-        */
-       size += sizeof(struct ethhdr) + dev->net->mtu + RX_EXTRA;
-       size += dev->port_usb->header_len;
-       size += out->maxpacket - 1;
-       size -= size % out->maxpacket;
-
-       if (dev->port_usb->is_fixed)
-               size = max_t(size_t, size, dev->port_usb->fixed_out_len);
-
-       skb = alloc_skb(size + NET_IP_ALIGN, gfp_flags);
-       if (skb == NULL) {
-               DBG(dev, "no rx skb\n");
-               goto enomem;
-       }
-
-       /* Some platforms perform better when IP packets are aligned,
-        * but on at least one, checksumming fails otherwise.  Note:
-        * RNDIS headers involve variable numbers of LE32 values.
-        */
-       skb_reserve(skb, NET_IP_ALIGN);
-
-       req->buf = skb->data;
-       req->length = size;
-       req->complete = rx_complete;
-       req->context = skb;
-
-       retval = usb_ep_queue(out, req, gfp_flags);
-       if (retval == -ENOMEM)
-enomem:
-               defer_kevent(dev, WORK_RX_MEMORY);
-       if (retval) {
-               DBG(dev, "rx submit --> %d\n", retval);
-               if (skb)
-                       dev_kfree_skb_any(skb);
-               spin_lock_irqsave(&dev->req_lock, flags);
-               list_add(&req->list, &dev->rx_reqs);
-               spin_unlock_irqrestore(&dev->req_lock, flags);
-       }
-       return retval;
-}
-
-static void rx_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       struct sk_buff  *skb = req->context, *skb2;
-       struct eth_dev  *dev = ep->driver_data;
-       int             status = req->status;
-
-       switch (status) {
-
-       /* normal completion */
-       case 0:
-               skb_put(skb, req->actual);
-
-               if (dev->unwrap) {
-                       unsigned long   flags;
-
-                       spin_lock_irqsave(&dev->lock, flags);
-                       if (dev->port_usb) {
-                               status = dev->unwrap(dev->port_usb,
-                                                       skb,
-                                                       &dev->rx_frames);
-                       } else {
-                               dev_kfree_skb_any(skb);
-                               status = -ENOTCONN;
-                       }
-                       spin_unlock_irqrestore(&dev->lock, flags);
-               } else {
-                       skb_queue_tail(&dev->rx_frames, skb);
-               }
-               skb = NULL;
-
-               skb2 = skb_dequeue(&dev->rx_frames);
-               while (skb2) {
-                       if (status < 0
-                                       || ETH_HLEN > skb2->len
-                                       || skb2->len > VLAN_ETH_FRAME_LEN) {
-                               dev->net->stats.rx_errors++;
-                               dev->net->stats.rx_length_errors++;
-                               DBG(dev, "rx length %d\n", skb2->len);
-                               dev_kfree_skb_any(skb2);
-                               goto next_frame;
-                       }
-                       skb2->protocol = eth_type_trans(skb2, dev->net);
-                       dev->net->stats.rx_packets++;
-                       dev->net->stats.rx_bytes += skb2->len;
-
-                       /* no buffer copies needed, unless hardware can't
-                        * use skb buffers.
-                        */
-                       status = netif_rx(skb2);
-next_frame:
-                       skb2 = skb_dequeue(&dev->rx_frames);
-               }
-               break;
-
-       /* software-driven interface shutdown */
-       case -ECONNRESET:               /* unlink */
-       case -ESHUTDOWN:                /* disconnect etc */
-               VDBG(dev, "rx shutdown, code %d\n", status);
-               goto quiesce;
-
-       /* for hardware automagic (such as pxa) */
-       case -ECONNABORTED:             /* endpoint reset */
-               DBG(dev, "rx %s reset\n", ep->name);
-               defer_kevent(dev, WORK_RX_MEMORY);
-quiesce:
-               dev_kfree_skb_any(skb);
-               goto clean;
-
-       /* data overrun */
-       case -EOVERFLOW:
-               dev->net->stats.rx_over_errors++;
-               /* FALLTHROUGH */
-
-       default:
-               dev->net->stats.rx_errors++;
-               DBG(dev, "rx status %d\n", status);
-               break;
-       }
-
-       if (skb)
-               dev_kfree_skb_any(skb);
-       if (!netif_running(dev->net)) {
-clean:
-               spin_lock(&dev->req_lock);
-               list_add(&req->list, &dev->rx_reqs);
-               spin_unlock(&dev->req_lock);
-               req = NULL;
-       }
-       if (req)
-               rx_submit(dev, req, GFP_ATOMIC);
-}
-
-static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n)
-{
-       unsigned                i;
-       struct usb_request      *req;
-
-       if (!n)
-               return -ENOMEM;
-
-       /* queue/recycle up to N requests */
-       i = n;
-       list_for_each_entry(req, list, list) {
-               if (i-- == 0)
-                       goto extra;
-       }
-       while (i--) {
-               req = usb_ep_alloc_request(ep, GFP_ATOMIC);
-               if (!req)
-                       return list_empty(list) ? -ENOMEM : 0;
-               list_add(&req->list, list);
-       }
-       return 0;
-
-extra:
-       /* free extras */
-       for (;;) {
-               struct list_head        *next;
-
-               next = req->list.next;
-               list_del(&req->list);
-               usb_ep_free_request(ep, req);
-
-               if (next == list)
-                       break;
-
-               req = container_of(next, struct usb_request, list);
-       }
-       return 0;
-}
-
-static int alloc_requests(struct eth_dev *dev, struct gether *link, unsigned n)
-{
-       int     status;
-
-       spin_lock(&dev->req_lock);
-       status = prealloc(&dev->tx_reqs, link->in_ep, n);
-       if (status < 0)
-               goto fail;
-       status = prealloc(&dev->rx_reqs, link->out_ep, n);
-       if (status < 0)
-               goto fail;
-       goto done;
-fail:
-       DBG(dev, "can't alloc requests\n");
-done:
-       spin_unlock(&dev->req_lock);
-       return status;
-}
-
-static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)
-{
-       struct usb_request      *req;
-       unsigned long           flags;
-
-       /* fill unused rxq slots with some skb */
-       spin_lock_irqsave(&dev->req_lock, flags);
-       while (!list_empty(&dev->rx_reqs)) {
-               req = container_of(dev->rx_reqs.next,
-                               struct usb_request, list);
-               list_del_init(&req->list);
-               spin_unlock_irqrestore(&dev->req_lock, flags);
-
-               if (rx_submit(dev, req, gfp_flags) < 0) {
-                       defer_kevent(dev, WORK_RX_MEMORY);
-                       return;
-               }
-
-               spin_lock_irqsave(&dev->req_lock, flags);
-       }
-       spin_unlock_irqrestore(&dev->req_lock, flags);
-}
-
-static void eth_work(struct work_struct *work)
-{
-       struct eth_dev  *dev = container_of(work, struct eth_dev, work);
-
-       if (test_and_clear_bit(WORK_RX_MEMORY, &dev->todo)) {
-               if (netif_running(dev->net))
-                       rx_fill(dev, GFP_KERNEL);
-       }
-
-       if (dev->todo)
-               DBG(dev, "work done, flags = 0x%lx\n", dev->todo);
-}
-
-static void tx_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       struct sk_buff  *skb = req->context;
-       struct eth_dev  *dev = ep->driver_data;
-
-       switch (req->status) {
-       default:
-               dev->net->stats.tx_errors++;
-               VDBG(dev, "tx err %d\n", req->status);
-               /* FALLTHROUGH */
-       case -ECONNRESET:               /* unlink */
-       case -ESHUTDOWN:                /* disconnect etc */
-               break;
-       case 0:
-               dev->net->stats.tx_bytes += skb->len;
-       }
-       dev->net->stats.tx_packets++;
-
-       spin_lock(&dev->req_lock);
-       list_add(&req->list, &dev->tx_reqs);
-       spin_unlock(&dev->req_lock);
-       dev_kfree_skb_any(skb);
-
-       atomic_dec(&dev->tx_qlen);
-       if (netif_carrier_ok(dev->net))
-               netif_wake_queue(dev->net);
-}
-
-static inline int is_promisc(u16 cdc_filter)
-{
-       return cdc_filter & USB_CDC_PACKET_TYPE_PROMISCUOUS;
-}
-
-static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
-                                       struct net_device *net)
-{
-       struct eth_dev          *dev = netdev_priv(net);
-       int                     length = 0;
-       int                     retval;
-       struct usb_request      *req = NULL;
-       unsigned long           flags;
-       struct usb_ep           *in;
-       u16                     cdc_filter;
-
-       spin_lock_irqsave(&dev->lock, flags);
-       if (dev->port_usb) {
-               in = dev->port_usb->in_ep;
-               cdc_filter = dev->port_usb->cdc_filter;
-       } else {
-               in = NULL;
-               cdc_filter = 0;
-       }
-       spin_unlock_irqrestore(&dev->lock, flags);
-
-       if (skb && !in) {
-               dev_kfree_skb_any(skb);
-               return NETDEV_TX_OK;
-       }
-
-       /* apply outgoing CDC or RNDIS filters */
-       if (skb && !is_promisc(cdc_filter)) {
-               u8              *dest = skb->data;
-
-               if (is_multicast_ether_addr(dest)) {
-                       u16     type;
-
-                       /* ignores USB_CDC_PACKET_TYPE_MULTICAST and host
-                        * SET_ETHERNET_MULTICAST_FILTERS requests
-                        */
-                       if (is_broadcast_ether_addr(dest))
-                               type = USB_CDC_PACKET_TYPE_BROADCAST;
-                       else
-                               type = USB_CDC_PACKET_TYPE_ALL_MULTICAST;
-                       if (!(cdc_filter & type)) {
-                               dev_kfree_skb_any(skb);
-                               return NETDEV_TX_OK;
-                       }
-               }
-               /* ignores USB_CDC_PACKET_TYPE_DIRECTED */
-       }
-
-       spin_lock_irqsave(&dev->req_lock, flags);
-       /*
-        * this freelist can be empty if an interrupt triggered disconnect()
-        * and reconfigured the gadget (shutting down this queue) after the
-        * network stack decided to xmit but before we got the spinlock.
-        */
-       if (list_empty(&dev->tx_reqs)) {
-               spin_unlock_irqrestore(&dev->req_lock, flags);
-               return NETDEV_TX_BUSY;
-       }
-
-       req = container_of(dev->tx_reqs.next, struct usb_request, list);
-       list_del(&req->list);
-
-       /* temporarily stop TX queue when the freelist empties */
-       if (list_empty(&dev->tx_reqs))
-               netif_stop_queue(net);
-       spin_unlock_irqrestore(&dev->req_lock, flags);
-
-       /* no buffer copies needed, unless the network stack did it
-        * or the hardware can't use skb buffers.
-        * or there's not enough space for extra headers we need
-        */
-       if (dev->wrap) {
-               unsigned long   flags;
-
-               spin_lock_irqsave(&dev->lock, flags);
-               if (dev->port_usb)
-                       skb = dev->wrap(dev->port_usb, skb);
-               spin_unlock_irqrestore(&dev->lock, flags);
-               if (!skb) {
-                       /* Multi frame CDC protocols may store the frame for
-                        * later which is not a dropped frame.
-                        */
-                       if (dev->port_usb->supports_multi_frame)
-                               goto multiframe;
-                       goto drop;
-               }
-       }
-
-       length = skb->len;
-       req->buf = skb->data;
-       req->context = skb;
-       req->complete = tx_complete;
-
-       /* NCM requires no zlp if transfer is dwNtbInMaxSize */
-       if (dev->port_usb->is_fixed &&
-           length == dev->port_usb->fixed_in_len &&
-           (length % in->maxpacket) == 0)
-               req->zero = 0;
-       else
-               req->zero = 1;
-
-       /* use zlp framing on tx for strict CDC-Ether conformance,
-        * though any robust network rx path ignores extra padding.
-        * and some hardware doesn't like to write zlps.
-        */
-       if (req->zero && !dev->zlp && (length % in->maxpacket) == 0)
-               length++;
-
-       req->length = length;
-
-       /* throttle high/super speed IRQ rate back slightly */
-       if (gadget_is_dualspeed(dev->gadget))
-               req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH ||
-                                    dev->gadget->speed == USB_SPEED_SUPER)
-                       ? ((atomic_read(&dev->tx_qlen) % dev->qmult) != 0)
-                       : 0;
-
-       retval = usb_ep_queue(in, req, GFP_ATOMIC);
-       switch (retval) {
-       default:
-               DBG(dev, "tx queue err %d\n", retval);
-               break;
-       case 0:
-               net->trans_start = jiffies;
-               atomic_inc(&dev->tx_qlen);
-       }
-
-       if (retval) {
-               dev_kfree_skb_any(skb);
-drop:
-               dev->net->stats.tx_dropped++;
-multiframe:
-               spin_lock_irqsave(&dev->req_lock, flags);
-               if (list_empty(&dev->tx_reqs))
-                       netif_start_queue(net);
-               list_add(&req->list, &dev->tx_reqs);
-               spin_unlock_irqrestore(&dev->req_lock, flags);
-       }
-       return NETDEV_TX_OK;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static void eth_start(struct eth_dev *dev, gfp_t gfp_flags)
-{
-       DBG(dev, "%s\n", __func__);
-
-       /* fill the rx queue */
-       rx_fill(dev, gfp_flags);
-
-       /* and open the tx floodgates */
-       atomic_set(&dev->tx_qlen, 0);
-       netif_wake_queue(dev->net);
-}
-
-static int eth_open(struct net_device *net)
-{
-       struct eth_dev  *dev = netdev_priv(net);
-       struct gether   *link;
-
-       DBG(dev, "%s\n", __func__);
-       if (netif_carrier_ok(dev->net))
-               eth_start(dev, GFP_KERNEL);
-
-       spin_lock_irq(&dev->lock);
-       link = dev->port_usb;
-       if (link && link->open)
-               link->open(link);
-       spin_unlock_irq(&dev->lock);
-
-       return 0;
-}
-
-static int eth_stop(struct net_device *net)
-{
-       struct eth_dev  *dev = netdev_priv(net);
-       unsigned long   flags;
-
-       VDBG(dev, "%s\n", __func__);
-       netif_stop_queue(net);
-
-       DBG(dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n",
-               dev->net->stats.rx_packets, dev->net->stats.tx_packets,
-               dev->net->stats.rx_errors, dev->net->stats.tx_errors
-               );
-
-       /* ensure there are no more active requests */
-       spin_lock_irqsave(&dev->lock, flags);
-       if (dev->port_usb) {
-               struct gether   *link = dev->port_usb;
-               const struct usb_endpoint_descriptor *in;
-               const struct usb_endpoint_descriptor *out;
-
-               if (link->close)
-                       link->close(link);
-
-               /* NOTE:  we have no abort-queue primitive we could use
-                * to cancel all pending I/O.  Instead, we disable then
-                * reenable the endpoints ... this idiom may leave toggle
-                * wrong, but that's a self-correcting error.
-                *
-                * REVISIT:  we *COULD* just let the transfers complete at
-                * their own pace; the network stack can handle old packets.
-                * For the moment we leave this here, since it works.
-                */
-               in = link->in_ep->desc;
-               out = link->out_ep->desc;
-               usb_ep_disable(link->in_ep);
-               usb_ep_disable(link->out_ep);
-               if (netif_carrier_ok(net)) {
-                       DBG(dev, "host still using in/out endpoints\n");
-                       link->in_ep->desc = in;
-                       link->out_ep->desc = out;
-                       usb_ep_enable(link->in_ep);
-                       usb_ep_enable(link->out_ep);
-               }
-       }
-       spin_unlock_irqrestore(&dev->lock, flags);
-
-       return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static int get_ether_addr(const char *str, u8 *dev_addr)
-{
-       if (str) {
-               unsigned        i;
-
-               for (i = 0; i < 6; i++) {
-                       unsigned char num;
-
-                       if ((*str == '.') || (*str == ':'))
-                               str++;
-                       num = hex_to_bin(*str++) << 4;
-                       num |= hex_to_bin(*str++);
-                       dev_addr [i] = num;
-               }
-               if (is_valid_ether_addr(dev_addr))
-                       return 0;
-       }
-       eth_random_addr(dev_addr);
-       return 1;
-}
-
-static int get_ether_addr_str(u8 dev_addr[ETH_ALEN], char *str, int len)
-{
-       if (len < 18)
-               return -EINVAL;
-
-       snprintf(str, len, "%02x:%02x:%02x:%02x:%02x:%02x",
-                dev_addr[0], dev_addr[1], dev_addr[2],
-                dev_addr[3], dev_addr[4], dev_addr[5]);
-       return 18;
-}
-
-static const struct net_device_ops eth_netdev_ops = {
-       .ndo_open               = eth_open,
-       .ndo_stop               = eth_stop,
-       .ndo_start_xmit         = eth_start_xmit,
-       .ndo_change_mtu         = ueth_change_mtu,
-       .ndo_set_mac_address    = eth_mac_addr,
-       .ndo_validate_addr      = eth_validate_addr,
-};
-
-static struct device_type gadget_type = {
-       .name   = "gadget",
-};
-
-/**
- * gether_setup_name - initialize one ethernet-over-usb link
- * @g: gadget to associated with these links
- * @ethaddr: NULL, or a buffer in which the ethernet address of the
- *     host side of the link is recorded
- * @netname: name for network device (for example, "usb")
- * Context: may sleep
- *
- * This sets up the single network link that may be exported by a
- * gadget driver using this framework.  The link layer addresses are
- * set up using module parameters.
- *
- * Returns an eth_dev pointer on success, or an ERR_PTR on failure.
- */
-struct eth_dev *gether_setup_name(struct usb_gadget *g,
-               const char *dev_addr, const char *host_addr,
-               u8 ethaddr[ETH_ALEN], unsigned qmult, const char *netname)
-{
-       struct eth_dev          *dev;
-       struct net_device       *net;
-       int                     status;
-
-       net = alloc_etherdev(sizeof *dev);
-       if (!net)
-               return ERR_PTR(-ENOMEM);
-
-       dev = netdev_priv(net);
-       spin_lock_init(&dev->lock);
-       spin_lock_init(&dev->req_lock);
-       INIT_WORK(&dev->work, eth_work);
-       INIT_LIST_HEAD(&dev->tx_reqs);
-       INIT_LIST_HEAD(&dev->rx_reqs);
-
-       skb_queue_head_init(&dev->rx_frames);
-
-       /* network device setup */
-       dev->net = net;
-       dev->qmult = qmult;
-       snprintf(net->name, sizeof(net->name), "%s%%d", netname);
-
-       if (get_ether_addr(dev_addr, net->dev_addr))
-               dev_warn(&g->dev,
-                       "using random %s ethernet address\n", "self");
-       if (get_ether_addr(host_addr, dev->host_mac))
-               dev_warn(&g->dev,
-                       "using random %s ethernet address\n", "host");
-
-       if (ethaddr)
-               memcpy(ethaddr, dev->host_mac, ETH_ALEN);
-
-       net->netdev_ops = &eth_netdev_ops;
-
-       net->ethtool_ops = &ops;
-
-       dev->gadget = g;
-       SET_NETDEV_DEV(net, &g->dev);
-       SET_NETDEV_DEVTYPE(net, &gadget_type);
-
-       status = register_netdev(net);
-       if (status < 0) {
-               dev_dbg(&g->dev, "register_netdev failed, %d\n", status);
-               free_netdev(net);
-               dev = ERR_PTR(status);
-       } else {
-               INFO(dev, "MAC %pM\n", net->dev_addr);
-               INFO(dev, "HOST MAC %pM\n", dev->host_mac);
-
-               /*
-                * two kinds of host-initiated state changes:
-                *  - iff DATA transfer is active, carrier is "on"
-                *  - tx queueing enabled if open *and* carrier is "on"
-                */
-               netif_carrier_off(net);
-       }
-
-       return dev;
-}
-EXPORT_SYMBOL_GPL(gether_setup_name);
-
-struct net_device *gether_setup_name_default(const char *netname)
-{
-       struct net_device       *net;
-       struct eth_dev          *dev;
-
-       net = alloc_etherdev(sizeof(*dev));
-       if (!net)
-               return ERR_PTR(-ENOMEM);
-
-       dev = netdev_priv(net);
-       spin_lock_init(&dev->lock);
-       spin_lock_init(&dev->req_lock);
-       INIT_WORK(&dev->work, eth_work);
-       INIT_LIST_HEAD(&dev->tx_reqs);
-       INIT_LIST_HEAD(&dev->rx_reqs);
-
-       skb_queue_head_init(&dev->rx_frames);
-
-       /* network device setup */
-       dev->net = net;
-       dev->qmult = QMULT_DEFAULT;
-       snprintf(net->name, sizeof(net->name), "%s%%d", netname);
-
-       eth_random_addr(dev->dev_mac);
-       pr_warn("using random %s ethernet address\n", "self");
-       eth_random_addr(dev->host_mac);
-       pr_warn("using random %s ethernet address\n", "host");
-
-       net->netdev_ops = &eth_netdev_ops;
-
-       net->ethtool_ops = &ops;
-       SET_NETDEV_DEVTYPE(net, &gadget_type);
-
-       return net;
-}
-EXPORT_SYMBOL_GPL(gether_setup_name_default);
-
-int gether_register_netdev(struct net_device *net)
-{
-       struct eth_dev *dev;
-       struct usb_gadget *g;
-       struct sockaddr sa;
-       int status;
-
-       if (!net->dev.parent)
-               return -EINVAL;
-       dev = netdev_priv(net);
-       g = dev->gadget;
-       status = register_netdev(net);
-       if (status < 0) {
-               dev_dbg(&g->dev, "register_netdev failed, %d\n", status);
-               return status;
-       } else {
-               INFO(dev, "HOST MAC %pM\n", dev->host_mac);
-
-               /* two kinds of host-initiated state changes:
-                *  - iff DATA transfer is active, carrier is "on"
-                *  - tx queueing enabled if open *and* carrier is "on"
-                */
-               netif_carrier_off(net);
-       }
-       sa.sa_family = net->type;
-       memcpy(sa.sa_data, dev->dev_mac, ETH_ALEN);
-       rtnl_lock();
-       status = dev_set_mac_address(net, &sa);
-       rtnl_unlock();
-       if (status)
-               pr_warn("cannot set self ethernet address: %d\n", status);
-       else
-               INFO(dev, "MAC %pM\n", dev->dev_mac);
-
-       return status;
-}
-EXPORT_SYMBOL_GPL(gether_register_netdev);
-
-void gether_set_gadget(struct net_device *net, struct usb_gadget *g)
-{
-       struct eth_dev *dev;
-
-       dev = netdev_priv(net);
-       dev->gadget = g;
-       SET_NETDEV_DEV(net, &g->dev);
-}
-EXPORT_SYMBOL_GPL(gether_set_gadget);
-
-int gether_set_dev_addr(struct net_device *net, const char *dev_addr)
-{
-       struct eth_dev *dev;
-       u8 new_addr[ETH_ALEN];
-
-       dev = netdev_priv(net);
-       if (get_ether_addr(dev_addr, new_addr))
-               return -EINVAL;
-       memcpy(dev->dev_mac, new_addr, ETH_ALEN);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(gether_set_dev_addr);
-
-int gether_get_dev_addr(struct net_device *net, char *dev_addr, int len)
-{
-       struct eth_dev *dev;
-
-       dev = netdev_priv(net);
-       return get_ether_addr_str(dev->dev_mac, dev_addr, len);
-}
-EXPORT_SYMBOL_GPL(gether_get_dev_addr);
-
-int gether_set_host_addr(struct net_device *net, const char *host_addr)
-{
-       struct eth_dev *dev;
-       u8 new_addr[ETH_ALEN];
-
-       dev = netdev_priv(net);
-       if (get_ether_addr(host_addr, new_addr))
-               return -EINVAL;
-       memcpy(dev->host_mac, new_addr, ETH_ALEN);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(gether_set_host_addr);
-
-int gether_get_host_addr(struct net_device *net, char *host_addr, int len)
-{
-       struct eth_dev *dev;
-
-       dev = netdev_priv(net);
-       return get_ether_addr_str(dev->host_mac, host_addr, len);
-}
-EXPORT_SYMBOL_GPL(gether_get_host_addr);
-
-int gether_get_host_addr_cdc(struct net_device *net, char *host_addr, int len)
-{
-       struct eth_dev *dev;
-
-       if (len < 13)
-               return -EINVAL;
-
-       dev = netdev_priv(net);
-       snprintf(host_addr, len, "%pm", dev->host_mac);
-
-       return strlen(host_addr);
-}
-EXPORT_SYMBOL_GPL(gether_get_host_addr_cdc);
-
-void gether_get_host_addr_u8(struct net_device *net, u8 host_mac[ETH_ALEN])
-{
-       struct eth_dev *dev;
-
-       dev = netdev_priv(net);
-       memcpy(host_mac, dev->host_mac, ETH_ALEN);
-}
-EXPORT_SYMBOL_GPL(gether_get_host_addr_u8);
-
-void gether_set_qmult(struct net_device *net, unsigned qmult)
-{
-       struct eth_dev *dev;
-
-       dev = netdev_priv(net);
-       dev->qmult = qmult;
-}
-EXPORT_SYMBOL_GPL(gether_set_qmult);
-
-unsigned gether_get_qmult(struct net_device *net)
-{
-       struct eth_dev *dev;
-
-       dev = netdev_priv(net);
-       return dev->qmult;
-}
-EXPORT_SYMBOL_GPL(gether_get_qmult);
-
-int gether_get_ifname(struct net_device *net, char *name, int len)
-{
-       rtnl_lock();
-       strlcpy(name, netdev_name(net), len);
-       rtnl_unlock();
-       return strlen(name);
-}
-EXPORT_SYMBOL_GPL(gether_get_ifname);
-
-/**
- * gether_cleanup - remove Ethernet-over-USB device
- * Context: may sleep
- *
- * This is called to free all resources allocated by @gether_setup().
- */
-void gether_cleanup(struct eth_dev *dev)
-{
-       if (!dev)
-               return;
-
-       unregister_netdev(dev->net);
-       flush_work(&dev->work);
-       free_netdev(dev->net);
-}
-EXPORT_SYMBOL_GPL(gether_cleanup);
-
-/**
- * gether_connect - notify network layer that USB link is active
- * @link: the USB link, set up with endpoints, descriptors matching
- *     current device speed, and any framing wrapper(s) set up.
- * Context: irqs blocked
- *
- * This is called to activate endpoints and let the network layer know
- * the connection is active ("carrier detect").  It may cause the I/O
- * queues to open and start letting network packets flow, but will in
- * any case activate the endpoints so that they respond properly to the
- * USB host.
- *
- * Verify net_device pointer returned using IS_ERR().  If it doesn't
- * indicate some error code (negative errno), ep->driver_data values
- * have been overwritten.
- */
-struct net_device *gether_connect(struct gether *link)
-{
-       struct eth_dev          *dev = link->ioport;
-       int                     result = 0;
-
-       if (!dev)
-               return ERR_PTR(-EINVAL);
-
-       link->in_ep->driver_data = dev;
-       result = usb_ep_enable(link->in_ep);
-       if (result != 0) {
-               DBG(dev, "enable %s --> %d\n",
-                       link->in_ep->name, result);
-               goto fail0;
-       }
-
-       link->out_ep->driver_data = dev;
-       result = usb_ep_enable(link->out_ep);
-       if (result != 0) {
-               DBG(dev, "enable %s --> %d\n",
-                       link->out_ep->name, result);
-               goto fail1;
-       }
-
-       if (result == 0)
-               result = alloc_requests(dev, link, qlen(dev->gadget,
-                                       dev->qmult));
-
-       if (result == 0) {
-               dev->zlp = link->is_zlp_ok;
-               DBG(dev, "qlen %d\n", qlen(dev->gadget, dev->qmult));
-
-               dev->header_len = link->header_len;
-               dev->unwrap = link->unwrap;
-               dev->wrap = link->wrap;
-
-               spin_lock(&dev->lock);
-               dev->port_usb = link;
-               if (netif_running(dev->net)) {
-                       if (link->open)
-                               link->open(link);
-               } else {
-                       if (link->close)
-                               link->close(link);
-               }
-               spin_unlock(&dev->lock);
-
-               netif_carrier_on(dev->net);
-               if (netif_running(dev->net))
-                       eth_start(dev, GFP_ATOMIC);
-
-       /* on error, disable any endpoints  */
-       } else {
-               (void) usb_ep_disable(link->out_ep);
-fail1:
-               (void) usb_ep_disable(link->in_ep);
-       }
-fail0:
-       /* caller is responsible for cleanup on error */
-       if (result < 0)
-               return ERR_PTR(result);
-       return dev->net;
-}
-EXPORT_SYMBOL_GPL(gether_connect);
-
-/**
- * gether_disconnect - notify network layer that USB link is inactive
- * @link: the USB link, on which gether_connect() was called
- * Context: irqs blocked
- *
- * This is called to deactivate endpoints and let the network layer know
- * the connection went inactive ("no carrier").
- *
- * On return, the state is as if gether_connect() had never been called.
- * The endpoints are inactive, and accordingly without active USB I/O.
- * Pointers to endpoint descriptors and endpoint private data are nulled.
- */
-void gether_disconnect(struct gether *link)
-{
-       struct eth_dev          *dev = link->ioport;
-       struct usb_request      *req;
-
-       WARN_ON(!dev);
-       if (!dev)
-               return;
-
-       DBG(dev, "%s\n", __func__);
-
-       netif_stop_queue(dev->net);
-       netif_carrier_off(dev->net);
-
-       /* disable endpoints, forcing (synchronous) completion
-        * of all pending i/o.  then free the request objects
-        * and forget about the endpoints.
-        */
-       usb_ep_disable(link->in_ep);
-       spin_lock(&dev->req_lock);
-       while (!list_empty(&dev->tx_reqs)) {
-               req = container_of(dev->tx_reqs.next,
-                                       struct usb_request, list);
-               list_del(&req->list);
-
-               spin_unlock(&dev->req_lock);
-               usb_ep_free_request(link->in_ep, req);
-               spin_lock(&dev->req_lock);
-       }
-       spin_unlock(&dev->req_lock);
-       link->in_ep->driver_data = NULL;
-       link->in_ep->desc = NULL;
-
-       usb_ep_disable(link->out_ep);
-       spin_lock(&dev->req_lock);
-       while (!list_empty(&dev->rx_reqs)) {
-               req = container_of(dev->rx_reqs.next,
-                                       struct usb_request, list);
-               list_del(&req->list);
-
-               spin_unlock(&dev->req_lock);
-               usb_ep_free_request(link->out_ep, req);
-               spin_lock(&dev->req_lock);
-       }
-       spin_unlock(&dev->req_lock);
-       link->out_ep->driver_data = NULL;
-       link->out_ep->desc = NULL;
-
-       /* finish forgetting about this USB link episode */
-       dev->header_len = 0;
-       dev->unwrap = NULL;
-       dev->wrap = NULL;
-
-       spin_lock(&dev->lock);
-       dev->port_usb = NULL;
-       spin_unlock(&dev->lock);
-}
-EXPORT_SYMBOL_GPL(gether_disconnect);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h
deleted file mode 100644 (file)
index 334b389..0000000
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * u_ether.h -- interface to USB gadget "ethernet link" utilities
- *
- * Copyright (C) 2003-2005,2008 David Brownell
- * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
- * Copyright (C) 2008 Nokia Corporation
- *
- * 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.
- */
-
-#ifndef __U_ETHER_H
-#define __U_ETHER_H
-
-#include <linux/err.h>
-#include <linux/if_ether.h>
-#include <linux/usb/composite.h>
-#include <linux/usb/cdc.h>
-#include <linux/netdevice.h>
-
-#include "gadget_chips.h"
-
-#define QMULT_DEFAULT 5
-
-/*
- * dev_addr: initial value
- * changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx"
- * host_addr: this address is invisible to ifconfig
- */
-#define USB_ETHERNET_MODULE_PARAMETERS() \
-       static unsigned qmult = QMULT_DEFAULT;                          \
-       module_param(qmult, uint, S_IRUGO|S_IWUSR);                     \
-       MODULE_PARM_DESC(qmult, "queue length multiplier at high/super speed");\
-                                                                       \
-       static char *dev_addr;                                          \
-       module_param(dev_addr, charp, S_IRUGO);                         \
-       MODULE_PARM_DESC(dev_addr, "Device Ethernet Address");          \
-                                                                       \
-       static char *host_addr;                                         \
-       module_param(host_addr, charp, S_IRUGO);                        \
-       MODULE_PARM_DESC(host_addr, "Host Ethernet Address")
-
-struct eth_dev;
-
-/*
- * This represents the USB side of an "ethernet" link, managed by a USB
- * function which provides control and (maybe) framing.  Two functions
- * in different configurations could share the same ethernet link/netdev,
- * using different host interaction models.
- *
- * There is a current limitation that only one instance of this link may
- * be present in any given configuration.  When that's a problem, network
- * layer facilities can be used to package multiple logical links on this
- * single "physical" one.
- */
-struct gether {
-       struct usb_function             func;
-
-       /* updated by gether_{connect,disconnect} */
-       struct eth_dev                  *ioport;
-
-       /* endpoints handle full and/or high speeds */
-       struct usb_ep                   *in_ep;
-       struct usb_ep                   *out_ep;
-
-       bool                            is_zlp_ok;
-
-       u16                             cdc_filter;
-
-       /* hooks for added framing, as needed for RNDIS and EEM. */
-       u32                             header_len;
-       /* NCM requires fixed size bundles */
-       bool                            is_fixed;
-       u32                             fixed_out_len;
-       u32                             fixed_in_len;
-       bool                            supports_multi_frame;
-       struct sk_buff                  *(*wrap)(struct gether *port,
-                                               struct sk_buff *skb);
-       int                             (*unwrap)(struct gether *port,
-                                               struct sk_buff *skb,
-                                               struct sk_buff_head *list);
-
-       /* called on network open/close */
-       void                            (*open)(struct gether *);
-       void                            (*close)(struct gether *);
-};
-
-#define        DEFAULT_FILTER  (USB_CDC_PACKET_TYPE_BROADCAST \
-                       |USB_CDC_PACKET_TYPE_ALL_MULTICAST \
-                       |USB_CDC_PACKET_TYPE_PROMISCUOUS \
-                       |USB_CDC_PACKET_TYPE_DIRECTED)
-
-/* variant of gether_setup that allows customizing network device name */
-struct eth_dev *gether_setup_name(struct usb_gadget *g,
-               const char *dev_addr, const char *host_addr,
-               u8 ethaddr[ETH_ALEN], unsigned qmult, const char *netname);
-
-/* netdev setup/teardown as directed by the gadget driver */
-/* gether_setup - initialize one ethernet-over-usb link
- * @g: gadget to associated with these links
- * @ethaddr: NULL, or a buffer in which the ethernet address of the
- *     host side of the link is recorded
- * Context: may sleep
- *
- * This sets up the single network link that may be exported by a
- * gadget driver using this framework.  The link layer addresses are
- * set up using module parameters.
- *
- * Returns a eth_dev pointer on success, or an ERR_PTR on failure
- */
-static inline struct eth_dev *gether_setup(struct usb_gadget *g,
-               const char *dev_addr, const char *host_addr,
-               u8 ethaddr[ETH_ALEN], unsigned qmult)
-{
-       return gether_setup_name(g, dev_addr, host_addr, ethaddr, qmult, "usb");
-}
-
-/*
- * variant of gether_setup_default that allows customizing
- * network device name
- */
-struct net_device *gether_setup_name_default(const char *netname);
-
-/*
- * gether_register_netdev - register the net device
- * @net: net device to register
- *
- * Registers the net device associated with this ethernet-over-usb link
- *
- */
-int gether_register_netdev(struct net_device *net);
-
-/* gether_setup_default - initialize one ethernet-over-usb link
- * Context: may sleep
- *
- * This sets up the single network link that may be exported by a
- * gadget driver using this framework.  The link layer addresses
- * are set to random values.
- *
- * Returns negative errno, or zero on success
- */
-static inline struct net_device *gether_setup_default(void)
-{
-       return gether_setup_name_default("usb");
-}
-
-/**
- * gether_set_gadget - initialize one ethernet-over-usb link with a gadget
- * @net: device representing this link
- * @g: the gadget to initialize with
- *
- * This associates one ethernet-over-usb link with a gadget.
- */
-void gether_set_gadget(struct net_device *net, struct usb_gadget *g);
-
-/**
- * gether_set_dev_addr - initialize an ethernet-over-usb link with eth address
- * @net: device representing this link
- * @dev_addr: eth address of this device
- *
- * This sets the device-side Ethernet address of this ethernet-over-usb link
- * if dev_addr is correct.
- * Returns negative errno if the new address is incorrect.
- */
-int gether_set_dev_addr(struct net_device *net, const char *dev_addr);
-
-/**
- * gether_get_dev_addr - get an ethernet-over-usb link eth address
- * @net: device representing this link
- * @dev_addr: place to store device's eth address
- * @len: length of the @dev_addr buffer
- *
- * This gets the device-side Ethernet address of this ethernet-over-usb link.
- * Returns zero on success, else negative errno.
- */
-int gether_get_dev_addr(struct net_device *net, char *dev_addr, int len);
-
-/**
- * gether_set_host_addr - initialize an ethernet-over-usb link with host address
- * @net: device representing this link
- * @host_addr: eth address of the host
- *
- * This sets the host-side Ethernet address of this ethernet-over-usb link
- * if host_addr is correct.
- * Returns negative errno if the new address is incorrect.
- */
-int gether_set_host_addr(struct net_device *net, const char *host_addr);
-
-/**
- * gether_get_host_addr - get an ethernet-over-usb link host address
- * @net: device representing this link
- * @host_addr: place to store eth address of the host
- * @len: length of the @host_addr buffer
- *
- * This gets the host-side Ethernet address of this ethernet-over-usb link.
- * Returns zero on success, else negative errno.
- */
-int gether_get_host_addr(struct net_device *net, char *host_addr, int len);
-
-/**
- * gether_get_host_addr_cdc - get an ethernet-over-usb link host address
- * @net: device representing this link
- * @host_addr: place to store eth address of the host
- * @len: length of the @host_addr buffer
- *
- * This gets the CDC formatted host-side Ethernet address of this
- * ethernet-over-usb link.
- * Returns zero on success, else negative errno.
- */
-int gether_get_host_addr_cdc(struct net_device *net, char *host_addr, int len);
-
-/**
- * gether_get_host_addr_u8 - get an ethernet-over-usb link host address
- * @net: device representing this link
- * @host_mac: place to store the eth address of the host
- *
- * This gets the binary formatted host-side Ethernet address of this
- * ethernet-over-usb link.
- */
-void gether_get_host_addr_u8(struct net_device *net, u8 host_mac[ETH_ALEN]);
-
-/**
- * gether_set_qmult - initialize an ethernet-over-usb link with a multiplier
- * @net: device representing this link
- * @qmult: queue multiplier
- *
- * This sets the queue length multiplier of this ethernet-over-usb link.
- * For higher speeds use longer queues.
- */
-void gether_set_qmult(struct net_device *net, unsigned qmult);
-
-/**
- * gether_get_qmult - get an ethernet-over-usb link multiplier
- * @net: device representing this link
- *
- * This gets the queue length multiplier of this ethernet-over-usb link.
- */
-unsigned gether_get_qmult(struct net_device *net);
-
-/**
- * gether_get_ifname - get an ethernet-over-usb link interface name
- * @net: device representing this link
- * @name: place to store the interface name
- * @len: length of the @name buffer
- *
- * This gets the interface name of this ethernet-over-usb link.
- * Returns zero on success, else negative errno.
- */
-int gether_get_ifname(struct net_device *net, char *name, int len);
-
-void gether_cleanup(struct eth_dev *dev);
-
-/* connect/disconnect is handled by individual functions */
-struct net_device *gether_connect(struct gether *);
-void gether_disconnect(struct gether *);
-
-/* Some controllers can't support CDC Ethernet (ECM) ... */
-static inline bool can_support_ecm(struct usb_gadget *gadget)
-{
-       if (!gadget_supports_altsettings(gadget))
-               return false;
-
-       /* Everything else is *presumably* fine ... but this is a bit
-        * chancy, so be **CERTAIN** there are no hardware issues with
-        * your controller.  Add it above if it can't handle CDC.
-        */
-       return true;
-}
-
-#endif /* __U_ETHER_H */
diff --git a/drivers/usb/gadget/u_ether_configfs.h b/drivers/usb/gadget/u_ether_configfs.h
deleted file mode 100644 (file)
index bcbd301..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * u_ether_configfs.h
- *
- * Utility definitions for configfs support in USB Ethernet functions
- *
- * Copyright (c) 2013 Samsung Electronics Co., Ltd.
- *             http://www.samsung.com
- *
- * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef __U_ETHER_CONFIGFS_H
-#define __U_ETHER_CONFIGFS_H
-
-#define USB_ETHERNET_CONFIGFS_ITEM(_f_)                                        \
-       CONFIGFS_ATTR_STRUCT(f_##_f_##_opts);                           \
-       CONFIGFS_ATTR_OPS(f_##_f_##_opts);                              \
-                                                                       \
-       static void _f_##_attr_release(struct config_item *item)        \
-       {                                                               \
-               struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item);  \
-                                                                       \
-               usb_put_function_instance(&opts->func_inst);            \
-       }                                                               \
-                                                                       \
-       static struct configfs_item_operations _f_##_item_ops = {       \
-               .release        = _f_##_attr_release,                   \
-               .show_attribute = f_##_f_##_opts_attr_show,             \
-               .store_attribute = f_##_f_##_opts_attr_store,           \
-       }
-
-#define USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(_f_)                  \
-       static ssize_t _f_##_opts_dev_addr_show(struct f_##_f_##_opts *opts, \
-                                               char *page)             \
-       {                                                               \
-               int result;                                             \
-                                                                       \
-               mutex_lock(&opts->lock);                                \
-               result = gether_get_dev_addr(opts->net, page, PAGE_SIZE); \
-               mutex_unlock(&opts->lock);                              \
-                                                                       \
-               return result;                                          \
-       }                                                               \
-                                                                       \
-       static ssize_t _f_##_opts_dev_addr_store(struct f_##_f_##_opts *opts, \
-                                                const char *page, size_t len)\
-       {                                                               \
-               int ret;                                                \
-                                                                       \
-               mutex_lock(&opts->lock);                                \
-               if (opts->refcnt) {                                     \
-                       mutex_unlock(&opts->lock);                      \
-                       return -EBUSY;                                  \
-               }                                                       \
-                                                                       \
-               ret = gether_set_dev_addr(opts->net, page);             \
-               mutex_unlock(&opts->lock);                              \
-               if (!ret)                                               \
-                       ret = len;                                      \
-               return ret;                                             \
-       }                                                               \
-                                                                       \
-       static struct f_##_f_##_opts_attribute f_##_f_##_opts_dev_addr = \
-               __CONFIGFS_ATTR(dev_addr, S_IRUGO | S_IWUSR,            \
-                               _f_##_opts_dev_addr_show,               \
-                               _f_##_opts_dev_addr_store)
-
-#define USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(_f_)                 \
-       static ssize_t _f_##_opts_host_addr_show(struct f_##_f_##_opts *opts, \
-                                                char *page)            \
-       {                                                               \
-               int result;                                             \
-                                                                       \
-               mutex_lock(&opts->lock);                                \
-               result = gether_get_host_addr(opts->net, page, PAGE_SIZE); \
-               mutex_unlock(&opts->lock);                              \
-                                                                       \
-               return result;                                          \
-       }                                                               \
-                                                                       \
-       static ssize_t _f_##_opts_host_addr_store(struct f_##_f_##_opts *opts, \
-                                                 const char *page, size_t len)\
-       {                                                               \
-               int ret;                                                \
-                                                                       \
-               mutex_lock(&opts->lock);                                \
-               if (opts->refcnt) {                                     \
-                       mutex_unlock(&opts->lock);                      \
-                       return -EBUSY;                                  \
-               }                                                       \
-                                                                       \
-               ret = gether_set_host_addr(opts->net, page);            \
-               mutex_unlock(&opts->lock);                              \
-               if (!ret)                                               \
-                       ret = len;                                      \
-               return ret;                                             \
-       }                                                               \
-                                                                       \
-       static struct f_##_f_##_opts_attribute f_##_f_##_opts_host_addr = \
-               __CONFIGFS_ATTR(host_addr, S_IRUGO | S_IWUSR,           \
-                               _f_##_opts_host_addr_show,              \
-                               _f_##_opts_host_addr_store)
-
-#define USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(_f_)                     \
-       static ssize_t _f_##_opts_qmult_show(struct f_##_f_##_opts *opts, \
-                                            char *page)                \
-       {                                                               \
-               unsigned qmult;                                         \
-                                                                       \
-               mutex_lock(&opts->lock);                                \
-               qmult = gether_get_qmult(opts->net);                    \
-               mutex_unlock(&opts->lock);                              \
-               return sprintf(page, "%d", qmult);                      \
-       }                                                               \
-                                                                       \
-       static ssize_t _f_##_opts_qmult_store(struct f_##_f_##_opts *opts, \
-                                             const char *page, size_t len)\
-       {                                                               \
-               u8 val;                                                 \
-               int ret;                                                \
-                                                                       \
-               mutex_lock(&opts->lock);                                \
-               if (opts->refcnt) {                                     \
-                       ret = -EBUSY;                                   \
-                       goto out;                                       \
-               }                                                       \
-                                                                       \
-               ret = kstrtou8(page, 0, &val);                          \
-               if (ret)                                                \
-                       goto out;                                       \
-                                                                       \
-               gether_set_qmult(opts->net, val);                       \
-               ret = len;                                              \
-out:                                                                   \
-               mutex_unlock(&opts->lock);                              \
-               return ret;                                             \
-       }                                                               \
-                                                                       \
-       static struct f_##_f_##_opts_attribute f_##_f_##_opts_qmult =   \
-               __CONFIGFS_ATTR(qmult, S_IRUGO | S_IWUSR,               \
-                               _f_##_opts_qmult_show,          \
-                               _f_##_opts_qmult_store)
-
-#define USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(_f_)                    \
-       static ssize_t _f_##_opts_ifname_show(struct f_##_f_##_opts *opts, \
-                                             char *page)               \
-       {                                                               \
-               int ret;                                                \
-                                                                       \
-               mutex_lock(&opts->lock);                                \
-               ret = gether_get_ifname(opts->net, page, PAGE_SIZE);    \
-               mutex_unlock(&opts->lock);                              \
-                                                                       \
-               return ret;                                             \
-       }                                                               \
-                                                                       \
-       static struct f_##_f_##_opts_attribute f_##_f_##_opts_ifname =  \
-               __CONFIGFS_ATTR_RO(ifname, _f_##_opts_ifname_show)
-
-#endif /* __U_ETHER_CONFIGFS_H */
diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/u_fs.h
deleted file mode 100644 (file)
index 63d6e71..0000000
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * u_fs.h
- *
- * Utility definitions for the FunctionFS
- *
- * Copyright (c) 2013 Samsung Electronics Co., Ltd.
- *             http://www.samsung.com
- *
- * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef U_FFS_H
-#define U_FFS_H
-
-#include <linux/usb/composite.h>
-#include <linux/list.h>
-#include <linux/mutex.h>
-
-#ifdef VERBOSE_DEBUG
-#ifndef pr_vdebug
-#  define pr_vdebug pr_debug
-#endif /* pr_vdebug */
-#  define ffs_dump_mem(prefix, ptr, len) \
-       print_hex_dump_bytes(pr_fmt(prefix ": "), DUMP_PREFIX_NONE, ptr, len)
-#else
-#ifndef pr_vdebug
-#  define pr_vdebug(...)                 do { } while (0)
-#endif /* pr_vdebug */
-#  define ffs_dump_mem(prefix, ptr, len) do { } while (0)
-#endif /* VERBOSE_DEBUG */
-
-#define ENTER()    pr_vdebug("%s()\n", __func__)
-
-struct f_fs_opts;
-
-struct ffs_dev {
-       const char *name;
-       bool name_allocated;
-       bool mounted;
-       bool desc_ready;
-       bool single;
-       struct ffs_data *ffs_data;
-       struct f_fs_opts *opts;
-       struct list_head entry;
-
-       int (*ffs_ready_callback)(struct ffs_data *ffs);
-       void (*ffs_closed_callback)(struct ffs_data *ffs);
-       void *(*ffs_acquire_dev_callback)(struct ffs_dev *dev);
-       void (*ffs_release_dev_callback)(struct ffs_dev *dev);
-};
-
-extern struct mutex ffs_lock;
-
-static inline void ffs_dev_lock(void)
-{
-       mutex_lock(&ffs_lock);
-}
-
-static inline void ffs_dev_unlock(void)
-{
-       mutex_unlock(&ffs_lock);
-}
-
-int ffs_name_dev(struct ffs_dev *dev, const char *name);
-int ffs_single_dev(struct ffs_dev *dev);
-
-struct ffs_epfile;
-struct ffs_function;
-
-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 first 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 epfiles 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_CANCELLED
-};
-
-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 endpoint related structures (basically
-        * usb_ep_queue(), usb_ep_dequeue(), etc. calls) except for
-        * endpoint zero.
-        */
-       spinlock_t                      eps_lock;
-
-       /*
-        * XXX REVISIT do we need our own request? Since we are not
-        * handling setup requests immediately 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 that 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 */
-
-       /* reference counter */
-       atomic_t                        ref;
-       /* how many files are opened (EP0 and others) */
-       atomic_t                        opened;
-
-       /* EP0 state */
-       enum ffs_state                  state;
-
-       /*
-        * Possible transitions:
-        * + 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_CANCELLED -- P: ev.waitq.lock
-        * + FFS_SETUP_CANCELLED -> FFS_NO_SETUP        -- cmpxchg
-        *
-        * This field should never be accessed directly and instead
-        * ffs_setup_state_clear_cancelled function should be used.
-        */
-       enum ffs_setup_state            setup_state;
-
-       /* 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.
-        * Intended 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() */
-       /*
-        * raw_descs is what you kfree, real_descs points inside of raw_descs,
-        * where full speed, high speed and super speed descriptors start.
-        * real_descs_length is the length of all those descriptors.
-        */
-       const void                      *raw_descs_data;
-       const void                      *raw_descs;
-       unsigned                        raw_descs_length;
-       unsigned                        fs_descs_count;
-       unsigned                        hs_descs_count;
-       unsigned                        ss_descs_count;
-       unsigned                        ms_os_descs_count;
-       unsigned                        ms_os_descs_ext_prop_count;
-       unsigned                        ms_os_descs_ext_prop_name_len;
-       unsigned                        ms_os_descs_ext_prop_data_len;
-       void                            *ms_os_descs_ext_prop_avail;
-       void                            *ms_os_descs_ext_prop_name_avail;
-       void                            *ms_os_descs_ext_prop_data_avail;
-
-       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;
-               kuid_t                          uid;
-               kgid_t                          gid;
-       }                               file_perms;
-
-       /*
-        * The endpoint files, filled by ffs_epfiles_create(),
-        * destroyed by ffs_epfiles_destroy().
-        */
-       struct ffs_epfile               *epfiles;
-};
-
-
-struct f_fs_opts {
-       struct usb_function_instance    func_inst;
-       struct ffs_dev                  *dev;
-       unsigned                        refcnt;
-       bool                            no_configfs;
-};
-
-static inline struct f_fs_opts *to_f_fs_opts(struct usb_function_instance *fi)
-{
-       return container_of(fi, struct f_fs_opts, func_inst);
-}
-
-#endif /* U_FFS_H */
diff --git a/drivers/usb/gadget/u_gether.h b/drivers/usb/gadget/u_gether.h
deleted file mode 100644 (file)
index d407842..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * u_gether.h
- *
- * Utility definitions for the subset function
- *
- * Copyright (c) 2013 Samsung Electronics Co., Ltd.
- *             http://www.samsung.com
- *
- * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef U_GETHER_H
-#define U_GETHER_H
-
-#include <linux/usb/composite.h>
-
-struct f_gether_opts {
-       struct usb_function_instance    func_inst;
-       struct net_device               *net;
-       bool                            bound;
-
-       /*
-        * Read/write access to configfs attributes is handled by configfs.
-        *
-        * This is to protect the data from concurrent access by read/write
-        * and create symlink/remove symlink.
-        */
-       struct mutex                    lock;
-       int                             refcnt;
-};
-
-#endif /* U_GETHER_H */
diff --git a/drivers/usb/gadget/u_ncm.h b/drivers/usb/gadget/u_ncm.h
deleted file mode 100644 (file)
index ce0f3a7..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * u_ncm.h
- *
- * Utility definitions for the ncm function
- *
- * Copyright (c) 2013 Samsung Electronics Co., Ltd.
- *             http://www.samsung.com
- *
- * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef U_NCM_H
-#define U_NCM_H
-
-#include <linux/usb/composite.h>
-
-struct f_ncm_opts {
-       struct usb_function_instance    func_inst;
-       struct net_device               *net;
-       bool                            bound;
-
-       /*
-        * Read/write access to configfs attributes is handled by configfs.
-        *
-        * This is to protect the data from concurrent access by read/write
-        * and create symlink/remove symlink.
-        */
-       struct mutex                    lock;
-       int                             refcnt;
-};
-
-#endif /* U_NCM_H */
diff --git a/drivers/usb/gadget/u_phonet.h b/drivers/usb/gadget/u_phonet.h
deleted file mode 100644 (file)
index 98ced18..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * u_phonet.h - interface to Phonet
- *
- * Copyright (C) 2007-2008 by Nokia Corporation
- *
- * This software is distributed under the terms of the GNU General
- * Public License ("GPL") as published by the Free Software Foundation,
- * either version 2 of that License or (at your option) any later version.
- */
-
-#ifndef __U_PHONET_H
-#define __U_PHONET_H
-
-#include <linux/usb/composite.h>
-#include <linux/usb/cdc.h>
-
-struct f_phonet_opts {
-       struct usb_function_instance func_inst;
-       bool bound;
-       struct net_device *net;
-};
-
-struct net_device *gphonet_setup_default(void);
-void gphonet_set_gadget(struct net_device *net, struct usb_gadget *g);
-int gphonet_register_netdev(struct net_device *net);
-int phonet_bind_config(struct usb_configuration *c, struct net_device *dev);
-void gphonet_cleanup(struct net_device *dev);
-
-#endif /* __U_PHONET_H */
diff --git a/drivers/usb/gadget/u_rndis.h b/drivers/usb/gadget/u_rndis.h
deleted file mode 100644 (file)
index e902aa4..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * u_rndis.h
- *
- * Utility definitions for the subset function
- *
- * Copyright (c) 2013 Samsung Electronics Co., Ltd.
- *             http://www.samsung.com
- *
- * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef U_RNDIS_H
-#define U_RNDIS_H
-
-#include <linux/usb/composite.h>
-
-struct f_rndis_opts {
-       struct usb_function_instance    func_inst;
-       u32                             vendor_id;
-       const char                      *manufacturer;
-       struct net_device               *net;
-       bool                            bound;
-       bool                            borrowed_net;
-
-       struct usb_os_desc              rndis_os_desc;
-       char                            rndis_ext_compat_id[16];
-
-       /*
-        * Read/write access to configfs attributes is handled by configfs.
-        *
-        * This is to protect the data from concurrent access by read/write
-        * and create symlink/remove symlink.
-        */
-       struct mutex                    lock;
-       int                             refcnt;
-};
-
-int rndis_init(void);
-void rndis_exit(void);
-void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net);
-
-#endif /* U_RNDIS_H */
diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c
deleted file mode 100644 (file)
index ad0aca8..0000000
+++ /dev/null
@@ -1,1347 +0,0 @@
-/*
- * u_serial.c - utilities for USB gadget "serial port"/TTY support
- *
- * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
- * Copyright (C) 2008 David Brownell
- * Copyright (C) 2008 by Nokia Corporation
- *
- * This code also borrows from usbserial.c, which is
- * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
- * Copyright (C) 2000 Peter Berger (pberger@brimson.com)
- * Copyright (C) 2000 Al Borchers (alborchers@steinerpoint.com)
- *
- * This software is distributed under the terms of the GNU General
- * Public License ("GPL") as published by the Free Software Foundation,
- * either version 2 of that License or (at your option) any later version.
- */
-
-/* #define VERBOSE_DEBUG */
-
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/delay.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/slab.h>
-#include <linux/export.h>
-#include <linux/module.h>
-
-#include "u_serial.h"
-
-
-/*
- * This component encapsulates the TTY layer glue needed to provide basic
- * "serial port" functionality through the USB gadget stack.  Each such
- * port is exposed through a /dev/ttyGS* node.
- *
- * After this module has been loaded, the individual TTY port can be requested
- * (gserial_alloc_line()) and it will stay available until they are removed
- * (gserial_free_line()). Each one may be connected to a USB function
- * (gserial_connect), or disconnected (with gserial_disconnect) when the USB
- * host issues a config change event. Data can only flow when the port is
- * connected to the host.
- *
- * A given TTY port can be made available in multiple configurations.
- * For example, each one might expose a ttyGS0 node which provides a
- * login application.  In one case that might use CDC ACM interface 0,
- * while another configuration might use interface 3 for that.  The
- * work to handle that (including descriptor management) is not part
- * of this component.
- *
- * Configurations may expose more than one TTY port.  For example, if
- * ttyGS0 provides login service, then ttyGS1 might provide dialer access
- * for a telephone or fax link.  And ttyGS2 might be something that just
- * needs a simple byte stream interface for some messaging protocol that
- * is managed in userspace ... OBEX, PTP, and MTP have been mentioned.
- */
-
-#define PREFIX "ttyGS"
-
-/*
- * gserial is the lifecycle interface, used by USB functions
- * gs_port is the I/O nexus, used by the tty driver
- * tty_struct links to the tty/filesystem framework
- *
- * gserial <---> gs_port ... links will be null when the USB link is
- * inactive; managed by gserial_{connect,disconnect}().  each gserial
- * instance can wrap its own USB control protocol.
- *     gserial->ioport == usb_ep->driver_data ... gs_port
- *     gs_port->port_usb ... gserial
- *
- * gs_port <---> tty_struct ... links will be null when the TTY file
- * isn't opened; managed by gs_open()/gs_close()
- *     gserial->port_tty ... tty_struct
- *     tty_struct->driver_data ... gserial
- */
-
-/* RX and TX queues can buffer QUEUE_SIZE packets before they hit the
- * next layer of buffering.  For TX that's a circular buffer; for RX
- * consider it a NOP.  A third layer is provided by the TTY code.
- */
-#define QUEUE_SIZE             16
-#define WRITE_BUF_SIZE         8192            /* TX only */
-
-/* circular buffer */
-struct gs_buf {
-       unsigned                buf_size;
-       char                    *buf_buf;
-       char                    *buf_get;
-       char                    *buf_put;
-};
-
-/*
- * The port structure holds info for each port, one for each minor number
- * (and thus for each /dev/ node).
- */
-struct gs_port {
-       struct tty_port         port;
-       spinlock_t              port_lock;      /* guard port_* access */
-
-       struct gserial          *port_usb;
-
-       bool                    openclose;      /* open/close in progress */
-       u8                      port_num;
-
-       struct list_head        read_pool;
-       int read_started;
-       int read_allocated;
-       struct list_head        read_queue;
-       unsigned                n_read;
-       struct tasklet_struct   push;
-
-       struct list_head        write_pool;
-       int write_started;
-       int write_allocated;
-       struct gs_buf           port_write_buf;
-       wait_queue_head_t       drain_wait;     /* wait while writes drain */
-
-       /* REVISIT this state ... */
-       struct usb_cdc_line_coding port_line_coding;    /* 8-N-1 etc */
-};
-
-static struct portmaster {
-       struct mutex    lock;                   /* protect open/close */
-       struct gs_port  *port;
-} ports[MAX_U_SERIAL_PORTS];
-
-#define GS_CLOSE_TIMEOUT               15              /* seconds */
-
-
-
-#ifdef VERBOSE_DEBUG
-#ifndef pr_vdebug
-#define pr_vdebug(fmt, arg...) \
-       pr_debug(fmt, ##arg)
-#endif /* pr_vdebug */
-#else
-#ifndef pr_vdebug
-#define pr_vdebug(fmt, arg...) \
-       ({ if (0) pr_debug(fmt, ##arg); })
-#endif /* pr_vdebug */
-#endif
-
-/*-------------------------------------------------------------------------*/
-
-/* Circular Buffer */
-
-/*
- * gs_buf_alloc
- *
- * Allocate a circular buffer and all associated memory.
- */
-static int gs_buf_alloc(struct gs_buf *gb, unsigned size)
-{
-       gb->buf_buf = kmalloc(size, GFP_KERNEL);
-       if (gb->buf_buf == NULL)
-               return -ENOMEM;
-
-       gb->buf_size = size;
-       gb->buf_put = gb->buf_buf;
-       gb->buf_get = gb->buf_buf;
-
-       return 0;
-}
-
-/*
- * gs_buf_free
- *
- * Free the buffer and all associated memory.
- */
-static void gs_buf_free(struct gs_buf *gb)
-{
-       kfree(gb->buf_buf);
-       gb->buf_buf = NULL;
-}
-
-/*
- * gs_buf_clear
- *
- * Clear out all data in the circular buffer.
- */
-static void gs_buf_clear(struct gs_buf *gb)
-{
-       gb->buf_get = gb->buf_put;
-       /* equivalent to a get of all data available */
-}
-
-/*
- * gs_buf_data_avail
- *
- * Return the number of bytes of data written into the circular
- * buffer.
- */
-static unsigned gs_buf_data_avail(struct gs_buf *gb)
-{
-       return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size;
-}
-
-/*
- * gs_buf_space_avail
- *
- * Return the number of bytes of space available in the circular
- * buffer.
- */
-static unsigned gs_buf_space_avail(struct gs_buf *gb)
-{
-       return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size;
-}
-
-/*
- * gs_buf_put
- *
- * Copy data data from a user buffer and put it into the circular buffer.
- * Restrict to the amount of space available.
- *
- * Return the number of bytes copied.
- */
-static unsigned
-gs_buf_put(struct gs_buf *gb, const char *buf, unsigned count)
-{
-       unsigned len;
-
-       len  = gs_buf_space_avail(gb);
-       if (count > len)
-               count = len;
-
-       if (count == 0)
-               return 0;
-
-       len = gb->buf_buf + gb->buf_size - gb->buf_put;
-       if (count > len) {
-               memcpy(gb->buf_put, buf, len);
-               memcpy(gb->buf_buf, buf+len, count - len);
-               gb->buf_put = gb->buf_buf + count - len;
-       } else {
-               memcpy(gb->buf_put, buf, count);
-               if (count < len)
-                       gb->buf_put += count;
-               else /* count == len */
-                       gb->buf_put = gb->buf_buf;
-       }
-
-       return count;
-}
-
-/*
- * gs_buf_get
- *
- * Get data from the circular buffer and copy to the given buffer.
- * Restrict to the amount of data available.
- *
- * Return the number of bytes copied.
- */
-static unsigned
-gs_buf_get(struct gs_buf *gb, char *buf, unsigned count)
-{
-       unsigned len;
-
-       len = gs_buf_data_avail(gb);
-       if (count > len)
-               count = len;
-
-       if (count == 0)
-               return 0;
-
-       len = gb->buf_buf + gb->buf_size - gb->buf_get;
-       if (count > len) {
-               memcpy(buf, gb->buf_get, len);
-               memcpy(buf+len, gb->buf_buf, count - len);
-               gb->buf_get = gb->buf_buf + count - len;
-       } else {
-               memcpy(buf, gb->buf_get, count);
-               if (count < len)
-                       gb->buf_get += count;
-               else /* count == len */
-                       gb->buf_get = gb->buf_buf;
-       }
-
-       return count;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* I/O glue between TTY (upper) and USB function (lower) driver layers */
-
-/*
- * gs_alloc_req
- *
- * Allocate a usb_request and its buffer.  Returns a pointer to the
- * usb_request or NULL if there is an error.
- */
-struct usb_request *
-gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags)
-{
-       struct usb_request *req;
-
-       req = usb_ep_alloc_request(ep, kmalloc_flags);
-
-       if (req != NULL) {
-               req->length = len;
-               req->buf = kmalloc(len, kmalloc_flags);
-               if (req->buf == NULL) {
-                       usb_ep_free_request(ep, req);
-                       return NULL;
-               }
-       }
-
-       return req;
-}
-EXPORT_SYMBOL_GPL(gs_alloc_req);
-
-/*
- * gs_free_req
- *
- * Free a usb_request and its buffer.
- */
-void gs_free_req(struct usb_ep *ep, struct usb_request *req)
-{
-       kfree(req->buf);
-       usb_ep_free_request(ep, req);
-}
-EXPORT_SYMBOL_GPL(gs_free_req);
-
-/*
- * gs_send_packet
- *
- * If there is data to send, a packet is built in the given
- * buffer and the size is returned.  If there is no data to
- * send, 0 is returned.
- *
- * Called with port_lock held.
- */
-static unsigned
-gs_send_packet(struct gs_port *port, char *packet, unsigned size)
-{
-       unsigned len;
-
-       len = gs_buf_data_avail(&port->port_write_buf);
-       if (len < size)
-               size = len;
-       if (size != 0)
-               size = gs_buf_get(&port->port_write_buf, packet, size);
-       return size;
-}
-
-/*
- * gs_start_tx
- *
- * This function finds available write requests, calls
- * gs_send_packet to fill these packets with data, and
- * continues until either there are no more write requests
- * available or no more data to send.  This function is
- * run whenever data arrives or write requests are available.
- *
- * Context: caller owns port_lock; port_usb is non-null.
- */
-static int gs_start_tx(struct gs_port *port)
-/*
-__releases(&port->port_lock)
-__acquires(&port->port_lock)
-*/
-{
-       struct list_head        *pool = &port->write_pool;
-       struct usb_ep           *in = port->port_usb->in;
-       int                     status = 0;
-       bool                    do_tty_wake = false;
-
-       while (!list_empty(pool)) {
-               struct usb_request      *req;
-               int                     len;
-
-               if (port->write_started >= QUEUE_SIZE)
-                       break;
-
-               req = list_entry(pool->next, struct usb_request, list);
-               len = gs_send_packet(port, req->buf, in->maxpacket);
-               if (len == 0) {
-                       wake_up_interruptible(&port->drain_wait);
-                       break;
-               }
-               do_tty_wake = true;
-
-               req->length = len;
-               list_del(&req->list);
-               req->zero = (gs_buf_data_avail(&port->port_write_buf) == 0);
-
-               pr_vdebug(PREFIX "%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n",
-                               port->port_num, len, *((u8 *)req->buf),
-                               *((u8 *)req->buf+1), *((u8 *)req->buf+2));
-
-               /* Drop lock while we call out of driver; completions
-                * could be issued while we do so.  Disconnection may
-                * happen too; maybe immediately before we queue this!
-                *
-                * NOTE that we may keep sending data for a while after
-                * the TTY closed (dev->ioport->port_tty is NULL).
-                */
-               spin_unlock(&port->port_lock);
-               status = usb_ep_queue(in, req, GFP_ATOMIC);
-               spin_lock(&port->port_lock);
-
-               if (status) {
-                       pr_debug("%s: %s %s err %d\n",
-                                       __func__, "queue", in->name, status);
-                       list_add(&req->list, pool);
-                       break;
-               }
-
-               port->write_started++;
-
-               /* abort immediately after disconnect */
-               if (!port->port_usb)
-                       break;
-       }
-
-       if (do_tty_wake && port->port.tty)
-               tty_wakeup(port->port.tty);
-       return status;
-}
-
-/*
- * Context: caller owns port_lock, and port_usb is set
- */
-static unsigned gs_start_rx(struct gs_port *port)
-/*
-__releases(&port->port_lock)
-__acquires(&port->port_lock)
-*/
-{
-       struct list_head        *pool = &port->read_pool;
-       struct usb_ep           *out = port->port_usb->out;
-
-       while (!list_empty(pool)) {
-               struct usb_request      *req;
-               int                     status;
-               struct tty_struct       *tty;
-
-               /* no more rx if closed */
-               tty = port->port.tty;
-               if (!tty)
-                       break;
-
-               if (port->read_started >= QUEUE_SIZE)
-                       break;
-
-               req = list_entry(pool->next, struct usb_request, list);
-               list_del(&req->list);
-               req->length = out->maxpacket;
-
-               /* drop lock while we call out; the controller driver
-                * may need to call us back (e.g. for disconnect)
-                */
-               spin_unlock(&port->port_lock);
-               status = usb_ep_queue(out, req, GFP_ATOMIC);
-               spin_lock(&port->port_lock);
-
-               if (status) {
-                       pr_debug("%s: %s %s err %d\n",
-                                       __func__, "queue", out->name, status);
-                       list_add(&req->list, pool);
-                       break;
-               }
-               port->read_started++;
-
-               /* abort immediately after disconnect */
-               if (!port->port_usb)
-                       break;
-       }
-       return port->read_started;
-}
-
-/*
- * RX tasklet takes data out of the RX queue and hands it up to the TTY
- * layer until it refuses to take any more data (or is throttled back).
- * Then it issues reads for any further data.
- *
- * If the RX queue becomes full enough that no usb_request is queued,
- * the OUT endpoint may begin NAKing as soon as its FIFO fills up.
- * So QUEUE_SIZE packets plus however many the FIFO holds (usually two)
- * can be buffered before the TTY layer's buffers (currently 64 KB).
- */
-static void gs_rx_push(unsigned long _port)
-{
-       struct gs_port          *port = (void *)_port;
-       struct tty_struct       *tty;
-       struct list_head        *queue = &port->read_queue;
-       bool                    disconnect = false;
-       bool                    do_push = false;
-
-       /* hand any queued data to the tty */
-       spin_lock_irq(&port->port_lock);
-       tty = port->port.tty;
-       while (!list_empty(queue)) {
-               struct usb_request      *req;
-
-               req = list_first_entry(queue, struct usb_request, list);
-
-               /* leave data queued if tty was rx throttled */
-               if (tty && test_bit(TTY_THROTTLED, &tty->flags))
-                       break;
-
-               switch (req->status) {
-               case -ESHUTDOWN:
-                       disconnect = true;
-                       pr_vdebug(PREFIX "%d: shutdown\n", port->port_num);
-                       break;
-
-               default:
-                       /* presumably a transient fault */
-                       pr_warning(PREFIX "%d: unexpected RX status %d\n",
-                                       port->port_num, req->status);
-                       /* FALLTHROUGH */
-               case 0:
-                       /* normal completion */
-                       break;
-               }
-
-               /* push data to (open) tty */
-               if (req->actual) {
-                       char            *packet = req->buf;
-                       unsigned        size = req->actual;
-                       unsigned        n;
-                       int             count;
-
-                       /* we may have pushed part of this packet already... */
-                       n = port->n_read;
-                       if (n) {
-                               packet += n;
-                               size -= n;
-                       }
-
-                       count = tty_insert_flip_string(&port->port, packet,
-                                       size);
-                       if (count)
-                               do_push = true;
-                       if (count != size) {
-                               /* stop pushing; TTY layer can't handle more */
-                               port->n_read += count;
-                               pr_vdebug(PREFIX "%d: rx block %d/%d\n",
-                                               port->port_num,
-                                               count, req->actual);
-                               break;
-                       }
-                       port->n_read = 0;
-               }
-
-               list_move(&req->list, &port->read_pool);
-               port->read_started--;
-       }
-
-       /* Push from tty to ldisc; this is handled by a workqueue,
-        * so we won't get callbacks and can hold port_lock
-        */
-       if (do_push)
-               tty_flip_buffer_push(&port->port);
-
-
-       /* We want our data queue to become empty ASAP, keeping data
-        * in the tty and ldisc (not here).  If we couldn't push any
-        * this time around, there may be trouble unless there's an
-        * implicit tty_unthrottle() call on its way...
-        *
-        * REVISIT we should probably add a timer to keep the tasklet
-        * from starving ... but it's not clear that case ever happens.
-        */
-       if (!list_empty(queue) && tty) {
-               if (!test_bit(TTY_THROTTLED, &tty->flags)) {
-                       if (do_push)
-                               tasklet_schedule(&port->push);
-                       else
-                               pr_warning(PREFIX "%d: RX not scheduled?\n",
-                                       port->port_num);
-               }
-       }
-
-       /* If we're still connected, refill the USB RX queue. */
-       if (!disconnect && port->port_usb)
-               gs_start_rx(port);
-
-       spin_unlock_irq(&port->port_lock);
-}
-
-static void gs_read_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       struct gs_port  *port = ep->driver_data;
-
-       /* Queue all received data until the tty layer is ready for it. */
-       spin_lock(&port->port_lock);
-       list_add_tail(&req->list, &port->read_queue);
-       tasklet_schedule(&port->push);
-       spin_unlock(&port->port_lock);
-}
-
-static void gs_write_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       struct gs_port  *port = ep->driver_data;
-
-       spin_lock(&port->port_lock);
-       list_add(&req->list, &port->write_pool);
-       port->write_started--;
-
-       switch (req->status) {
-       default:
-               /* presumably a transient fault */
-               pr_warning("%s: unexpected %s status %d\n",
-                               __func__, ep->name, req->status);
-               /* FALL THROUGH */
-       case 0:
-               /* normal completion */
-               gs_start_tx(port);
-               break;
-
-       case -ESHUTDOWN:
-               /* disconnect */
-               pr_vdebug("%s: %s shutdown\n", __func__, ep->name);
-               break;
-       }
-
-       spin_unlock(&port->port_lock);
-}
-
-static void gs_free_requests(struct usb_ep *ep, struct list_head *head,
-                                                        int *allocated)
-{
-       struct usb_request      *req;
-
-       while (!list_empty(head)) {
-               req = list_entry(head->next, struct usb_request, list);
-               list_del(&req->list);
-               gs_free_req(ep, req);
-               if (allocated)
-                       (*allocated)--;
-       }
-}
-
-static int gs_alloc_requests(struct usb_ep *ep, struct list_head *head,
-               void (*fn)(struct usb_ep *, struct usb_request *),
-               int *allocated)
-{
-       int                     i;
-       struct usb_request      *req;
-       int n = allocated ? QUEUE_SIZE - *allocated : QUEUE_SIZE;
-
-       /* Pre-allocate up to QUEUE_SIZE transfers, but if we can't
-        * do quite that many this time, don't fail ... we just won't
-        * be as speedy as we might otherwise be.
-        */
-       for (i = 0; i < n; i++) {
-               req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC);
-               if (!req)
-                       return list_empty(head) ? -ENOMEM : 0;
-               req->complete = fn;
-               list_add_tail(&req->list, head);
-               if (allocated)
-                       (*allocated)++;
-       }
-       return 0;
-}
-
-/**
- * gs_start_io - start USB I/O streams
- * @dev: encapsulates endpoints to use
- * Context: holding port_lock; port_tty and port_usb are non-null
- *
- * We only start I/O when something is connected to both sides of
- * this port.  If nothing is listening on the host side, we may
- * be pointlessly filling up our TX buffers and FIFO.
- */
-static int gs_start_io(struct gs_port *port)
-{
-       struct list_head        *head = &port->read_pool;
-       struct usb_ep           *ep = port->port_usb->out;
-       int                     status;
-       unsigned                started;
-
-       /* Allocate RX and TX I/O buffers.  We can't easily do this much
-        * earlier (with GFP_KERNEL) because the requests are coupled to
-        * endpoints, as are the packet sizes we'll be using.  Different
-        * configurations may use different endpoints with a given port;
-        * and high speed vs full speed changes packet sizes too.
-        */
-       status = gs_alloc_requests(ep, head, gs_read_complete,
-               &port->read_allocated);
-       if (status)
-               return status;
-
-       status = gs_alloc_requests(port->port_usb->in, &port->write_pool,
-                       gs_write_complete, &port->write_allocated);
-       if (status) {
-               gs_free_requests(ep, head, &port->read_allocated);
-               return status;
-       }
-
-       /* queue read requests */
-       port->n_read = 0;
-       started = gs_start_rx(port);
-
-       /* unblock any pending writes into our circular buffer */
-       if (started) {
-               tty_wakeup(port->port.tty);
-       } else {
-               gs_free_requests(ep, head, &port->read_allocated);
-               gs_free_requests(port->port_usb->in, &port->write_pool,
-                       &port->write_allocated);
-               status = -EIO;
-       }
-
-       return status;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* TTY Driver */
-
-/*
- * gs_open sets up the link between a gs_port and its associated TTY.
- * That link is broken *only* by TTY close(), and all driver methods
- * know that.
- */
-static int gs_open(struct tty_struct *tty, struct file *file)
-{
-       int             port_num = tty->index;
-       struct gs_port  *port;
-       int             status;
-
-       do {
-               mutex_lock(&ports[port_num].lock);
-               port = ports[port_num].port;
-               if (!port)
-                       status = -ENODEV;
-               else {
-                       spin_lock_irq(&port->port_lock);
-
-                       /* already open?  Great. */
-                       if (port->port.count) {
-                               status = 0;
-                               port->port.count++;
-
-                       /* currently opening/closing? wait ... */
-                       } else if (port->openclose) {
-                               status = -EBUSY;
-
-                       /* ... else we do the work */
-                       } else {
-                               status = -EAGAIN;
-                               port->openclose = true;
-                       }
-                       spin_unlock_irq(&port->port_lock);
-               }
-               mutex_unlock(&ports[port_num].lock);
-
-               switch (status) {
-               default:
-                       /* fully handled */
-                       return status;
-               case -EAGAIN:
-                       /* must do the work */
-                       break;
-               case -EBUSY:
-                       /* wait for EAGAIN task to finish */
-                       msleep(1);
-                       /* REVISIT could have a waitchannel here, if
-                        * concurrent open performance is important
-                        */
-                       break;
-               }
-       } while (status != -EAGAIN);
-
-       /* Do the "real open" */
-       spin_lock_irq(&port->port_lock);
-
-       /* allocate circular buffer on first open */
-       if (port->port_write_buf.buf_buf == NULL) {
-
-               spin_unlock_irq(&port->port_lock);
-               status = gs_buf_alloc(&port->port_write_buf, WRITE_BUF_SIZE);
-               spin_lock_irq(&port->port_lock);
-
-               if (status) {
-                       pr_debug("gs_open: ttyGS%d (%p,%p) no buffer\n",
-                               port->port_num, tty, file);
-                       port->openclose = false;
-                       goto exit_unlock_port;
-               }
-       }
-
-       /* REVISIT if REMOVED (ports[].port NULL), abort the open
-        * to let rmmod work faster (but this way isn't wrong).
-        */
-
-       /* REVISIT maybe wait for "carrier detect" */
-
-       tty->driver_data = port;
-       port->port.tty = tty;
-
-       port->port.count = 1;
-       port->openclose = false;
-
-       /* if connected, start the I/O stream */
-       if (port->port_usb) {
-               struct gserial  *gser = port->port_usb;
-
-               pr_debug("gs_open: start ttyGS%d\n", port->port_num);
-               gs_start_io(port);
-
-               if (gser->connect)
-                       gser->connect(gser);
-       }
-
-       pr_debug("gs_open: ttyGS%d (%p,%p)\n", port->port_num, tty, file);
-
-       status = 0;
-
-exit_unlock_port:
-       spin_unlock_irq(&port->port_lock);
-       return status;
-}
-
-static int gs_writes_finished(struct gs_port *p)
-{
-       int cond;
-
-       /* return true on disconnect or empty buffer */
-       spin_lock_irq(&p->port_lock);
-       cond = (p->port_usb == NULL) || !gs_buf_data_avail(&p->port_write_buf);
-       spin_unlock_irq(&p->port_lock);
-
-       return cond;
-}
-
-static void gs_close(struct tty_struct *tty, struct file *file)
-{
-       struct gs_port *port = tty->driver_data;
-       struct gserial  *gser;
-
-       spin_lock_irq(&port->port_lock);
-
-       if (port->port.count != 1) {
-               if (port->port.count == 0)
-                       WARN_ON(1);
-               else
-                       --port->port.count;
-               goto exit;
-       }
-
-       pr_debug("gs_close: ttyGS%d (%p,%p) ...\n", port->port_num, tty, file);
-
-       /* mark port as closing but in use; we can drop port lock
-        * and sleep if necessary
-        */
-       port->openclose = true;
-       port->port.count = 0;
-
-       gser = port->port_usb;
-       if (gser && gser->disconnect)
-               gser->disconnect(gser);
-
-       /* wait for circular write buffer to drain, disconnect, or at
-        * most GS_CLOSE_TIMEOUT seconds; then discard the rest
-        */
-       if (gs_buf_data_avail(&port->port_write_buf) > 0 && gser) {
-               spin_unlock_irq(&port->port_lock);
-               wait_event_interruptible_timeout(port->drain_wait,
-                                       gs_writes_finished(port),
-                                       GS_CLOSE_TIMEOUT * HZ);
-               spin_lock_irq(&port->port_lock);
-               gser = port->port_usb;
-       }
-
-       /* Iff we're disconnected, there can be no I/O in flight so it's
-        * ok to free the circular buffer; else just scrub it.  And don't
-        * let the push tasklet fire again until we're re-opened.
-        */
-       if (gser == NULL)
-               gs_buf_free(&port->port_write_buf);
-       else
-               gs_buf_clear(&port->port_write_buf);
-
-       tty->driver_data = NULL;
-       port->port.tty = NULL;
-
-       port->openclose = false;
-
-       pr_debug("gs_close: ttyGS%d (%p,%p) done!\n",
-                       port->port_num, tty, file);
-
-       wake_up(&port->port.close_wait);
-exit:
-       spin_unlock_irq(&port->port_lock);
-}
-
-static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count)
-{
-       struct gs_port  *port = tty->driver_data;
-       unsigned long   flags;
-       int             status;
-
-       pr_vdebug("gs_write: ttyGS%d (%p) writing %d bytes\n",
-                       port->port_num, tty, count);
-
-       spin_lock_irqsave(&port->port_lock, flags);
-       if (count)
-               count = gs_buf_put(&port->port_write_buf, buf, count);
-       /* treat count == 0 as flush_chars() */
-       if (port->port_usb)
-               status = gs_start_tx(port);
-       spin_unlock_irqrestore(&port->port_lock, flags);
-
-       return count;
-}
-
-static int gs_put_char(struct tty_struct *tty, unsigned char ch)
-{
-       struct gs_port  *port = tty->driver_data;
-       unsigned long   flags;
-       int             status;
-
-       pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %pf\n",
-               port->port_num, tty, ch, __builtin_return_address(0));
-
-       spin_lock_irqsave(&port->port_lock, flags);
-       status = gs_buf_put(&port->port_write_buf, &ch, 1);
-       spin_unlock_irqrestore(&port->port_lock, flags);
-
-       return status;
-}
-
-static void gs_flush_chars(struct tty_struct *tty)
-{
-       struct gs_port  *port = tty->driver_data;
-       unsigned long   flags;
-
-       pr_vdebug("gs_flush_chars: (%d,%p)\n", port->port_num, tty);
-
-       spin_lock_irqsave(&port->port_lock, flags);
-       if (port->port_usb)
-               gs_start_tx(port);
-       spin_unlock_irqrestore(&port->port_lock, flags);
-}
-
-static int gs_write_room(struct tty_struct *tty)
-{
-       struct gs_port  *port = tty->driver_data;
-       unsigned long   flags;
-       int             room = 0;
-
-       spin_lock_irqsave(&port->port_lock, flags);
-       if (port->port_usb)
-               room = gs_buf_space_avail(&port->port_write_buf);
-       spin_unlock_irqrestore(&port->port_lock, flags);
-
-       pr_vdebug("gs_write_room: (%d,%p) room=%d\n",
-               port->port_num, tty, room);
-
-       return room;
-}
-
-static int gs_chars_in_buffer(struct tty_struct *tty)
-{
-       struct gs_port  *port = tty->driver_data;
-       unsigned long   flags;
-       int             chars = 0;
-
-       spin_lock_irqsave(&port->port_lock, flags);
-       chars = gs_buf_data_avail(&port->port_write_buf);
-       spin_unlock_irqrestore(&port->port_lock, flags);
-
-       pr_vdebug("gs_chars_in_buffer: (%d,%p) chars=%d\n",
-               port->port_num, tty, chars);
-
-       return chars;
-}
-
-/* undo side effects of setting TTY_THROTTLED */
-static void gs_unthrottle(struct tty_struct *tty)
-{
-       struct gs_port          *port = tty->driver_data;
-       unsigned long           flags;
-
-       spin_lock_irqsave(&port->port_lock, flags);
-       if (port->port_usb) {
-               /* Kickstart read queue processing.  We don't do xon/xoff,
-                * rts/cts, or other handshaking with the host, but if the
-                * read queue backs up enough we'll be NAKing OUT packets.
-                */
-               tasklet_schedule(&port->push);
-               pr_vdebug(PREFIX "%d: unthrottle\n", port->port_num);
-       }
-       spin_unlock_irqrestore(&port->port_lock, flags);
-}
-
-static int gs_break_ctl(struct tty_struct *tty, int duration)
-{
-       struct gs_port  *port = tty->driver_data;
-       int             status = 0;
-       struct gserial  *gser;
-
-       pr_vdebug("gs_break_ctl: ttyGS%d, send break (%d) \n",
-                       port->port_num, duration);
-
-       spin_lock_irq(&port->port_lock);
-       gser = port->port_usb;
-       if (gser && gser->send_break)
-               status = gser->send_break(gser, duration);
-       spin_unlock_irq(&port->port_lock);
-
-       return status;
-}
-
-static const struct tty_operations gs_tty_ops = {
-       .open =                 gs_open,
-       .close =                gs_close,
-       .write =                gs_write,
-       .put_char =             gs_put_char,
-       .flush_chars =          gs_flush_chars,
-       .write_room =           gs_write_room,
-       .chars_in_buffer =      gs_chars_in_buffer,
-       .unthrottle =           gs_unthrottle,
-       .break_ctl =            gs_break_ctl,
-};
-
-/*-------------------------------------------------------------------------*/
-
-static struct tty_driver *gs_tty_driver;
-
-static int
-gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding)
-{
-       struct gs_port  *port;
-       int             ret = 0;
-
-       mutex_lock(&ports[port_num].lock);
-       if (ports[port_num].port) {
-               ret = -EBUSY;
-               goto out;
-       }
-
-       port = kzalloc(sizeof(struct gs_port), GFP_KERNEL);
-       if (port == NULL) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       tty_port_init(&port->port);
-       spin_lock_init(&port->port_lock);
-       init_waitqueue_head(&port->drain_wait);
-
-       tasklet_init(&port->push, gs_rx_push, (unsigned long) port);
-
-       INIT_LIST_HEAD(&port->read_pool);
-       INIT_LIST_HEAD(&port->read_queue);
-       INIT_LIST_HEAD(&port->write_pool);
-
-       port->port_num = port_num;
-       port->port_line_coding = *coding;
-
-       ports[port_num].port = port;
-out:
-       mutex_unlock(&ports[port_num].lock);
-       return ret;
-}
-
-static int gs_closed(struct gs_port *port)
-{
-       int cond;
-
-       spin_lock_irq(&port->port_lock);
-       cond = (port->port.count == 0) && !port->openclose;
-       spin_unlock_irq(&port->port_lock);
-       return cond;
-}
-
-static void gserial_free_port(struct gs_port *port)
-{
-       tasklet_kill(&port->push);
-       /* wait for old opens to finish */
-       wait_event(port->port.close_wait, gs_closed(port));
-       WARN_ON(port->port_usb != NULL);
-       tty_port_destroy(&port->port);
-       kfree(port);
-}
-
-void gserial_free_line(unsigned char port_num)
-{
-       struct gs_port  *port;
-
-       mutex_lock(&ports[port_num].lock);
-       if (WARN_ON(!ports[port_num].port)) {
-               mutex_unlock(&ports[port_num].lock);
-               return;
-       }
-       port = ports[port_num].port;
-       ports[port_num].port = NULL;
-       mutex_unlock(&ports[port_num].lock);
-
-       gserial_free_port(port);
-       tty_unregister_device(gs_tty_driver, port_num);
-}
-EXPORT_SYMBOL_GPL(gserial_free_line);
-
-int gserial_alloc_line(unsigned char *line_num)
-{
-       struct usb_cdc_line_coding      coding;
-       struct device                   *tty_dev;
-       int                             ret;
-       int                             port_num;
-
-       coding.dwDTERate = cpu_to_le32(9600);
-       coding.bCharFormat = 8;
-       coding.bParityType = USB_CDC_NO_PARITY;
-       coding.bDataBits = USB_CDC_1_STOP_BITS;
-
-       for (port_num = 0; port_num < MAX_U_SERIAL_PORTS; port_num++) {
-               ret = gs_port_alloc(port_num, &coding);
-               if (ret == -EBUSY)
-                       continue;
-               if (ret)
-                       return ret;
-               break;
-       }
-       if (ret)
-               return ret;
-
-       /* ... and sysfs class devices, so mdev/udev make /dev/ttyGS* */
-
-       tty_dev = tty_port_register_device(&ports[port_num].port->port,
-                       gs_tty_driver, port_num, NULL);
-       if (IS_ERR(tty_dev)) {
-               struct gs_port  *port;
-               pr_err("%s: failed to register tty for port %d, err %ld\n",
-                               __func__, port_num, PTR_ERR(tty_dev));
-
-               ret = PTR_ERR(tty_dev);
-               port = ports[port_num].port;
-               ports[port_num].port = NULL;
-               gserial_free_port(port);
-               goto err;
-       }
-       *line_num = port_num;
-err:
-       return ret;
-}
-EXPORT_SYMBOL_GPL(gserial_alloc_line);
-
-/**
- * gserial_connect - notify TTY I/O glue that USB link is active
- * @gser: the function, set up with endpoints and descriptors
- * @port_num: which port is active
- * Context: any (usually from irq)
- *
- * This is called activate endpoints and let the TTY layer know that
- * the connection is active ... not unlike "carrier detect".  It won't
- * necessarily start I/O queues; unless the TTY is held open by any
- * task, there would be no point.  However, the endpoints will be
- * activated so the USB host can perform I/O, subject to basic USB
- * hardware flow control.
- *
- * Caller needs to have set up the endpoints and USB function in @dev
- * before calling this, as well as the appropriate (speed-specific)
- * endpoint descriptors, and also have allocate @port_num by calling
- * @gserial_alloc_line().
- *
- * Returns negative errno or zero.
- * On success, ep->driver_data will be overwritten.
- */
-int gserial_connect(struct gserial *gser, u8 port_num)
-{
-       struct gs_port  *port;
-       unsigned long   flags;
-       int             status;
-
-       if (port_num >= MAX_U_SERIAL_PORTS)
-               return -ENXIO;
-
-       port = ports[port_num].port;
-       if (!port) {
-               pr_err("serial line %d not allocated.\n", port_num);
-               return -EINVAL;
-       }
-       if (port->port_usb) {
-               pr_err("serial line %d is in use.\n", port_num);
-               return -EBUSY;
-       }
-
-       /* activate the endpoints */
-       status = usb_ep_enable(gser->in);
-       if (status < 0)
-               return status;
-       gser->in->driver_data = port;
-
-       status = usb_ep_enable(gser->out);
-       if (status < 0)
-               goto fail_out;
-       gser->out->driver_data = port;
-
-       /* then tell the tty glue that I/O can work */
-       spin_lock_irqsave(&port->port_lock, flags);
-       gser->ioport = port;
-       port->port_usb = gser;
-
-       /* REVISIT unclear how best to handle this state...
-        * we don't really couple it with the Linux TTY.
-        */
-       gser->port_line_coding = port->port_line_coding;
-
-       /* REVISIT if waiting on "carrier detect", signal. */
-
-       /* if it's already open, start I/O ... and notify the serial
-        * protocol about open/close status (connect/disconnect).
-        */
-       if (port->port.count) {
-               pr_debug("gserial_connect: start ttyGS%d\n", port->port_num);
-               gs_start_io(port);
-               if (gser->connect)
-                       gser->connect(gser);
-       } else {
-               if (gser->disconnect)
-                       gser->disconnect(gser);
-       }
-
-       spin_unlock_irqrestore(&port->port_lock, flags);
-
-       return status;
-
-fail_out:
-       usb_ep_disable(gser->in);
-       gser->in->driver_data = NULL;
-       return status;
-}
-EXPORT_SYMBOL_GPL(gserial_connect);
-/**
- * gserial_disconnect - notify TTY I/O glue that USB link is inactive
- * @gser: the function, on which gserial_connect() was called
- * Context: any (usually from irq)
- *
- * This is called to deactivate endpoints and let the TTY layer know
- * that the connection went inactive ... not unlike "hangup".
- *
- * On return, the state is as if gserial_connect() had never been called;
- * there is no active USB I/O on these endpoints.
- */
-void gserial_disconnect(struct gserial *gser)
-{
-       struct gs_port  *port = gser->ioport;
-       unsigned long   flags;
-
-       if (!port)
-               return;
-
-       /* tell the TTY glue not to do I/O here any more */
-       spin_lock_irqsave(&port->port_lock, flags);
-
-       /* REVISIT as above: how best to track this? */
-       port->port_line_coding = gser->port_line_coding;
-
-       port->port_usb = NULL;
-       gser->ioport = NULL;
-       if (port->port.count > 0 || port->openclose) {
-               wake_up_interruptible(&port->drain_wait);
-               if (port->port.tty)
-                       tty_hangup(port->port.tty);
-       }
-       spin_unlock_irqrestore(&port->port_lock, flags);
-
-       /* disable endpoints, aborting down any active I/O */
-       usb_ep_disable(gser->out);
-       gser->out->driver_data = NULL;
-
-       usb_ep_disable(gser->in);
-       gser->in->driver_data = NULL;
-
-       /* finally, free any unused/unusable I/O buffers */
-       spin_lock_irqsave(&port->port_lock, flags);
-       if (port->port.count == 0 && !port->openclose)
-               gs_buf_free(&port->port_write_buf);
-       gs_free_requests(gser->out, &port->read_pool, NULL);
-       gs_free_requests(gser->out, &port->read_queue, NULL);
-       gs_free_requests(gser->in, &port->write_pool, NULL);
-
-       port->read_allocated = port->read_started =
-               port->write_allocated = port->write_started = 0;
-
-       spin_unlock_irqrestore(&port->port_lock, flags);
-}
-EXPORT_SYMBOL_GPL(gserial_disconnect);
-
-static int userial_init(void)
-{
-       unsigned                        i;
-       int                             status;
-
-       gs_tty_driver = alloc_tty_driver(MAX_U_SERIAL_PORTS);
-       if (!gs_tty_driver)
-               return -ENOMEM;
-
-       gs_tty_driver->driver_name = "g_serial";
-       gs_tty_driver->name = PREFIX;
-       /* uses dynamically assigned dev_t values */
-
-       gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
-       gs_tty_driver->subtype = SERIAL_TYPE_NORMAL;
-       gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
-       gs_tty_driver->init_termios = tty_std_termios;
-
-       /* 9600-8-N-1 ... matches defaults expected by "usbser.sys" on
-        * MS-Windows.  Otherwise, most of these flags shouldn't affect
-        * anything unless we were to actually hook up to a serial line.
-        */
-       gs_tty_driver->init_termios.c_cflag =
-                       B9600 | CS8 | CREAD | HUPCL | CLOCAL;
-       gs_tty_driver->init_termios.c_ispeed = 9600;
-       gs_tty_driver->init_termios.c_ospeed = 9600;
-
-       tty_set_operations(gs_tty_driver, &gs_tty_ops);
-       for (i = 0; i < MAX_U_SERIAL_PORTS; i++)
-               mutex_init(&ports[i].lock);
-
-       /* export the driver ... */
-       status = tty_register_driver(gs_tty_driver);
-       if (status) {
-               pr_err("%s: cannot register, err %d\n",
-                               __func__, status);
-               goto fail;
-       }
-
-       pr_debug("%s: registered %d ttyGS* device%s\n", __func__,
-                       MAX_U_SERIAL_PORTS,
-                       (MAX_U_SERIAL_PORTS == 1) ? "" : "s");
-
-       return status;
-fail:
-       put_tty_driver(gs_tty_driver);
-       gs_tty_driver = NULL;
-       return status;
-}
-module_init(userial_init);
-
-static void userial_cleanup(void)
-{
-       tty_unregister_driver(gs_tty_driver);
-       put_tty_driver(gs_tty_driver);
-       gs_tty_driver = NULL;
-}
-module_exit(userial_cleanup);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/u_serial.h
deleted file mode 100644 (file)
index c20210c..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * u_serial.h - interface to USB gadget "serial port"/TTY utilities
- *
- * Copyright (C) 2008 David Brownell
- * Copyright (C) 2008 by Nokia Corporation
- *
- * This software is distributed under the terms of the GNU General
- * Public License ("GPL") as published by the Free Software Foundation,
- * either version 2 of that License or (at your option) any later version.
- */
-
-#ifndef __U_SERIAL_H
-#define __U_SERIAL_H
-
-#include <linux/usb/composite.h>
-#include <linux/usb/cdc.h>
-
-#define MAX_U_SERIAL_PORTS     4
-
-struct f_serial_opts {
-       struct usb_function_instance func_inst;
-       u8 port_num;
-};
-
-/*
- * One non-multiplexed "serial" I/O port ... there can be several of these
- * on any given USB peripheral device, if it provides enough endpoints.
- *
- * The "u_serial" utility component exists to do one thing:  manage TTY
- * style I/O using the USB peripheral endpoints listed here, including
- * hookups to sysfs and /dev for each logical "tty" device.
- *
- * REVISIT at least ACM could support tiocmget() if needed.
- *
- * REVISIT someday, allow multiplexing several TTYs over these endpoints.
- */
-struct gserial {
-       struct usb_function             func;
-
-       /* port is managed by gserial_{connect,disconnect} */
-       struct gs_port                  *ioport;
-
-       struct usb_ep                   *in;
-       struct usb_ep                   *out;
-
-       /* REVISIT avoid this CDC-ACM support harder ... */
-       struct usb_cdc_line_coding port_line_coding;    /* 9600-8-N-1 etc */
-
-       /* notification callbacks */
-       void (*connect)(struct gserial *p);
-       void (*disconnect)(struct gserial *p);
-       int (*send_break)(struct gserial *p, int duration);
-};
-
-/* utilities to allocate/free request and buffer */
-struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags);
-void gs_free_req(struct usb_ep *, struct usb_request *req);
-
-/* management of individual TTY ports */
-int gserial_alloc_line(unsigned char *port_line);
-void gserial_free_line(unsigned char port_line);
-
-/* connect/disconnect is handled by individual functions */
-int gserial_connect(struct gserial *, u8 port_num);
-void gserial_disconnect(struct gserial *);
-
-/* functions are bound to configurations by a config or gadget driver */
-int gser_bind_config(struct usb_configuration *c, u8 port_num);
-int obex_bind_config(struct usb_configuration *c, u8 port_num);
-
-#endif /* __U_SERIAL_H */
diff --git a/drivers/usb/gadget/u_uac1.c b/drivers/usb/gadget/u_uac1.c
deleted file mode 100644 (file)
index 7a55fea..0000000
+++ /dev/null
@@ -1,330 +0,0 @@
-/*
- * u_uac1.c -- ALSA audio utilities for Gadget stack
- *
- * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
- * Copyright (C) 2008 Analog Devices, Inc
- *
- * Enter bugs at http://blackfin.uclinux.org/
- *
- * Licensed under the GPL-2 or later.
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/device.h>
-#include <linux/delay.h>
-#include <linux/ctype.h>
-#include <linux/random.h>
-#include <linux/syscalls.h>
-
-#include "u_uac1.h"
-
-/*
- * This component encapsulates the ALSA devices for USB audio gadget
- */
-
-#define FILE_PCM_PLAYBACK      "/dev/snd/pcmC0D0p"
-#define FILE_PCM_CAPTURE       "/dev/snd/pcmC0D0c"
-#define FILE_CONTROL           "/dev/snd/controlC0"
-
-static char *fn_play = FILE_PCM_PLAYBACK;
-module_param(fn_play, charp, S_IRUGO);
-MODULE_PARM_DESC(fn_play, "Playback PCM device file name");
-
-static char *fn_cap = FILE_PCM_CAPTURE;
-module_param(fn_cap, charp, S_IRUGO);
-MODULE_PARM_DESC(fn_cap, "Capture PCM device file name");
-
-static char *fn_cntl = FILE_CONTROL;
-module_param(fn_cntl, charp, S_IRUGO);
-MODULE_PARM_DESC(fn_cntl, "Control device file name");
-
-/*-------------------------------------------------------------------------*/
-
-/**
- * Some ALSA internal helper functions
- */
-static int snd_interval_refine_set(struct snd_interval *i, unsigned int val)
-{
-       struct snd_interval t;
-       t.empty = 0;
-       t.min = t.max = val;
-       t.openmin = t.openmax = 0;
-       t.integer = 1;
-       return snd_interval_refine(i, &t);
-}
-
-static int _snd_pcm_hw_param_set(struct snd_pcm_hw_params *params,
-                                snd_pcm_hw_param_t var, unsigned int val,
-                                int dir)
-{
-       int changed;
-       if (hw_is_mask(var)) {
-               struct snd_mask *m = hw_param_mask(params, var);
-               if (val == 0 && dir < 0) {
-                       changed = -EINVAL;
-                       snd_mask_none(m);
-               } else {
-                       if (dir > 0)
-                               val++;
-                       else if (dir < 0)
-                               val--;
-                       changed = snd_mask_refine_set(
-                                       hw_param_mask(params, var), val);
-               }
-       } else if (hw_is_interval(var)) {
-               struct snd_interval *i = hw_param_interval(params, var);
-               if (val == 0 && dir < 0) {
-                       changed = -EINVAL;
-                       snd_interval_none(i);
-               } else if (dir == 0)
-                       changed = snd_interval_refine_set(i, val);
-               else {
-                       struct snd_interval t;
-                       t.openmin = 1;
-                       t.openmax = 1;
-                       t.empty = 0;
-                       t.integer = 0;
-                       if (dir < 0) {
-                               t.min = val - 1;
-                               t.max = val;
-                       } else {
-                               t.min = val;
-                               t.max = val+1;
-                       }
-                       changed = snd_interval_refine(i, &t);
-               }
-       } else
-               return -EINVAL;
-       if (changed) {
-               params->cmask |= 1 << var;
-               params->rmask |= 1 << var;
-       }
-       return changed;
-}
-/*-------------------------------------------------------------------------*/
-
-/**
- * Set default hardware params
- */
-static int playback_default_hw_params(struct gaudio_snd_dev *snd)
-{
-       struct snd_pcm_substream *substream = snd->substream;
-       struct snd_pcm_hw_params *params;
-       snd_pcm_sframes_t result;
-
-       /*
-       * SNDRV_PCM_ACCESS_RW_INTERLEAVED,
-       * SNDRV_PCM_FORMAT_S16_LE
-       * CHANNELS: 2
-       * RATE: 48000
-       */
-       snd->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
-       snd->format = SNDRV_PCM_FORMAT_S16_LE;
-       snd->channels = 2;
-       snd->rate = 48000;
-
-       params = kzalloc(sizeof(*params), GFP_KERNEL);
-       if (!params)
-               return -ENOMEM;
-
-       _snd_pcm_hw_params_any(params);
-       _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS,
-                       snd->access, 0);
-       _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT,
-                       snd->format, 0);
-       _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS,
-                       snd->channels, 0);
-       _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE,
-                       snd->rate, 0);
-
-       snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
-       snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, params);
-
-       result = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL);
-       if (result < 0) {
-               ERROR(snd->card,
-                       "Preparing sound card failed: %d\n", (int)result);
-               kfree(params);
-               return result;
-       }
-
-       /* Store the hardware parameters */
-       snd->access = params_access(params);
-       snd->format = params_format(params);
-       snd->channels = params_channels(params);
-       snd->rate = params_rate(params);
-
-       kfree(params);
-
-       INFO(snd->card,
-               "Hardware params: access %x, format %x, channels %d, rate %d\n",
-               snd->access, snd->format, snd->channels, snd->rate);
-
-       return 0;
-}
-
-/**
- * Playback audio buffer data by ALSA PCM device
- */
-static size_t u_audio_playback(struct gaudio *card, void *buf, size_t count)
-{
-       struct gaudio_snd_dev   *snd = &card->playback;
-       struct snd_pcm_substream *substream = snd->substream;
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       mm_segment_t old_fs;
-       ssize_t result;
-       snd_pcm_sframes_t frames;
-
-try_again:
-       if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
-               runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
-               result = snd_pcm_kernel_ioctl(substream,
-                               SNDRV_PCM_IOCTL_PREPARE, NULL);
-               if (result < 0) {
-                       ERROR(card, "Preparing sound card failed: %d\n",
-                                       (int)result);
-                       return result;
-               }
-       }
-
-       frames = bytes_to_frames(runtime, count);
-       old_fs = get_fs();
-       set_fs(KERNEL_DS);
-       result = snd_pcm_lib_write(snd->substream, (void __user *)buf, frames);
-       if (result != frames) {
-               ERROR(card, "Playback error: %d\n", (int)result);
-               set_fs(old_fs);
-               goto try_again;
-       }
-       set_fs(old_fs);
-
-       return 0;
-}
-
-static int u_audio_get_playback_channels(struct gaudio *card)
-{
-       return card->playback.channels;
-}
-
-static int u_audio_get_playback_rate(struct gaudio *card)
-{
-       return card->playback.rate;
-}
-
-/**
- * Open ALSA PCM and control device files
- * Initial the PCM or control device
- */
-static int gaudio_open_snd_dev(struct gaudio *card)
-{
-       struct snd_pcm_file *pcm_file;
-       struct gaudio_snd_dev *snd;
-
-       if (!card)
-               return -ENODEV;
-
-       /* Open control device */
-       snd = &card->control;
-       snd->filp = filp_open(fn_cntl, O_RDWR, 0);
-       if (IS_ERR(snd->filp)) {
-               int ret = PTR_ERR(snd->filp);
-               ERROR(card, "unable to open sound control device file: %s\n",
-                               fn_cntl);
-               snd->filp = NULL;
-               return ret;
-       }
-       snd->card = card;
-
-       /* Open PCM playback device and setup substream */
-       snd = &card->playback;
-       snd->filp = filp_open(fn_play, O_WRONLY, 0);
-       if (IS_ERR(snd->filp)) {
-               int ret = PTR_ERR(snd->filp);
-
-               ERROR(card, "No such PCM playback device: %s\n", fn_play);
-               snd->filp = NULL;
-               return ret;
-       }
-       pcm_file = snd->filp->private_data;
-       snd->substream = pcm_file->substream;
-       snd->card = card;
-       playback_default_hw_params(snd);
-
-       /* Open PCM capture device and setup substream */
-       snd = &card->capture;
-       snd->filp = filp_open(fn_cap, O_RDONLY, 0);
-       if (IS_ERR(snd->filp)) {
-               ERROR(card, "No such PCM capture device: %s\n", fn_cap);
-               snd->substream = NULL;
-               snd->card = NULL;
-               snd->filp = NULL;
-       } else {
-               pcm_file = snd->filp->private_data;
-               snd->substream = pcm_file->substream;
-               snd->card = card;
-       }
-
-       return 0;
-}
-
-/**
- * Close ALSA PCM and control device files
- */
-static int gaudio_close_snd_dev(struct gaudio *gau)
-{
-       struct gaudio_snd_dev   *snd;
-
-       /* Close control device */
-       snd = &gau->control;
-       if (snd->filp)
-               filp_close(snd->filp, NULL);
-
-       /* Close PCM playback device and setup substream */
-       snd = &gau->playback;
-       if (snd->filp)
-               filp_close(snd->filp, NULL);
-
-       /* Close PCM capture device and setup substream */
-       snd = &gau->capture;
-       if (snd->filp)
-               filp_close(snd->filp, NULL);
-
-       return 0;
-}
-
-static struct gaudio *the_card;
-/**
- * gaudio_setup - setup ALSA interface and preparing for USB transfer
- *
- * This sets up PCM, mixer or MIDI ALSA devices fore USB gadget using.
- *
- * Returns negative errno, or zero on success
- */
-int __init gaudio_setup(struct gaudio *card)
-{
-       int     ret;
-
-       ret = gaudio_open_snd_dev(card);
-       if (ret)
-               ERROR(card, "we need at least one control device\n");
-       else if (!the_card)
-               the_card = card;
-
-       return ret;
-
-}
-
-/**
- * gaudio_cleanup - remove ALSA device interface
- *
- * This is called to free all resources allocated by @gaudio_setup().
- */
-void gaudio_cleanup(void)
-{
-       if (the_card) {
-               gaudio_close_snd_dev(the_card);
-               the_card = NULL;
-       }
-}
-
diff --git a/drivers/usb/gadget/u_uac1.h b/drivers/usb/gadget/u_uac1.h
deleted file mode 100644 (file)
index 18c2e72..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * u_uac1.h -- interface to USB gadget "ALSA AUDIO" utilities
- *
- * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
- * Copyright (C) 2008 Analog Devices, Inc
- *
- * Enter bugs at http://blackfin.uclinux.org/
- *
- * Licensed under the GPL-2 or later.
- */
-
-#ifndef __U_AUDIO_H
-#define __U_AUDIO_H
-
-#include <linux/device.h>
-#include <linux/err.h>
-#include <linux/usb/audio.h>
-#include <linux/usb/composite.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-
-#include "gadget_chips.h"
-
-/*
- * This represents the USB side of an audio card device, managed by a USB
- * function which provides control and stream interfaces.
- */
-
-struct gaudio_snd_dev {
-       struct gaudio                   *card;
-       struct file                     *filp;
-       struct snd_pcm_substream        *substream;
-       int                             access;
-       int                             format;
-       int                             channels;
-       int                             rate;
-};
-
-struct gaudio {
-       struct usb_function             func;
-       struct usb_gadget               *gadget;
-
-       /* ALSA sound device interfaces */
-       struct gaudio_snd_dev           control;
-       struct gaudio_snd_dev           playback;
-       struct gaudio_snd_dev           capture;
-
-       /* TODO */
-};
-
-int gaudio_setup(struct gaudio *card);
-void gaudio_cleanup(void);
-
-#endif /* __U_AUDIO_H */
diff --git a/drivers/usb/gadget/uvc.h b/drivers/usb/gadget/uvc.h
deleted file mode 100644 (file)
index 7a9111d..0000000
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- *     uvc_gadget.h  --  USB Video Class Gadget driver
- *
- *     Copyright (C) 2009-2010
- *         Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- *
- *     This program is free software; you can redistribute it and/or modify
- *     it under the terms of the GNU General Public License as published by
- *     the Free Software Foundation; either version 2 of the License, or
- *     (at your option) any later version.
- */
-
-#ifndef _UVC_GADGET_H_
-#define _UVC_GADGET_H_
-
-#include <linux/ioctl.h>
-#include <linux/types.h>
-#include <linux/usb/ch9.h>
-
-#define UVC_EVENT_FIRST                        (V4L2_EVENT_PRIVATE_START + 0)
-#define UVC_EVENT_CONNECT              (V4L2_EVENT_PRIVATE_START + 0)
-#define UVC_EVENT_DISCONNECT           (V4L2_EVENT_PRIVATE_START + 1)
-#define UVC_EVENT_STREAMON             (V4L2_EVENT_PRIVATE_START + 2)
-#define UVC_EVENT_STREAMOFF            (V4L2_EVENT_PRIVATE_START + 3)
-#define UVC_EVENT_SETUP                        (V4L2_EVENT_PRIVATE_START + 4)
-#define UVC_EVENT_DATA                 (V4L2_EVENT_PRIVATE_START + 5)
-#define UVC_EVENT_LAST                 (V4L2_EVENT_PRIVATE_START + 5)
-
-struct uvc_request_data
-{
-       __s32 length;
-       __u8 data[60];
-};
-
-struct uvc_event
-{
-       union {
-               enum usb_device_speed speed;
-               struct usb_ctrlrequest req;
-               struct uvc_request_data data;
-       };
-};
-
-#define UVCIOC_SEND_RESPONSE           _IOW('U', 1, struct uvc_request_data)
-
-#define UVC_INTF_CONTROL               0
-#define UVC_INTF_STREAMING             1
-
-/* ------------------------------------------------------------------------
- * Debugging, printing and logging
- */
-
-#ifdef __KERNEL__
-
-#include <linux/usb.h> /* For usb_endpoint_* */
-#include <linux/usb/gadget.h>
-#include <linux/videodev2.h>
-#include <linux/version.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-device.h>
-
-#include "uvc_queue.h"
-
-#define UVC_TRACE_PROBE                                (1 << 0)
-#define UVC_TRACE_DESCR                                (1 << 1)
-#define UVC_TRACE_CONTROL                      (1 << 2)
-#define UVC_TRACE_FORMAT                       (1 << 3)
-#define UVC_TRACE_CAPTURE                      (1 << 4)
-#define UVC_TRACE_CALLS                                (1 << 5)
-#define UVC_TRACE_IOCTL                                (1 << 6)
-#define UVC_TRACE_FRAME                                (1 << 7)
-#define UVC_TRACE_SUSPEND                      (1 << 8)
-#define UVC_TRACE_STATUS                       (1 << 9)
-
-#define UVC_WARN_MINMAX                                0
-#define UVC_WARN_PROBE_DEF                     1
-
-extern unsigned int uvc_gadget_trace_param;
-
-#define uvc_trace(flag, msg...) \
-       do { \
-               if (uvc_gadget_trace_param & flag) \
-                       printk(KERN_DEBUG "uvcvideo: " msg); \
-       } while (0)
-
-#define uvc_warn_once(dev, warn, msg...) \
-       do { \
-               if (!test_and_set_bit(warn, &dev->warnings)) \
-                       printk(KERN_INFO "uvcvideo: " msg); \
-       } while (0)
-
-#define uvc_printk(level, msg...) \
-       printk(level "uvcvideo: " msg)
-
-/* ------------------------------------------------------------------------
- * Driver specific constants
- */
-
-#define DRIVER_VERSION                         "0.1.0"
-#define DRIVER_VERSION_NUMBER                  KERNEL_VERSION(0, 1, 0)
-
-#define UVC_NUM_REQUESTS                       4
-#define UVC_MAX_REQUEST_SIZE                   64
-#define UVC_MAX_EVENTS                         4
-
-/* ------------------------------------------------------------------------
- * Structures
- */
-
-struct uvc_video
-{
-       struct usb_ep *ep;
-
-       /* Frame parameters */
-       u8 bpp;
-       u32 fcc;
-       unsigned int width;
-       unsigned int height;
-       unsigned int imagesize;
-
-       /* Requests */
-       unsigned int req_size;
-       struct usb_request *req[UVC_NUM_REQUESTS];
-       __u8 *req_buffer[UVC_NUM_REQUESTS];
-       struct list_head req_free;
-       spinlock_t req_lock;
-
-       void (*encode) (struct usb_request *req, struct uvc_video *video,
-                       struct uvc_buffer *buf);
-
-       /* Context data used by the completion handler */
-       __u32 payload_size;
-       __u32 max_payload_size;
-
-       struct uvc_video_queue queue;
-       unsigned int fid;
-};
-
-enum uvc_state
-{
-       UVC_STATE_DISCONNECTED,
-       UVC_STATE_CONNECTED,
-       UVC_STATE_STREAMING,
-};
-
-struct uvc_device
-{
-       struct video_device *vdev;
-       struct v4l2_device v4l2_dev;
-       enum uvc_state state;
-       struct usb_function func;
-       struct uvc_video video;
-
-       /* Descriptors */
-       struct {
-               const struct uvc_descriptor_header * const *fs_control;
-               const struct uvc_descriptor_header * const *ss_control;
-               const struct uvc_descriptor_header * const *fs_streaming;
-               const struct uvc_descriptor_header * const *hs_streaming;
-               const struct uvc_descriptor_header * const *ss_streaming;
-       } desc;
-
-       unsigned int control_intf;
-       struct usb_ep *control_ep;
-       struct usb_request *control_req;
-       void *control_buf;
-
-       unsigned int streaming_intf;
-
-       /* Events */
-       unsigned int event_length;
-       unsigned int event_setup_out : 1;
-};
-
-static inline struct uvc_device *to_uvc(struct usb_function *f)
-{
-       return container_of(f, struct uvc_device, func);
-}
-
-struct uvc_file_handle
-{
-       struct v4l2_fh vfh;
-       struct uvc_video *device;
-};
-
-#define to_uvc_file_handle(handle) \
-       container_of(handle, struct uvc_file_handle, vfh)
-
-/* ------------------------------------------------------------------------
- * Functions
- */
-
-extern void uvc_function_setup_continue(struct uvc_device *uvc);
-extern void uvc_endpoint_stream(struct uvc_device *dev);
-
-extern void uvc_function_connect(struct uvc_device *uvc);
-extern void uvc_function_disconnect(struct uvc_device *uvc);
-
-#endif /* __KERNEL__ */
-
-#endif /* _UVC_GADGET_H_ */
-
diff --git a/drivers/usb/gadget/uvc_queue.c b/drivers/usb/gadget/uvc_queue.c
deleted file mode 100644 (file)
index 1c29bc9..0000000
+++ /dev/null
@@ -1,407 +0,0 @@
-/*
- *     uvc_queue.c  --  USB Video Class driver - Buffers management
- *
- *     Copyright (C) 2005-2010
- *         Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- *
- *     This program is free software; you can redistribute it and/or modify
- *     it under the terms of the GNU General Public License as published by
- *     the Free Software Foundation; either version 2 of the License, or
- *     (at your option) any later version.
- */
-
-#include <linux/atomic.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/usb.h>
-#include <linux/videodev2.h>
-#include <linux/vmalloc.h>
-#include <linux/wait.h>
-
-#include <media/v4l2-common.h>
-#include <media/videobuf2-vmalloc.h>
-
-#include "uvc.h"
-
-/* ------------------------------------------------------------------------
- * Video buffers queue management.
- *
- * Video queues is initialized by uvc_queue_init(). The function performs
- * basic initialization of the uvc_video_queue struct and never fails.
- *
- * Video buffers are managed by videobuf2. The driver uses a mutex to protect
- * the videobuf2 queue operations by serializing calls to videobuf2 and a
- * spinlock to protect the IRQ queue that holds the buffers to be processed by
- * the driver.
- */
-
-/* -----------------------------------------------------------------------------
- * videobuf2 queue operations
- */
-
-static int uvc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
-                          unsigned int *nbuffers, unsigned int *nplanes,
-                          unsigned int sizes[], void *alloc_ctxs[])
-{
-       struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
-       struct uvc_video *video = container_of(queue, struct uvc_video, queue);
-
-       if (*nbuffers > UVC_MAX_VIDEO_BUFFERS)
-               *nbuffers = UVC_MAX_VIDEO_BUFFERS;
-
-       *nplanes = 1;
-
-       sizes[0] = video->imagesize;
-
-       return 0;
-}
-
-static int uvc_buffer_prepare(struct vb2_buffer *vb)
-{
-       struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
-       struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf);
-
-       if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
-           vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) {
-               uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n");
-               return -EINVAL;
-       }
-
-       if (unlikely(queue->flags & UVC_QUEUE_DISCONNECTED))
-               return -ENODEV;
-
-       buf->state = UVC_BUF_STATE_QUEUED;
-       buf->mem = vb2_plane_vaddr(vb, 0);
-       buf->length = vb2_plane_size(vb, 0);
-       if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               buf->bytesused = 0;
-       else
-               buf->bytesused = vb2_get_plane_payload(vb, 0);
-
-       return 0;
-}
-
-static void uvc_buffer_queue(struct vb2_buffer *vb)
-{
-       struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
-       struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf);
-       unsigned long flags;
-
-       spin_lock_irqsave(&queue->irqlock, flags);
-
-       if (likely(!(queue->flags & UVC_QUEUE_DISCONNECTED))) {
-               list_add_tail(&buf->queue, &queue->irqqueue);
-       } else {
-               /* If the device is disconnected return the buffer to userspace
-                * directly. The next QBUF call will fail with -ENODEV.
-                */
-               buf->state = UVC_BUF_STATE_ERROR;
-               vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR);
-       }
-
-       spin_unlock_irqrestore(&queue->irqlock, flags);
-}
-
-static void uvc_wait_prepare(struct vb2_queue *vq)
-{
-       struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
-
-       mutex_unlock(&queue->mutex);
-}
-
-static void uvc_wait_finish(struct vb2_queue *vq)
-{
-       struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
-
-       mutex_lock(&queue->mutex);
-}
-
-static struct vb2_ops uvc_queue_qops = {
-       .queue_setup = uvc_queue_setup,
-       .buf_prepare = uvc_buffer_prepare,
-       .buf_queue = uvc_buffer_queue,
-       .wait_prepare = uvc_wait_prepare,
-       .wait_finish = uvc_wait_finish,
-};
-
-static int uvc_queue_init(struct uvc_video_queue *queue,
-                         enum v4l2_buf_type type)
-{
-       int ret;
-
-       queue->queue.type = type;
-       queue->queue.io_modes = VB2_MMAP | VB2_USERPTR;
-       queue->queue.drv_priv = queue;
-       queue->queue.buf_struct_size = sizeof(struct uvc_buffer);
-       queue->queue.ops = &uvc_queue_qops;
-       queue->queue.mem_ops = &vb2_vmalloc_memops;
-       queue->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC
-                                    | V4L2_BUF_FLAG_TSTAMP_SRC_EOF;
-       ret = vb2_queue_init(&queue->queue);
-       if (ret)
-               return ret;
-
-       mutex_init(&queue->mutex);
-       spin_lock_init(&queue->irqlock);
-       INIT_LIST_HEAD(&queue->irqqueue);
-       queue->flags = 0;
-
-       return 0;
-}
-
-/*
- * Free the video buffers.
- */
-static void uvc_free_buffers(struct uvc_video_queue *queue)
-{
-       mutex_lock(&queue->mutex);
-       vb2_queue_release(&queue->queue);
-       mutex_unlock(&queue->mutex);
-}
-
-/*
- * Allocate the video buffers.
- */
-static int uvc_alloc_buffers(struct uvc_video_queue *queue,
-                            struct v4l2_requestbuffers *rb)
-{
-       int ret;
-
-       mutex_lock(&queue->mutex);
-       ret = vb2_reqbufs(&queue->queue, rb);
-       mutex_unlock(&queue->mutex);
-
-       return ret ? ret : rb->count;
-}
-
-static int uvc_query_buffer(struct uvc_video_queue *queue,
-                           struct v4l2_buffer *buf)
-{
-       int ret;
-
-       mutex_lock(&queue->mutex);
-       ret = vb2_querybuf(&queue->queue, buf);
-       mutex_unlock(&queue->mutex);
-
-       return ret;
-}
-
-static int uvc_queue_buffer(struct uvc_video_queue *queue,
-                           struct v4l2_buffer *buf)
-{
-       unsigned long flags;
-       int ret;
-
-       mutex_lock(&queue->mutex);
-       ret = vb2_qbuf(&queue->queue, buf);
-       if (ret < 0)
-               goto done;
-
-       spin_lock_irqsave(&queue->irqlock, flags);
-       ret = (queue->flags & UVC_QUEUE_PAUSED) != 0;
-       queue->flags &= ~UVC_QUEUE_PAUSED;
-       spin_unlock_irqrestore(&queue->irqlock, flags);
-
-done:
-       mutex_unlock(&queue->mutex);
-       return ret;
-}
-
-/*
- * Dequeue a video buffer. If nonblocking is false, block until a buffer is
- * available.
- */
-static int uvc_dequeue_buffer(struct uvc_video_queue *queue,
-                             struct v4l2_buffer *buf, int nonblocking)
-{
-       int ret;
-
-       mutex_lock(&queue->mutex);
-       ret = vb2_dqbuf(&queue->queue, buf, nonblocking);
-       mutex_unlock(&queue->mutex);
-
-       return ret;
-}
-
-/*
- * Poll the video queue.
- *
- * This function implements video queue polling and is intended to be used by
- * the device poll handler.
- */
-static unsigned int uvc_queue_poll(struct uvc_video_queue *queue,
-                                  struct file *file, poll_table *wait)
-{
-       unsigned int ret;
-
-       mutex_lock(&queue->mutex);
-       ret = vb2_poll(&queue->queue, file, wait);
-       mutex_unlock(&queue->mutex);
-
-       return ret;
-}
-
-static int uvc_queue_mmap(struct uvc_video_queue *queue,
-                         struct vm_area_struct *vma)
-{
-       int ret;
-
-       mutex_lock(&queue->mutex);
-       ret = vb2_mmap(&queue->queue, vma);
-       mutex_unlock(&queue->mutex);
-
-       return ret;
-}
-
-#ifndef CONFIG_MMU
-/*
- * Get unmapped area.
- *
- * NO-MMU arch need this function to make mmap() work correctly.
- */
-static unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue,
-               unsigned long pgoff)
-{
-       unsigned long ret;
-
-       mutex_lock(&queue->mutex);
-       ret = vb2_get_unmapped_area(&queue->queue, 0, 0, pgoff, 0);
-       mutex_unlock(&queue->mutex);
-       return ret;
-}
-#endif
-
-/*
- * Cancel the video buffers queue.
- *
- * Cancelling the queue marks all buffers on the irq queue as erroneous,
- * wakes them up and removes them from the queue.
- *
- * If the disconnect parameter is set, further calls to uvc_queue_buffer will
- * fail with -ENODEV.
- *
- * This function acquires the irq spinlock and can be called from interrupt
- * context.
- */
-static void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect)
-{
-       struct uvc_buffer *buf;
-       unsigned long flags;
-
-       spin_lock_irqsave(&queue->irqlock, flags);
-       while (!list_empty(&queue->irqqueue)) {
-               buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
-                                      queue);
-               list_del(&buf->queue);
-               buf->state = UVC_BUF_STATE_ERROR;
-               vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR);
-       }
-       /* This must be protected by the irqlock spinlock to avoid race
-        * conditions between uvc_queue_buffer and the disconnection event that
-        * could result in an interruptible wait in uvc_dequeue_buffer. Do not
-        * blindly replace this logic by checking for the UVC_DEV_DISCONNECTED
-        * state outside the queue code.
-        */
-       if (disconnect)
-               queue->flags |= UVC_QUEUE_DISCONNECTED;
-       spin_unlock_irqrestore(&queue->irqlock, flags);
-}
-
-/*
- * Enable or disable the video buffers queue.
- *
- * The queue must be enabled before starting video acquisition and must be
- * disabled after stopping it. This ensures that the video buffers queue
- * state can be properly initialized before buffers are accessed from the
- * interrupt handler.
- *
- * Enabling the video queue initializes parameters (such as sequence number,
- * sync pattern, ...). If the queue is already enabled, return -EBUSY.
- *
- * Disabling the video queue cancels the queue and removes all buffers from
- * the main queue.
- *
- * This function can't be called from interrupt context. Use
- * uvc_queue_cancel() instead.
- */
-static int uvc_queue_enable(struct uvc_video_queue *queue, int enable)
-{
-       unsigned long flags;
-       int ret = 0;
-
-       mutex_lock(&queue->mutex);
-       if (enable) {
-               ret = vb2_streamon(&queue->queue, queue->queue.type);
-               if (ret < 0)
-                       goto done;
-
-               queue->sequence = 0;
-               queue->buf_used = 0;
-       } else {
-               ret = vb2_streamoff(&queue->queue, queue->queue.type);
-               if (ret < 0)
-                       goto done;
-
-               spin_lock_irqsave(&queue->irqlock, flags);
-               INIT_LIST_HEAD(&queue->irqqueue);
-
-               /*
-                * FIXME: We need to clear the DISCONNECTED flag to ensure that
-                * applications will be able to queue buffers for the next
-                * streaming run. However, clearing it here doesn't guarantee
-                * that the device will be reconnected in the meantime.
-                */
-               queue->flags &= ~UVC_QUEUE_DISCONNECTED;
-               spin_unlock_irqrestore(&queue->irqlock, flags);
-       }
-
-done:
-       mutex_unlock(&queue->mutex);
-       return ret;
-}
-
-/* called with &queue_irqlock held.. */
-static struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
-                                               struct uvc_buffer *buf)
-{
-       struct uvc_buffer *nextbuf;
-
-       if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) &&
-            buf->length != buf->bytesused) {
-               buf->state = UVC_BUF_STATE_QUEUED;
-               vb2_set_plane_payload(&buf->buf, 0, 0);
-               return buf;
-       }
-
-       list_del(&buf->queue);
-       if (!list_empty(&queue->irqqueue))
-               nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
-                                          queue);
-       else
-               nextbuf = NULL;
-
-       buf->buf.v4l2_buf.field = V4L2_FIELD_NONE;
-       buf->buf.v4l2_buf.sequence = queue->sequence++;
-       v4l2_get_timestamp(&buf->buf.v4l2_buf.timestamp);
-
-       vb2_set_plane_payload(&buf->buf, 0, buf->bytesused);
-       vb2_buffer_done(&buf->buf, VB2_BUF_STATE_DONE);
-
-       return nextbuf;
-}
-
-static struct uvc_buffer *uvc_queue_head(struct uvc_video_queue *queue)
-{
-       struct uvc_buffer *buf = NULL;
-
-       if (!list_empty(&queue->irqqueue))
-               buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
-                                      queue);
-       else
-               queue->flags |= UVC_QUEUE_PAUSED;
-
-       return buf;
-}
-
diff --git a/drivers/usb/gadget/uvc_queue.h b/drivers/usb/gadget/uvc_queue.h
deleted file mode 100644 (file)
index 8e76ce9..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-#ifndef _UVC_QUEUE_H_
-#define _UVC_QUEUE_H_
-
-#ifdef __KERNEL__
-
-#include <linux/kernel.h>
-#include <linux/poll.h>
-#include <linux/videodev2.h>
-#include <media/videobuf2-core.h>
-
-/* Maximum frame size in bytes, for sanity checking. */
-#define UVC_MAX_FRAME_SIZE     (16*1024*1024)
-/* Maximum number of video buffers. */
-#define UVC_MAX_VIDEO_BUFFERS  32
-
-/* ------------------------------------------------------------------------
- * Structures.
- */
-
-enum uvc_buffer_state {
-       UVC_BUF_STATE_IDLE      = 0,
-       UVC_BUF_STATE_QUEUED    = 1,
-       UVC_BUF_STATE_ACTIVE    = 2,
-       UVC_BUF_STATE_DONE      = 3,
-       UVC_BUF_STATE_ERROR     = 4,
-};
-
-struct uvc_buffer {
-       struct vb2_buffer buf;
-       struct list_head queue;
-
-       enum uvc_buffer_state state;
-       void *mem;
-       unsigned int length;
-       unsigned int bytesused;
-};
-
-#define UVC_QUEUE_DISCONNECTED         (1 << 0)
-#define UVC_QUEUE_DROP_INCOMPLETE      (1 << 1)
-#define UVC_QUEUE_PAUSED               (1 << 2)
-
-struct uvc_video_queue {
-       struct vb2_queue queue;
-       struct mutex mutex;     /* Protects queue */
-
-       unsigned int flags;
-       __u32 sequence;
-
-       unsigned int buf_used;
-
-       spinlock_t irqlock;     /* Protects flags and irqqueue */
-       struct list_head irqqueue;
-};
-
-static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
-{
-       return vb2_is_streaming(&queue->queue);
-}
-
-#endif /* __KERNEL__ */
-
-#endif /* _UVC_QUEUE_H_ */
-
diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/uvc_v4l2.c
deleted file mode 100644 (file)
index ad48e81..0000000
+++ /dev/null
@@ -1,365 +0,0 @@
-/*
- *     uvc_v4l2.c  --  USB Video Class Gadget driver
- *
- *     Copyright (C) 2009-2010
- *         Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- *
- *     This program is free software; you can redistribute it and/or modify
- *     it under the terms of the GNU General Public License as published by
- *     the Free Software Foundation; either version 2 of the License, or
- *     (at your option) any later version.
- */
-
-#include <linux/kernel.h>
-#include <linux/device.h>
-#include <linux/errno.h>
-#include <linux/list.h>
-#include <linux/mutex.h>
-#include <linux/videodev2.h>
-#include <linux/vmalloc.h>
-#include <linux/wait.h>
-
-#include <media/v4l2-dev.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-ioctl.h>
-
-#include "uvc.h"
-#include "uvc_queue.h"
-
-/* --------------------------------------------------------------------------
- * Requests handling
- */
-
-static int
-uvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data)
-{
-       struct usb_composite_dev *cdev = uvc->func.config->cdev;
-       struct usb_request *req = uvc->control_req;
-
-       if (data->length < 0)
-               return usb_ep_set_halt(cdev->gadget->ep0);
-
-       req->length = min_t(unsigned int, uvc->event_length, data->length);
-       req->zero = data->length < uvc->event_length;
-
-       memcpy(req->buf, data->data, req->length);
-
-       return usb_ep_queue(cdev->gadget->ep0, req, GFP_KERNEL);
-}
-
-/* --------------------------------------------------------------------------
- * V4L2
- */
-
-struct uvc_format
-{
-       u8 bpp;
-       u32 fcc;
-};
-
-static struct uvc_format uvc_formats[] = {
-       { 16, V4L2_PIX_FMT_YUYV  },
-       { 0,  V4L2_PIX_FMT_MJPEG },
-};
-
-static int
-uvc_v4l2_get_format(struct uvc_video *video, struct v4l2_format *fmt)
-{
-       fmt->fmt.pix.pixelformat = video->fcc;
-       fmt->fmt.pix.width = video->width;
-       fmt->fmt.pix.height = video->height;
-       fmt->fmt.pix.field = V4L2_FIELD_NONE;
-       fmt->fmt.pix.bytesperline = video->bpp * video->width / 8;
-       fmt->fmt.pix.sizeimage = video->imagesize;
-       fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
-       fmt->fmt.pix.priv = 0;
-
-       return 0;
-}
-
-static int
-uvc_v4l2_set_format(struct uvc_video *video, struct v4l2_format *fmt)
-{
-       struct uvc_format *format;
-       unsigned int imagesize;
-       unsigned int bpl;
-       unsigned int i;
-
-       for (i = 0; i < ARRAY_SIZE(uvc_formats); ++i) {
-               format = &uvc_formats[i];
-               if (format->fcc == fmt->fmt.pix.pixelformat)
-                       break;
-       }
-
-       if (i == ARRAY_SIZE(uvc_formats)) {
-               printk(KERN_INFO "Unsupported format 0x%08x.\n",
-                       fmt->fmt.pix.pixelformat);
-               return -EINVAL;
-       }
-
-       bpl = format->bpp * fmt->fmt.pix.width / 8;
-       imagesize = bpl ? bpl * fmt->fmt.pix.height : fmt->fmt.pix.sizeimage;
-
-       video->fcc = format->fcc;
-       video->bpp = format->bpp;
-       video->width = fmt->fmt.pix.width;
-       video->height = fmt->fmt.pix.height;
-       video->imagesize = imagesize;
-
-       fmt->fmt.pix.field = V4L2_FIELD_NONE;
-       fmt->fmt.pix.bytesperline = bpl;
-       fmt->fmt.pix.sizeimage = imagesize;
-       fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
-       fmt->fmt.pix.priv = 0;
-
-       return 0;
-}
-
-static int
-uvc_v4l2_open(struct file *file)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct uvc_device *uvc = video_get_drvdata(vdev);
-       struct uvc_file_handle *handle;
-
-       handle = kzalloc(sizeof(*handle), GFP_KERNEL);
-       if (handle == NULL)
-               return -ENOMEM;
-
-       v4l2_fh_init(&handle->vfh, vdev);
-       v4l2_fh_add(&handle->vfh);
-
-       handle->device = &uvc->video;
-       file->private_data = &handle->vfh;
-
-       uvc_function_connect(uvc);
-       return 0;
-}
-
-static int
-uvc_v4l2_release(struct file *file)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct uvc_device *uvc = video_get_drvdata(vdev);
-       struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data);
-       struct uvc_video *video = handle->device;
-
-       uvc_function_disconnect(uvc);
-
-       uvc_video_enable(video, 0);
-       uvc_free_buffers(&video->queue);
-
-       file->private_data = NULL;
-       v4l2_fh_del(&handle->vfh);
-       v4l2_fh_exit(&handle->vfh);
-       kfree(handle);
-
-       return 0;
-}
-
-static long
-uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct uvc_device *uvc = video_get_drvdata(vdev);
-       struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data);
-       struct usb_composite_dev *cdev = uvc->func.config->cdev;
-       struct uvc_video *video = &uvc->video;
-       int ret = 0;
-
-       switch (cmd) {
-       /* Query capabilities */
-       case VIDIOC_QUERYCAP:
-       {
-               struct v4l2_capability *cap = arg;
-
-               memset(cap, 0, sizeof *cap);
-               strlcpy(cap->driver, "g_uvc", sizeof(cap->driver));
-               strlcpy(cap->card, cdev->gadget->name, sizeof(cap->card));
-               strlcpy(cap->bus_info, dev_name(&cdev->gadget->dev),
-                       sizeof cap->bus_info);
-               cap->version = DRIVER_VERSION_NUMBER;
-               cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
-               break;
-       }
-
-       /* Get & Set format */
-       case VIDIOC_G_FMT:
-       {
-               struct v4l2_format *fmt = arg;
-
-               if (fmt->type != video->queue.queue.type)
-                       return -EINVAL;
-
-               return uvc_v4l2_get_format(video, fmt);
-       }
-
-       case VIDIOC_S_FMT:
-       {
-               struct v4l2_format *fmt = arg;
-
-               if (fmt->type != video->queue.queue.type)
-                       return -EINVAL;
-
-               return uvc_v4l2_set_format(video, fmt);
-       }
-
-       /* Buffers & streaming */
-       case VIDIOC_REQBUFS:
-       {
-               struct v4l2_requestbuffers *rb = arg;
-
-               if (rb->type != video->queue.queue.type)
-                       return -EINVAL;
-
-               ret = uvc_alloc_buffers(&video->queue, rb);
-               if (ret < 0)
-                       return ret;
-
-               ret = 0;
-               break;
-       }
-
-       case VIDIOC_QUERYBUF:
-       {
-               struct v4l2_buffer *buf = arg;
-
-               return uvc_query_buffer(&video->queue, buf);
-       }
-
-       case VIDIOC_QBUF:
-               if ((ret = uvc_queue_buffer(&video->queue, arg)) < 0)
-                       return ret;
-
-               return uvc_video_pump(video);
-
-       case VIDIOC_DQBUF:
-               return uvc_dequeue_buffer(&video->queue, arg,
-                       file->f_flags & O_NONBLOCK);
-
-       case VIDIOC_STREAMON:
-       {
-               int *type = arg;
-
-               if (*type != video->queue.queue.type)
-                       return -EINVAL;
-
-               /* Enable UVC video. */
-               ret = uvc_video_enable(video, 1);
-               if (ret < 0)
-                       return ret;
-
-               /*
-                * Complete the alternate setting selection setup phase now that
-                * userspace is ready to provide video frames.
-                */
-               uvc_function_setup_continue(uvc);
-               uvc->state = UVC_STATE_STREAMING;
-
-               return 0;
-       }
-
-       case VIDIOC_STREAMOFF:
-       {
-               int *type = arg;
-
-               if (*type != video->queue.queue.type)
-                       return -EINVAL;
-
-               return uvc_video_enable(video, 0);
-       }
-
-       /* Events */
-       case VIDIOC_DQEVENT:
-       {
-               struct v4l2_event *event = arg;
-
-               ret = v4l2_event_dequeue(&handle->vfh, event,
-                                        file->f_flags & O_NONBLOCK);
-               if (ret == 0 && event->type == UVC_EVENT_SETUP) {
-                       struct uvc_event *uvc_event = (void *)&event->u.data;
-
-                       /* Tell the complete callback to generate an event for
-                        * the next request that will be enqueued by
-                        * uvc_event_write.
-                        */
-                       uvc->event_setup_out =
-                               !(uvc_event->req.bRequestType & USB_DIR_IN);
-                       uvc->event_length = uvc_event->req.wLength;
-               }
-
-               return ret;
-       }
-
-       case VIDIOC_SUBSCRIBE_EVENT:
-       {
-               struct v4l2_event_subscription *sub = arg;
-
-               if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST)
-                       return -EINVAL;
-
-               return v4l2_event_subscribe(&handle->vfh, arg, 2, NULL);
-       }
-
-       case VIDIOC_UNSUBSCRIBE_EVENT:
-               return v4l2_event_unsubscribe(&handle->vfh, arg);
-
-       case UVCIOC_SEND_RESPONSE:
-               ret = uvc_send_response(uvc, arg);
-               break;
-
-       default:
-               return -ENOIOCTLCMD;
-       }
-
-       return ret;
-}
-
-static long
-uvc_v4l2_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
-       return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl);
-}
-
-static int
-uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct uvc_device *uvc = video_get_drvdata(vdev);
-
-       return uvc_queue_mmap(&uvc->video.queue, vma);
-}
-
-static unsigned int
-uvc_v4l2_poll(struct file *file, poll_table *wait)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct uvc_device *uvc = video_get_drvdata(vdev);
-
-       return uvc_queue_poll(&uvc->video.queue, file, wait);
-}
-
-#ifndef CONFIG_MMU
-static unsigned long uvc_v4l2_get_unmapped_area(struct file *file,
-               unsigned long addr, unsigned long len, unsigned long pgoff,
-               unsigned long flags)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct uvc_device *uvc = video_get_drvdata(vdev);
-
-       return uvc_queue_get_unmapped_area(&uvc->video.queue, pgoff);
-}
-#endif
-
-static struct v4l2_file_operations uvc_v4l2_fops = {
-       .owner          = THIS_MODULE,
-       .open           = uvc_v4l2_open,
-       .release        = uvc_v4l2_release,
-       .ioctl          = uvc_v4l2_ioctl,
-       .mmap           = uvc_v4l2_mmap,
-       .poll           = uvc_v4l2_poll,
-#ifndef CONFIG_MMU
-       .get_unmapped_area = uvc_v4l2_get_unmapped_area,
-#endif
-};
-
diff --git a/drivers/usb/gadget/uvc_video.c b/drivers/usb/gadget/uvc_video.c
deleted file mode 100644 (file)
index 71e896d..0000000
+++ /dev/null
@@ -1,394 +0,0 @@
-/*
- *     uvc_video.c  --  USB Video Class Gadget driver
- *
- *     Copyright (C) 2009-2010
- *         Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- *
- *     This program is free software; you can redistribute it and/or modify
- *     it under the terms of the GNU General Public License as published by
- *     the Free Software Foundation; either version 2 of the License, or
- *     (at your option) any later version.
- */
-
-#include <linux/kernel.h>
-#include <linux/device.h>
-#include <linux/errno.h>
-#include <linux/usb/ch9.h>
-#include <linux/usb/gadget.h>
-
-#include <media/v4l2-dev.h>
-
-#include "uvc.h"
-#include "uvc_queue.h"
-
-/* --------------------------------------------------------------------------
- * Video codecs
- */
-
-static int
-uvc_video_encode_header(struct uvc_video *video, struct uvc_buffer *buf,
-               u8 *data, int len)
-{
-       data[0] = 2;
-       data[1] = UVC_STREAM_EOH | video->fid;
-
-       if (buf->bytesused - video->queue.buf_used <= len - 2)
-               data[1] |= UVC_STREAM_EOF;
-
-       return 2;
-}
-
-static int
-uvc_video_encode_data(struct uvc_video *video, struct uvc_buffer *buf,
-               u8 *data, int len)
-{
-       struct uvc_video_queue *queue = &video->queue;
-       unsigned int nbytes;
-       void *mem;
-
-       /* Copy video data to the USB buffer. */
-       mem = buf->mem + queue->buf_used;
-       nbytes = min((unsigned int)len, buf->bytesused - queue->buf_used);
-
-       memcpy(data, mem, nbytes);
-       queue->buf_used += nbytes;
-
-       return nbytes;
-}
-
-static void
-uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video,
-               struct uvc_buffer *buf)
-{
-       void *mem = req->buf;
-       int len = video->req_size;
-       int ret;
-
-       /* Add a header at the beginning of the payload. */
-       if (video->payload_size == 0) {
-               ret = uvc_video_encode_header(video, buf, mem, len);
-               video->payload_size += ret;
-               mem += ret;
-               len -= ret;
-       }
-
-       /* Process video data. */
-       len = min((int)(video->max_payload_size - video->payload_size), len);
-       ret = uvc_video_encode_data(video, buf, mem, len);
-
-       video->payload_size += ret;
-       len -= ret;
-
-       req->length = video->req_size - len;
-       req->zero = video->payload_size == video->max_payload_size;
-
-       if (buf->bytesused == video->queue.buf_used) {
-               video->queue.buf_used = 0;
-               buf->state = UVC_BUF_STATE_DONE;
-               uvc_queue_next_buffer(&video->queue, buf);
-               video->fid ^= UVC_STREAM_FID;
-
-               video->payload_size = 0;
-       }
-
-       if (video->payload_size == video->max_payload_size ||
-           buf->bytesused == video->queue.buf_used)
-               video->payload_size = 0;
-}
-
-static void
-uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video,
-               struct uvc_buffer *buf)
-{
-       void *mem = req->buf;
-       int len = video->req_size;
-       int ret;
-
-       /* Add the header. */
-       ret = uvc_video_encode_header(video, buf, mem, len);
-       mem += ret;
-       len -= ret;
-
-       /* Process video data. */
-       ret = uvc_video_encode_data(video, buf, mem, len);
-       len -= ret;
-
-       req->length = video->req_size - len;
-
-       if (buf->bytesused == video->queue.buf_used) {
-               video->queue.buf_used = 0;
-               buf->state = UVC_BUF_STATE_DONE;
-               uvc_queue_next_buffer(&video->queue, buf);
-               video->fid ^= UVC_STREAM_FID;
-       }
-}
-
-/* --------------------------------------------------------------------------
- * Request handling
- */
-
-/*
- * I somehow feel that synchronisation won't be easy to achieve here. We have
- * three events that control USB requests submission:
- *
- * - USB request completion: the completion handler will resubmit the request
- *   if a video buffer is available.
- *
- * - USB interface setting selection: in response to a SET_INTERFACE request,
- *   the handler will start streaming if a video buffer is available and if
- *   video is not currently streaming.
- *
- * - V4L2 buffer queueing: the driver will start streaming if video is not
- *   currently streaming.
- *
- * Race conditions between those 3 events might lead to deadlocks or other
- * nasty side effects.
- *
- * The "video currently streaming" condition can't be detected by the irqqueue
- * being empty, as a request can still be in flight. A separate "queue paused"
- * flag is thus needed.
- *
- * The paused flag will be set when we try to retrieve the irqqueue head if the
- * queue is empty, and cleared when we queue a buffer.
- *
- * The USB request completion handler will get the buffer at the irqqueue head
- * under protection of the queue spinlock. If the queue is empty, the streaming
- * paused flag will be set. Right after releasing the spinlock a userspace
- * application can queue a buffer. The flag will then cleared, and the ioctl
- * handler will restart the video stream.
- */
-static void
-uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       struct uvc_video *video = req->context;
-       struct uvc_video_queue *queue = &video->queue;
-       struct uvc_buffer *buf;
-       unsigned long flags;
-       int ret;
-
-       switch (req->status) {
-       case 0:
-               break;
-
-       case -ESHUTDOWN:        /* disconnect from host. */
-               printk(KERN_INFO "VS request cancelled.\n");
-               uvc_queue_cancel(queue, 1);
-               goto requeue;
-
-       default:
-               printk(KERN_INFO "VS request completed with status %d.\n",
-                       req->status);
-               uvc_queue_cancel(queue, 0);
-               goto requeue;
-       }
-
-       spin_lock_irqsave(&video->queue.irqlock, flags);
-       buf = uvc_queue_head(&video->queue);
-       if (buf == NULL) {
-               spin_unlock_irqrestore(&video->queue.irqlock, flags);
-               goto requeue;
-       }
-
-       video->encode(req, video, buf);
-
-       if ((ret = usb_ep_queue(ep, req, GFP_ATOMIC)) < 0) {
-               printk(KERN_INFO "Failed to queue request (%d).\n", ret);
-               usb_ep_set_halt(ep);
-               spin_unlock_irqrestore(&video->queue.irqlock, flags);
-               goto requeue;
-       }
-       spin_unlock_irqrestore(&video->queue.irqlock, flags);
-
-       return;
-
-requeue:
-       spin_lock_irqsave(&video->req_lock, flags);
-       list_add_tail(&req->list, &video->req_free);
-       spin_unlock_irqrestore(&video->req_lock, flags);
-}
-
-static int
-uvc_video_free_requests(struct uvc_video *video)
-{
-       unsigned int i;
-
-       for (i = 0; i < UVC_NUM_REQUESTS; ++i) {
-               if (video->req[i]) {
-                       usb_ep_free_request(video->ep, video->req[i]);
-                       video->req[i] = NULL;
-               }
-
-               if (video->req_buffer[i]) {
-                       kfree(video->req_buffer[i]);
-                       video->req_buffer[i] = NULL;
-               }
-       }
-
-       INIT_LIST_HEAD(&video->req_free);
-       video->req_size = 0;
-       return 0;
-}
-
-static int
-uvc_video_alloc_requests(struct uvc_video *video)
-{
-       unsigned int req_size;
-       unsigned int i;
-       int ret = -ENOMEM;
-
-       BUG_ON(video->req_size);
-
-       req_size = video->ep->maxpacket
-                * max_t(unsigned int, video->ep->maxburst, 1)
-                * (video->ep->mult + 1);
-
-       for (i = 0; i < UVC_NUM_REQUESTS; ++i) {
-               video->req_buffer[i] = kmalloc(req_size, GFP_KERNEL);
-               if (video->req_buffer[i] == NULL)
-                       goto error;
-
-               video->req[i] = usb_ep_alloc_request(video->ep, GFP_KERNEL);
-               if (video->req[i] == NULL)
-                       goto error;
-
-               video->req[i]->buf = video->req_buffer[i];
-               video->req[i]->length = 0;
-               video->req[i]->complete = uvc_video_complete;
-               video->req[i]->context = video;
-
-               list_add_tail(&video->req[i]->list, &video->req_free);
-       }
-
-       video->req_size = req_size;
-
-       return 0;
-
-error:
-       uvc_video_free_requests(video);
-       return ret;
-}
-
-/* --------------------------------------------------------------------------
- * Video streaming
- */
-
-/*
- * uvc_video_pump - Pump video data into the USB requests
- *
- * This function fills the available USB requests (listed in req_free) with
- * video data from the queued buffers.
- */
-static int
-uvc_video_pump(struct uvc_video *video)
-{
-       struct usb_request *req;
-       struct uvc_buffer *buf;
-       unsigned long flags;
-       int ret;
-
-       /* FIXME TODO Race between uvc_video_pump and requests completion
-        * handler ???
-        */
-
-       while (1) {
-               /* Retrieve the first available USB request, protected by the
-                * request lock.
-                */
-               spin_lock_irqsave(&video->req_lock, flags);
-               if (list_empty(&video->req_free)) {
-                       spin_unlock_irqrestore(&video->req_lock, flags);
-                       return 0;
-               }
-               req = list_first_entry(&video->req_free, struct usb_request,
-                                       list);
-               list_del(&req->list);
-               spin_unlock_irqrestore(&video->req_lock, flags);
-
-               /* Retrieve the first available video buffer and fill the
-                * request, protected by the video queue irqlock.
-                */
-               spin_lock_irqsave(&video->queue.irqlock, flags);
-               buf = uvc_queue_head(&video->queue);
-               if (buf == NULL) {
-                       spin_unlock_irqrestore(&video->queue.irqlock, flags);
-                       break;
-               }
-
-               video->encode(req, video, buf);
-
-               /* Queue the USB request */
-               ret = usb_ep_queue(video->ep, req, GFP_ATOMIC);
-               if (ret < 0) {
-                       printk(KERN_INFO "Failed to queue request (%d)\n", ret);
-                       usb_ep_set_halt(video->ep);
-                       spin_unlock_irqrestore(&video->queue.irqlock, flags);
-                       break;
-               }
-               spin_unlock_irqrestore(&video->queue.irqlock, flags);
-       }
-
-       spin_lock_irqsave(&video->req_lock, flags);
-       list_add_tail(&req->list, &video->req_free);
-       spin_unlock_irqrestore(&video->req_lock, flags);
-       return 0;
-}
-
-/*
- * Enable or disable the video stream.
- */
-static int
-uvc_video_enable(struct uvc_video *video, int enable)
-{
-       unsigned int i;
-       int ret;
-
-       if (video->ep == NULL) {
-               printk(KERN_INFO "Video enable failed, device is "
-                       "uninitialized.\n");
-               return -ENODEV;
-       }
-
-       if (!enable) {
-               for (i = 0; i < UVC_NUM_REQUESTS; ++i)
-                       usb_ep_dequeue(video->ep, video->req[i]);
-
-               uvc_video_free_requests(video);
-               uvc_queue_enable(&video->queue, 0);
-               return 0;
-       }
-
-       if ((ret = uvc_queue_enable(&video->queue, 1)) < 0)
-               return ret;
-
-       if ((ret = uvc_video_alloc_requests(video)) < 0)
-               return ret;
-
-       if (video->max_payload_size) {
-               video->encode = uvc_video_encode_bulk;
-               video->payload_size = 0;
-       } else
-               video->encode = uvc_video_encode_isoc;
-
-       return uvc_video_pump(video);
-}
-
-/*
- * Initialize the UVC video stream.
- */
-static int
-uvc_video_init(struct uvc_video *video)
-{
-       INIT_LIST_HEAD(&video->req_free);
-       spin_lock_init(&video->req_lock);
-
-       video->fcc = V4L2_PIX_FMT_YUYV;
-       video->bpp = 16;
-       video->width = 320;
-       video->height = 240;
-       video->imagesize = 320 * 240 * 2;
-
-       /* Initialize the video buffers queue. */
-       uvc_queue_init(&video->queue, V4L2_BUF_TYPE_VIDEO_OUTPUT);
-       return 0;
-}
-