summaryrefslogtreecommitdiff
path: root/drivers/usb/gadget/legacy
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/gadget/legacy')
-rw-r--r--drivers/usb/gadget/legacy/Kconfig481
-rw-r--r--drivers/usb/gadget/legacy/Makefile44
-rw-r--r--drivers/usb/gadget/legacy/acm_ms.c274
-rw-r--r--drivers/usb/gadget/legacy/audio.c309
-rw-r--r--drivers/usb/gadget/legacy/cdc2.c238
-rw-r--r--drivers/usb/gadget/legacy/dbgp.c439
-rw-r--r--drivers/usb/gadget/legacy/ether.c482
-rw-r--r--drivers/usb/gadget/legacy/g_ffs.c586
-rw-r--r--drivers/usb/gadget/legacy/gmidi.c197
-rw-r--r--drivers/usb/gadget/legacy/hid.c298
-rw-r--r--drivers/usb/gadget/legacy/inode.c2064
-rw-r--r--drivers/usb/gadget/legacy/mass_storage.c276
-rw-r--r--drivers/usb/gadget/legacy/multi.c510
-rw-r--r--drivers/usb/gadget/legacy/ncm.c211
-rw-r--r--drivers/usb/gadget/legacy/nokia.c350
-rw-r--r--drivers/usb/gadget/legacy/printer.c215
-rw-r--r--drivers/usb/gadget/legacy/serial.c276
-rw-r--r--drivers/usb/gadget/legacy/tcm_usb_gadget.c2433
-rw-r--r--drivers/usb/gadget/legacy/tcm_usb_gadget.h145
-rw-r--r--drivers/usb/gadget/legacy/webcam.c440
-rw-r--r--drivers/usb/gadget/legacy/zero.c417
21 files changed, 10685 insertions, 0 deletions
diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig
new file mode 100644
index 000000000..d5a7102de
--- /dev/null
+++ b/drivers/usb/gadget/legacy/Kconfig
@@ -0,0 +1,481 @@
+#
+# USB Gadget support on a system involves
+# (a) a peripheral controller, and
+# (b) the gadget driver using it.
+#
+# NOTE: Gadget support ** DOES NOT ** depend on host-side CONFIG_USB !!
+#
+# - Host systems (like PCs) need CONFIG_USB (with "A" jacks).
+# - Peripherals (like PDAs) need CONFIG_USB_GADGET (with "B" jacks).
+# - Some systems have both kinds of controllers.
+#
+# With help from a special transceiver and a "Mini-AB" jack, systems with
+# both kinds of controller can also support "USB On-the-Go" (CONFIG_USB_OTG).
+#
+
+config USB_ZERO
+ tristate "Gadget Zero (DEVELOPMENT)"
+ select USB_LIBCOMPOSITE
+ select USB_F_SS_LB
+ help
+ Gadget Zero is a two-configuration device. It either sinks and
+ sources bulk data; or it loops back a configurable number of
+ transfers. It also implements control requests, for "chapter 9"
+ conformance. The driver needs only two bulk-capable endpoints, so
+ it can work on top of most device-side usb controllers. It's
+ useful for testing, and is also a working example showing how
+ USB "gadget drivers" can be written.
+
+ Make this be the first driver you try using on top of any new
+ USB peripheral controller driver. Then you can use host-side
+ test software, like the "usbtest" driver, to put your hardware
+ and its driver through a basic set of functional tests.
+
+ Gadget Zero also works with the host-side "usb-skeleton" driver,
+ and with many kinds of host-side test software. You may need
+ to tweak product and vendor IDs before host software knows about
+ this device, and arrange to select an appropriate configuration.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_zero".
+
+config USB_ZERO_HNPTEST
+ bool "HNP Test Device"
+ depends on USB_ZERO && USB_OTG
+ help
+ You can configure this device to enumerate using the device
+ identifiers of the USB-OTG test device. That means that when
+ this gadget connects to another OTG device, with this one using
+ the "B-Peripheral" role, that device will use HNP to let this
+ one serve as the USB host instead (in the "B-Host" role).
+
+config USB_AUDIO
+ tristate "Audio Gadget"
+ depends on SND
+ select USB_LIBCOMPOSITE
+ select SND_PCM
+ select USB_F_UAC1 if GADGET_UAC1
+ select USB_F_UAC2 if !GADGET_UAC1
+ help
+ This Gadget Audio driver is compatible with USB Audio Class
+ specification 2.0. It implements 1 AudioControl interface,
+ 1 AudioStreaming Interface each for USB-OUT and USB-IN.
+ Number of channels, sample rate and sample size can be
+ specified as module parameters.
+ This driver doesn't expect any real Audio codec to be present
+ on the device - the audio streams are simply sinked to and
+ sourced from a virtual ALSA sound card created. The user-space
+ application may choose to do whatever it wants with the data
+ received from the USB Host and choose to provide whatever it
+ wants as audio data to the USB Host.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_audio".
+
+config GADGET_UAC1
+ bool "UAC 1.0 (Legacy)"
+ depends on USB_AUDIO
+ help
+ If you instead want older UAC Spec-1.0 driver that also has audio
+ paths hardwired to the Audio codec chip on-board and doesn't work
+ without one.
+
+config USB_ETH
+ tristate "Ethernet Gadget (with CDC Ethernet support)"
+ depends on NET
+ select USB_LIBCOMPOSITE
+ select USB_U_ETHER
+ select USB_F_ECM
+ select USB_F_SUBSET
+ select CRC32
+ help
+ This driver implements Ethernet style communication, in one of
+ several ways:
+
+ - The "Communication Device Class" (CDC) Ethernet Control Model.
+ That protocol is often avoided with pure Ethernet adapters, in
+ favor of simpler vendor-specific hardware, but is widely
+ supported by firmware for smart network devices.
+
+ - On hardware can't implement that protocol, a simple CDC subset
+ is used, placing fewer demands on USB.
+
+ - CDC Ethernet Emulation Model (EEM) is a newer standard that has
+ a simpler interface that can be used by more USB hardware.
+
+ RNDIS support is an additional option, more demanding than than
+ subset.
+
+ Within the USB device, this gadget driver exposes a network device
+ "usbX", where X depends on what other networking devices you have.
+ Treat it like a two-node Ethernet link: host, and gadget.
+
+ The Linux-USB host-side "usbnet" driver interoperates with this
+ driver, so that deep I/O queues can be supported. On 2.4 kernels,
+ use "CDCEther" instead, if you're using the CDC option. That CDC
+ mode should also interoperate with standard CDC Ethernet class
+ drivers on other host operating systems.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_ether".
+
+config USB_ETH_RNDIS
+ bool "RNDIS support"
+ depends on USB_ETH
+ select USB_LIBCOMPOSITE
+ select USB_F_RNDIS
+ default y
+ help
+ Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol,
+ and Microsoft provides redistributable binary RNDIS drivers for
+ older versions of Windows.
+
+ If you say "y" here, the Ethernet gadget driver will try to provide
+ a second device configuration, supporting RNDIS to talk to such
+ Microsoft USB hosts.
+
+ To make MS-Windows work with this, use Documentation/usb/linux.inf
+ as the "driver info file". For versions of MS-Windows older than
+ XP, you'll need to download drivers from Microsoft's website; a URL
+ is given in comments found in that info file.
+
+config USB_ETH_EEM
+ bool "Ethernet Emulation Model (EEM) support"
+ depends on USB_ETH
+ select USB_LIBCOMPOSITE
+ select USB_F_EEM
+ default n
+ help
+ CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM
+ and therefore can be supported by more hardware. Technically ECM and
+ EEM are designed for different applications. The ECM model extends
+ the network interface to the target (e.g. a USB cable modem), and the
+ EEM model is for mobile devices to communicate with hosts using
+ ethernet over USB. For Linux gadgets, however, the interface with
+ the host is the same (a usbX device), so the differences are minimal.
+
+ If you say "y" here, the Ethernet gadget driver will use the EEM
+ protocol rather than ECM. If unsure, say "n".
+
+config USB_G_NCM
+ tristate "Network Control Model (NCM) support"
+ depends on NET
+ select USB_LIBCOMPOSITE
+ select USB_U_ETHER
+ select USB_F_NCM
+ select CRC32
+ help
+ This driver implements USB CDC NCM subclass standard. NCM is
+ an advanced protocol for Ethernet encapsulation, allows grouping
+ of several ethernet frames into one USB transfer and different
+ alignment possibilities.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_ncm".
+
+config USB_GADGETFS
+ tristate "Gadget Filesystem"
+ help
+ This driver provides a filesystem based API that lets user mode
+ programs implement a single-configuration USB device, including
+ endpoint I/O and control requests that don't relate to enumeration.
+ All endpoints, transfer speeds, and transfer types supported by
+ the hardware are available, through read() and write() calls.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "gadgetfs".
+
+config USB_FUNCTIONFS
+ tristate "Function Filesystem"
+ select USB_LIBCOMPOSITE
+ select USB_F_FS
+ select USB_FUNCTIONFS_GENERIC if !(USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS)
+ help
+ The Function Filesystem (FunctionFS) lets one create USB
+ composite functions in user space in the same way GadgetFS
+ lets one create USB gadgets in user space. This allows creation
+ of composite gadgets such that some of the functions are
+ implemented in kernel space (for instance Ethernet, serial or
+ mass storage) and other are implemented in user space.
+
+ If you say "y" or "m" here you will be able what kind of
+ configurations the gadget will provide.
+
+ Say "y" to link the driver statically, or "m" to build
+ a dynamically linked module called "g_ffs".
+
+config USB_FUNCTIONFS_ETH
+ bool "Include configuration with CDC ECM (Ethernet)"
+ depends on USB_FUNCTIONFS && NET
+ select USB_U_ETHER
+ select USB_F_ECM
+ select USB_F_SUBSET
+ help
+ Include a configuration with CDC ECM function (Ethernet) and the
+ Function Filesystem.
+
+config USB_FUNCTIONFS_RNDIS
+ bool "Include configuration with RNDIS (Ethernet)"
+ depends on USB_FUNCTIONFS && NET
+ select USB_U_ETHER
+ select USB_F_RNDIS
+ help
+ Include a configuration with RNDIS function (Ethernet) and the Filesystem.
+
+config USB_FUNCTIONFS_GENERIC
+ bool "Include 'pure' configuration"
+ depends on USB_FUNCTIONFS
+ help
+ Include a configuration with the Function Filesystem alone with
+ no Ethernet interface.
+
+config USB_MASS_STORAGE
+ tristate "Mass Storage Gadget"
+ depends on BLOCK
+ select USB_LIBCOMPOSITE
+ select USB_F_MASS_STORAGE
+ help
+ The Mass Storage Gadget acts as a USB Mass Storage disk drive.
+ As its storage repository it can use a regular file or a block
+ device (in much the same way as the "loop" device driver),
+ specified as a module parameter or sysfs option.
+
+ This driver is a replacement for now removed File-backed
+ Storage Gadget (g_file_storage).
+
+ Say "y" to link the driver statically, or "m" to build
+ a dynamically linked module called "g_mass_storage".
+
+config USB_GADGET_TARGET
+ tristate "USB Gadget Target Fabric Module"
+ depends on TARGET_CORE
+ select USB_LIBCOMPOSITE
+ help
+ This fabric is an USB gadget. Two USB protocols are supported that is
+ BBB or BOT (Bulk Only Transport) and UAS (USB Attached SCSI). BOT is
+ advertised on alternative interface 0 (primary) and UAS is on
+ alternative interface 1. Both protocols can work on USB2.0 and USB3.0.
+ UAS utilizes the USB 3.0 feature called streams support.
+
+config USB_G_SERIAL
+ tristate "Serial Gadget (with CDC ACM and CDC OBEX support)"
+ depends on TTY
+ select USB_U_SERIAL
+ select USB_F_ACM
+ select USB_F_SERIAL
+ select USB_F_OBEX
+ select USB_LIBCOMPOSITE
+ help
+ The Serial Gadget talks to the Linux-USB generic serial driver.
+ This driver supports a CDC-ACM module option, which can be used
+ to interoperate with MS-Windows hosts or with the Linux-USB
+ "cdc-acm" driver.
+
+ This driver also supports a CDC-OBEX option. You will need a
+ user space OBEX server talking to /dev/ttyGS*, since the kernel
+ itself doesn't implement the OBEX protocol.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_serial".
+
+ For more information, see Documentation/usb/gadget_serial.txt
+ which includes instructions and a "driver info file" needed to
+ make MS-Windows work with CDC ACM.
+
+config USB_MIDI_GADGET
+ tristate "MIDI Gadget"
+ depends on SND
+ select USB_LIBCOMPOSITE
+ select SND_RAWMIDI
+ select USB_F_MIDI
+ help
+ The MIDI Gadget acts as a USB Audio device, with one MIDI
+ input and one MIDI output. These MIDI jacks appear as
+ a sound "card" in the ALSA sound system. Other MIDI
+ connections can then be made on the gadget system, using
+ ALSA's aconnect utility etc.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_midi".
+
+config USB_G_PRINTER
+ tristate "Printer Gadget"
+ select USB_LIBCOMPOSITE
+ select USB_F_PRINTER
+ help
+ The Printer Gadget channels data between the USB host and a
+ userspace program driving the print engine. The user space
+ program reads and writes the device file /dev/g_printer to
+ receive or send printer data. It can use ioctl calls to
+ the device file to get or set printer status.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_printer".
+
+ For more information, see Documentation/usb/gadget_printer.txt
+ which includes sample code for accessing the device file.
+
+if TTY
+
+config USB_CDC_COMPOSITE
+ tristate "CDC Composite Device (Ethernet and ACM)"
+ depends on NET
+ select USB_LIBCOMPOSITE
+ select USB_U_SERIAL
+ select USB_U_ETHER
+ select USB_F_ACM
+ select USB_F_ECM
+ help
+ This driver provides two functions in one configuration:
+ a CDC Ethernet (ECM) link, and a CDC ACM (serial port) link.
+
+ This driver requires four bulk and two interrupt endpoints,
+ plus the ability to handle altsettings. Not all peripheral
+ controllers are that capable.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module.
+
+config USB_G_NOKIA
+ tristate "Nokia composite gadget"
+ depends on PHONET
+ select USB_LIBCOMPOSITE
+ select USB_U_SERIAL
+ select USB_U_ETHER
+ select USB_F_ACM
+ select USB_F_OBEX
+ select USB_F_PHONET
+ select USB_F_ECM
+ help
+ The Nokia composite gadget provides support for acm, obex
+ and phonet in only one composite gadget driver.
+
+ It's only really useful for N900 hardware. If you're building
+ a kernel for N900, say Y or M here. If unsure, say N.
+
+config USB_G_ACM_MS
+ tristate "CDC Composite Device (ACM and mass storage)"
+ depends on BLOCK
+ select USB_LIBCOMPOSITE
+ select USB_U_SERIAL
+ select USB_F_ACM
+ select USB_F_MASS_STORAGE
+ help
+ This driver provides two functions in one configuration:
+ a mass storage, and a CDC ACM (serial port) link.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_acm_ms".
+
+config USB_G_MULTI
+ tristate "Multifunction Composite Gadget"
+ depends on BLOCK && NET
+ select USB_G_MULTI_CDC if !USB_G_MULTI_RNDIS
+ select USB_LIBCOMPOSITE
+ select USB_U_SERIAL
+ select USB_U_ETHER
+ select USB_F_ACM
+ select USB_F_MASS_STORAGE
+ help
+ The Multifunction Composite Gadget provides Ethernet (RNDIS
+ and/or CDC Ethernet), mass storage and ACM serial link
+ interfaces.
+
+ You will be asked to choose which of the two configurations is
+ to be available in the gadget. At least one configuration must
+ be chosen to make the gadget usable. Selecting more than one
+ configuration will prevent Windows from automatically detecting
+ the gadget as a composite gadget, so an INF file will be needed to
+ use the gadget.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_multi".
+
+config USB_G_MULTI_RNDIS
+ bool "RNDIS + CDC Serial + Storage configuration"
+ depends on USB_G_MULTI
+ select USB_F_RNDIS
+ default y
+ help
+ This option enables a configuration with RNDIS, CDC Serial and
+ Mass Storage functions available in the Multifunction Composite
+ Gadget. This is the configuration dedicated for Windows since RNDIS
+ is Microsoft's protocol.
+
+ If unsure, say "y".
+
+config USB_G_MULTI_CDC
+ bool "CDC Ethernet + CDC Serial + Storage configuration"
+ depends on USB_G_MULTI
+ default n
+ select USB_F_ECM
+ help
+ This option enables a configuration with CDC Ethernet (ECM), CDC
+ Serial and Mass Storage functions available in the Multifunction
+ Composite Gadget.
+
+ If unsure, say "y".
+
+endif # TTY
+
+config USB_G_HID
+ tristate "HID Gadget"
+ select USB_LIBCOMPOSITE
+ select USB_F_HID
+ help
+ The HID gadget driver provides generic emulation of USB
+ Human Interface Devices (HID).
+
+ For more information, see Documentation/usb/gadget_hid.txt which
+ includes sample code for accessing the device files.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_hid".
+
+# Standalone / single function gadgets
+config USB_G_DBGP
+ tristate "EHCI Debug Device Gadget"
+ depends on TTY
+ select USB_LIBCOMPOSITE
+ help
+ This gadget emulates an EHCI Debug device. This is useful when you want
+ to interact with an EHCI Debug Port.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_dbgp".
+
+if USB_G_DBGP
+choice
+ prompt "EHCI Debug Device mode"
+ default USB_G_DBGP_SERIAL
+
+config USB_G_DBGP_PRINTK
+ depends on USB_G_DBGP
+ bool "printk"
+ help
+ Directly printk() received data. No interaction.
+
+config USB_G_DBGP_SERIAL
+ depends on USB_G_DBGP
+ select USB_U_SERIAL
+ bool "serial"
+ help
+ Userland can interact using /dev/ttyGSxxx.
+endchoice
+endif
+
+# put drivers that need isochronous transfer support (for audio
+# or video class gadget drivers), or specific hardware, here.
+config USB_G_WEBCAM
+ tristate "USB Webcam Gadget"
+ depends on VIDEO_DEV
+ select USB_LIBCOMPOSITE
+ select VIDEOBUF2_VMALLOC
+ select USB_F_UVC
+ help
+ The Webcam Gadget acts as a composite USB Audio and Video Class
+ device. It provides a userspace API to process UVC control requests
+ and stream video data to the host.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_webcam".
diff --git a/drivers/usb/gadget/legacy/Makefile b/drivers/usb/gadget/legacy/Makefile
new file mode 100644
index 000000000..7f485f257
--- /dev/null
+++ b/drivers/usb/gadget/legacy/Makefile
@@ -0,0 +1,44 @@
+#
+# USB gadget drivers
+#
+
+ccflags-y := -I$(srctree)/drivers/usb/gadget/
+ccflags-y += -I$(srctree)/drivers/usb/gadget/udc/
+ccflags-y += -I$(srctree)/drivers/usb/gadget/function/
+
+g_zero-y := zero.o
+g_audio-y := audio.o
+g_ether-y := ether.o
+g_serial-y := serial.o
+g_midi-y := gmidi.o
+gadgetfs-y := inode.o
+g_mass_storage-y := mass_storage.o
+g_printer-y := printer.o
+g_cdc-y := cdc2.o
+g_multi-y := multi.o
+g_hid-y := hid.o
+g_dbgp-y := dbgp.o
+g_nokia-y := nokia.o
+g_webcam-y := webcam.o
+g_ncm-y := ncm.o
+g_acm_ms-y := acm_ms.o
+g_tcm_usb_gadget-y := tcm_usb_gadget.o
+
+obj-$(CONFIG_USB_ZERO) += g_zero.o
+obj-$(CONFIG_USB_AUDIO) += g_audio.o
+obj-$(CONFIG_USB_ETH) += g_ether.o
+obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o
+obj-$(CONFIG_USB_FUNCTIONFS) += g_ffs.o
+obj-$(CONFIG_USB_MASS_STORAGE) += g_mass_storage.o
+obj-$(CONFIG_USB_G_SERIAL) += g_serial.o
+obj-$(CONFIG_USB_G_PRINTER) += g_printer.o
+obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o
+obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o
+obj-$(CONFIG_USB_G_HID) += g_hid.o
+obj-$(CONFIG_USB_G_DBGP) += g_dbgp.o
+obj-$(CONFIG_USB_G_MULTI) += g_multi.o
+obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o
+obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o
+obj-$(CONFIG_USB_G_NCM) += g_ncm.o
+obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o
+obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o
diff --git a/drivers/usb/gadget/legacy/acm_ms.c b/drivers/usb/gadget/legacy/acm_ms.c
new file mode 100644
index 000000000..1194b09ae
--- /dev/null
+++ b/drivers/usb/gadget/legacy/acm_ms.c
@@ -0,0 +1,274 @@
+/*
+ * acm_ms.c -- Composite driver, with ACM and mass storage support
+ *
+ * Copyright (C) 2008 David Brownell
+ * Copyright (C) 2008 Nokia Corporation
+ * Author: David Brownell
+ * Modified: Klaus Schwarzkopf <schwarzkopf@sensortherm.de>
+ *
+ * Heavily based on multi.c and cdc2.c
+ *
+ * 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 "u_serial.h"
+
+#define DRIVER_DESC "Composite Gadget (ACM + MS)"
+#define DRIVER_VERSION "2011/10/10"
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
+ * Instead: allocate your own, using normal USB-IF procedures.
+ */
+#define ACM_MS_VENDOR_NUM 0x1d6b /* Linux Foundation */
+#define ACM_MS_PRODUCT_NUM 0x0106 /* Composite Gadget: ACM + MS*/
+
+#include "f_mass_storage.h"
+
+/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
+
+static struct usb_device_descriptor device_desc = {
+ .bLength = sizeof device_desc,
+ .bDescriptorType = USB_DT_DEVICE,
+
+ .bcdUSB = cpu_to_le16(0x0200),
+
+ .bDeviceClass = USB_CLASS_MISC /* 0xEF */,
+ .bDeviceSubClass = 2,
+ .bDeviceProtocol = 1,
+
+ /* .bMaxPacketSize0 = f(hardware) */
+
+ /* Vendor and product id can be overridden by module parameters. */
+ .idVendor = cpu_to_le16(ACM_MS_VENDOR_NUM),
+ .idProduct = cpu_to_le16(ACM_MS_PRODUCT_NUM),
+ /* .bcdDevice = f(hardware) */
+ /* .iManufacturer = DYNAMIC */
+ /* .iProduct = DYNAMIC */
+ /* NO SERIAL NUMBER */
+ /*.bNumConfigurations = DYNAMIC*/
+};
+
+static struct usb_otg_descriptor otg_descriptor = {
+ .bLength = sizeof otg_descriptor,
+ .bDescriptorType = USB_DT_OTG,
+
+ /*
+ * REVISIT SRP-only hardware is possible, although
+ * it would not be called "OTG" ...
+ */
+ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
+};
+
+static const struct usb_descriptor_header *otg_desc[] = {
+ (struct usb_descriptor_header *) &otg_descriptor,
+ NULL,
+};
+
+/* string IDs are assigned dynamically */
+static struct usb_string strings_dev[] = {
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+ .language = 0x0409, /* en-us */
+ .strings = strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+ &stringtab_dev,
+ NULL,
+};
+
+/****************************** Configurations ******************************/
+
+static struct fsg_module_parameters fsg_mod_data = { .stall = 1 };
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
+
+#else
+
+/*
+ * Number of buffers we will use.
+ * 2 is usually enough for good buffering pipeline
+ */
+#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
+
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
+
+FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
+
+/*-------------------------------------------------------------------------*/
+static struct usb_function *f_acm;
+static struct usb_function_instance *f_acm_inst;
+
+static struct usb_function_instance *fi_msg;
+static struct usb_function *f_msg;
+
+/*
+ * We _always_ have both ACM and mass storage functions.
+ */
+static int acm_ms_do_config(struct usb_configuration *c)
+{
+ struct fsg_opts *opts;
+ int status;
+
+ if (gadget_is_otg(c->cdev->gadget)) {
+ c->descriptors = otg_desc;
+ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
+ opts = fsg_opts_from_func_inst(fi_msg);
+
+ f_acm = usb_get_function(f_acm_inst);
+ if (IS_ERR(f_acm))
+ return PTR_ERR(f_acm);
+
+ f_msg = usb_get_function(fi_msg);
+ if (IS_ERR(f_msg)) {
+ status = PTR_ERR(f_msg);
+ goto put_acm;
+ }
+
+ status = usb_add_function(c, f_acm);
+ if (status < 0)
+ goto put_msg;
+
+ status = fsg_common_run_thread(opts->common);
+ if (status)
+ goto remove_acm;
+
+ status = usb_add_function(c, f_msg);
+ if (status)
+ goto remove_acm;
+
+ return 0;
+remove_acm:
+ usb_remove_function(c, f_acm);
+put_msg:
+ usb_put_function(f_msg);
+put_acm:
+ usb_put_function(f_acm);
+ return status;
+}
+
+static struct usb_configuration acm_ms_config_driver = {
+ .label = DRIVER_DESC,
+ .bConfigurationValue = 1,
+ /* .iConfiguration = DYNAMIC */
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int acm_ms_bind(struct usb_composite_dev *cdev)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ struct fsg_opts *opts;
+ struct fsg_config config;
+ int status;
+
+ f_acm_inst = usb_get_function_instance("acm");
+ if (IS_ERR(f_acm_inst))
+ return PTR_ERR(f_acm_inst);
+
+ fi_msg = usb_get_function_instance("mass_storage");
+ if (IS_ERR(fi_msg)) {
+ status = PTR_ERR(fi_msg);
+ goto fail_get_msg;
+ }
+
+ /* set up mass storage function */
+ fsg_config_from_params(&config, &fsg_mod_data, fsg_num_buffers);
+ opts = fsg_opts_from_func_inst(fi_msg);
+
+ opts->no_configfs = true;
+ status = fsg_common_set_num_buffers(opts->common, fsg_num_buffers);
+ if (status)
+ goto fail;
+
+ status = fsg_common_set_nluns(opts->common, config.nluns);
+ if (status)
+ goto fail_set_nluns;
+
+ status = fsg_common_set_cdev(opts->common, cdev, config.can_stall);
+ if (status)
+ goto fail_set_cdev;
+
+ fsg_common_set_sysfs(opts->common, true);
+ status = fsg_common_create_luns(opts->common, &config);
+ if (status)
+ goto fail_set_cdev;
+
+ fsg_common_set_inquiry_string(opts->common, config.vendor_name,
+ config.product_name);
+ /*
+ * Allocate string descriptor numbers ... note that string
+ * contents can be overridden by the composite_dev glue.
+ */
+ status = usb_string_ids_tab(cdev, strings_dev);
+ if (status < 0)
+ goto fail_string_ids;
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+
+ /* register our configuration */
+ status = usb_add_config(cdev, &acm_ms_config_driver, acm_ms_do_config);
+ if (status < 0)
+ goto fail_string_ids;
+
+ usb_composite_overwrite_options(cdev, &coverwrite);
+ dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",
+ DRIVER_DESC);
+ return 0;
+
+ /* error recovery */
+fail_string_ids:
+ fsg_common_remove_luns(opts->common);
+fail_set_cdev:
+ fsg_common_free_luns(opts->common);
+fail_set_nluns:
+ fsg_common_free_buffers(opts->common);
+fail:
+ usb_put_function_instance(fi_msg);
+fail_get_msg:
+ usb_put_function_instance(f_acm_inst);
+ return status;
+}
+
+static int acm_ms_unbind(struct usb_composite_dev *cdev)
+{
+ usb_put_function(f_msg);
+ usb_put_function_instance(fi_msg);
+ usb_put_function(f_acm);
+ usb_put_function_instance(f_acm_inst);
+ return 0;
+}
+
+static struct usb_composite_driver acm_ms_driver = {
+ .name = "g_acm_ms",
+ .dev = &device_desc,
+ .max_speed = USB_SPEED_SUPER,
+ .strings = dev_strings,
+ .bind = acm_ms_bind,
+ .unbind = acm_ms_unbind,
+};
+
+module_usb_composite_driver(acm_ms_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Klaus Schwarzkopf <schwarzkopf@sensortherm.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c
new file mode 100644
index 000000000..f289caf18
--- /dev/null
+++ b/drivers/usb/gadget/legacy/audio.c
@@ -0,0 +1,309 @@
+/*
+ * audio.c -- Audio gadget 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.
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb/composite.h>
+
+#include "gadget_chips.h"
+#define DRIVER_DESC "Linux USB Audio Gadget"
+#define DRIVER_VERSION "Feb 2, 2012"
+
+USB_GADGET_COMPOSITE_OPTIONS();
+
+#ifndef CONFIG_GADGET_UAC1
+#include "u_uac2.h"
+
+/* Playback(USB-IN) Default Stereo - Fl/Fr */
+static int p_chmask = UAC2_DEF_PCHMASK;
+module_param(p_chmask, uint, S_IRUGO);
+MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
+
+/* Playback Default 48 KHz */
+static int p_srate = UAC2_DEF_PSRATE;
+module_param(p_srate, uint, S_IRUGO);
+MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
+
+/* Playback Default 16bits/sample */
+static int p_ssize = UAC2_DEF_PSSIZE;
+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 = UAC2_DEF_CCHMASK;
+module_param(c_chmask, uint, S_IRUGO);
+MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
+
+/* Capture Default 64 KHz */
+static int c_srate = UAC2_DEF_CSRATE;
+module_param(c_srate, uint, S_IRUGO);
+MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
+
+/* Capture Default 16bits/sample */
+static int c_ssize = UAC2_DEF_CSSIZE;
+module_param(c_ssize, uint, S_IRUGO);
+MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
+#else
+#include "u_uac1.h"
+
+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");
+
+static int req_buf_size = UAC1_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 = UAC1_REQ_COUNT;
+module_param(req_count, int, S_IRUGO);
+MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count");
+
+static int audio_buf_size = UAC1_AUDIO_BUF_SIZE;
+module_param(audio_buf_size, int, S_IRUGO);
+MODULE_PARM_DESC(audio_buf_size, "Audio buffer size");
+#endif
+
+/* string IDs are assigned dynamically */
+
+static struct usb_string strings_dev[] = {
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+ .language = 0x0409, /* en-us */
+ .strings = strings_dev,
+};
+
+static struct usb_gadget_strings *audio_strings[] = {
+ &stringtab_dev,
+ NULL,
+};
+
+#ifndef CONFIG_GADGET_UAC1
+static struct usb_function_instance *fi_uac2;
+static struct usb_function *f_uac2;
+#else
+static struct usb_function_instance *fi_uac1;
+static struct usb_function *f_uac1;
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
+ * Instead: allocate your own, using normal USB-IF procedures.
+ */
+
+/* Thanks to Linux Foundation for donating this product ID. */
+#define AUDIO_VENDOR_NUM 0x1d6b /* Linux Foundation */
+#define AUDIO_PRODUCT_NUM 0x0101 /* Linux-USB Audio Gadget */
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_device_descriptor device_desc = {
+ .bLength = sizeof device_desc,
+ .bDescriptorType = USB_DT_DEVICE,
+
+ .bcdUSB = __constant_cpu_to_le16(0x200),
+
+#ifdef CONFIG_GADGET_UAC1
+ .bDeviceClass = USB_CLASS_PER_INTERFACE,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+#else
+ .bDeviceClass = USB_CLASS_MISC,
+ .bDeviceSubClass = 0x02,
+ .bDeviceProtocol = 0x01,
+#endif
+ /* .bMaxPacketSize0 = f(hardware) */
+
+ /* Vendor and product id defaults change according to what configs
+ * we support. (As does bNumConfigurations.) These values can
+ * also be overridden by module parameters.
+ */
+ .idVendor = __constant_cpu_to_le16(AUDIO_VENDOR_NUM),
+ .idProduct = __constant_cpu_to_le16(AUDIO_PRODUCT_NUM),
+ /* .bcdDevice = f(hardware) */
+ /* .iManufacturer = DYNAMIC */
+ /* .iProduct = DYNAMIC */
+ /* NO SERIAL NUMBER */
+ .bNumConfigurations = 1,
+};
+
+static struct usb_otg_descriptor otg_descriptor = {
+ .bLength = sizeof otg_descriptor,
+ .bDescriptorType = USB_DT_OTG,
+
+ /* REVISIT SRP-only hardware is possible, although
+ * it would not be called "OTG" ...
+ */
+ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
+};
+
+static const struct usb_descriptor_header *otg_desc[] = {
+ (struct usb_descriptor_header *) &otg_descriptor,
+ NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int audio_do_config(struct usb_configuration *c)
+{
+ int status;
+
+ /* FIXME alloc iConfiguration string, set it in c->strings */
+
+ if (gadget_is_otg(c->cdev->gadget)) {
+ c->descriptors = otg_desc;
+ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
+#ifdef CONFIG_GADGET_UAC1
+ f_uac1 = usb_get_function(fi_uac1);
+ if (IS_ERR(f_uac1)) {
+ status = PTR_ERR(f_uac1);
+ return status;
+ }
+
+ status = usb_add_function(c, f_uac1);
+ if (status < 0) {
+ usb_put_function(f_uac1);
+ return status;
+ }
+#else
+ f_uac2 = usb_get_function(fi_uac2);
+ if (IS_ERR(f_uac2)) {
+ status = PTR_ERR(f_uac2);
+ return status;
+ }
+
+ status = usb_add_function(c, f_uac2);
+ if (status < 0) {
+ usb_put_function(f_uac2);
+ return status;
+ }
+#endif
+
+ return 0;
+}
+
+static struct usb_configuration audio_config_driver = {
+ .label = DRIVER_DESC,
+ .bConfigurationValue = 1,
+ /* .iConfiguration = DYNAMIC */
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int audio_bind(struct usb_composite_dev *cdev)
+{
+#ifndef CONFIG_GADGET_UAC1
+ struct f_uac2_opts *uac2_opts;
+#else
+ struct f_uac1_opts *uac1_opts;
+#endif
+ int status;
+
+#ifndef CONFIG_GADGET_UAC1
+ fi_uac2 = usb_get_function_instance("uac2");
+ if (IS_ERR(fi_uac2))
+ return PTR_ERR(fi_uac2);
+#else
+ fi_uac1 = usb_get_function_instance("uac1");
+ if (IS_ERR(fi_uac1))
+ return PTR_ERR(fi_uac1);
+#endif
+
+#ifndef CONFIG_GADGET_UAC1
+ uac2_opts = container_of(fi_uac2, struct f_uac2_opts, func_inst);
+ uac2_opts->p_chmask = p_chmask;
+ uac2_opts->p_srate = p_srate;
+ uac2_opts->p_ssize = p_ssize;
+ uac2_opts->c_chmask = c_chmask;
+ uac2_opts->c_srate = c_srate;
+ uac2_opts->c_ssize = c_ssize;
+#else
+ uac1_opts = container_of(fi_uac1, struct f_uac1_opts, func_inst);
+ uac1_opts->fn_play = fn_play;
+ uac1_opts->fn_cap = fn_cap;
+ uac1_opts->fn_cntl = fn_cntl;
+ uac1_opts->req_buf_size = req_buf_size;
+ uac1_opts->req_count = req_count;
+ uac1_opts->audio_buf_size = audio_buf_size;
+#endif
+
+ status = usb_string_ids_tab(cdev, strings_dev);
+ if (status < 0)
+ goto fail;
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+
+ status = usb_add_config(cdev, &audio_config_driver, audio_do_config);
+ if (status < 0)
+ goto fail;
+ usb_composite_overwrite_options(cdev, &coverwrite);
+
+ INFO(cdev, "%s, version: %s\n", DRIVER_DESC, DRIVER_VERSION);
+ return 0;
+
+fail:
+#ifndef CONFIG_GADGET_UAC1
+ usb_put_function_instance(fi_uac2);
+#else
+ usb_put_function_instance(fi_uac1);
+#endif
+ return status;
+}
+
+static int audio_unbind(struct usb_composite_dev *cdev)
+{
+#ifdef CONFIG_GADGET_UAC1
+ if (!IS_ERR_OR_NULL(f_uac1))
+ usb_put_function(f_uac1);
+ if (!IS_ERR_OR_NULL(fi_uac1))
+ usb_put_function_instance(fi_uac1);
+#else
+ if (!IS_ERR_OR_NULL(f_uac2))
+ usb_put_function(f_uac2);
+ if (!IS_ERR_OR_NULL(fi_uac2))
+ usb_put_function_instance(fi_uac2);
+#endif
+ return 0;
+}
+
+static struct usb_composite_driver audio_driver = {
+ .name = "g_audio",
+ .dev = &device_desc,
+ .strings = audio_strings,
+ .max_speed = USB_SPEED_HIGH,
+ .bind = audio_bind,
+ .unbind = audio_unbind,
+};
+
+module_usb_composite_driver(audio_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Bryan Wu <cooloney@kernel.org>");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/usb/gadget/legacy/cdc2.c b/drivers/usb/gadget/legacy/cdc2.c
new file mode 100644
index 000000000..afd3e3792
--- /dev/null
+++ b/drivers/usb/gadget/legacy/cdc2.c
@@ -0,0 +1,238 @@
+/*
+ * cdc2.c -- CDC Composite driver, with ECM and ACM support
+ *
+ * Copyright (C) 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 "u_ether.h"
+#include "u_serial.h"
+#include "u_ecm.h"
+
+
+#define DRIVER_DESC "CDC Composite Gadget"
+#define DRIVER_VERSION "King Kamehameha Day 2008"
+
+/*-------------------------------------------------------------------------*/
+
+/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
+ * Instead: allocate your own, using normal USB-IF procedures.
+ */
+
+/* Thanks to NetChip Technologies for donating this product ID.
+ * It's for devices with only this composite CDC configuration.
+ */
+#define CDC_VENDOR_NUM 0x0525 /* NetChip */
+#define CDC_PRODUCT_NUM 0xa4aa /* CDC Composite: ECM + ACM */
+
+USB_GADGET_COMPOSITE_OPTIONS();
+
+USB_ETHERNET_MODULE_PARAMETERS();
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_device_descriptor device_desc = {
+ .bLength = sizeof device_desc,
+ .bDescriptorType = USB_DT_DEVICE,
+
+ .bcdUSB = cpu_to_le16(0x0200),
+
+ .bDeviceClass = USB_CLASS_COMM,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+ /* .bMaxPacketSize0 = f(hardware) */
+
+ /* Vendor and product id can be overridden by module parameters. */
+ .idVendor = cpu_to_le16(CDC_VENDOR_NUM),
+ .idProduct = cpu_to_le16(CDC_PRODUCT_NUM),
+ /* .bcdDevice = f(hardware) */
+ /* .iManufacturer = DYNAMIC */
+ /* .iProduct = DYNAMIC */
+ /* NO SERIAL NUMBER */
+ .bNumConfigurations = 1,
+};
+
+static struct usb_otg_descriptor otg_descriptor = {
+ .bLength = sizeof otg_descriptor,
+ .bDescriptorType = USB_DT_OTG,
+
+ /* REVISIT SRP-only hardware is possible, although
+ * it would not be called "OTG" ...
+ */
+ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
+};
+
+static const struct usb_descriptor_header *otg_desc[] = {
+ (struct usb_descriptor_header *) &otg_descriptor,
+ NULL,
+};
+
+
+/* string IDs are assigned dynamically */
+static struct usb_string strings_dev[] = {
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+ .language = 0x0409, /* en-us */
+ .strings = strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+ &stringtab_dev,
+ NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+static struct usb_function *f_acm;
+static struct usb_function_instance *fi_serial;
+
+static struct usb_function *f_ecm;
+static struct usb_function_instance *fi_ecm;
+
+/*
+ * We _always_ have both CDC ECM and CDC ACM functions.
+ */
+static int cdc_do_config(struct usb_configuration *c)
+{
+ int status;
+
+ if (gadget_is_otg(c->cdev->gadget)) {
+ c->descriptors = otg_desc;
+ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
+ f_ecm = usb_get_function(fi_ecm);
+ if (IS_ERR(f_ecm)) {
+ status = PTR_ERR(f_ecm);
+ goto err_get_ecm;
+ }
+
+ status = usb_add_function(c, f_ecm);
+ if (status)
+ goto err_add_ecm;
+
+ f_acm = usb_get_function(fi_serial);
+ if (IS_ERR(f_acm)) {
+ status = PTR_ERR(f_acm);
+ goto err_get_acm;
+ }
+
+ status = usb_add_function(c, f_acm);
+ if (status)
+ goto err_add_acm;
+ return 0;
+
+err_add_acm:
+ usb_put_function(f_acm);
+err_get_acm:
+ usb_remove_function(c, f_ecm);
+err_add_ecm:
+ usb_put_function(f_ecm);
+err_get_ecm:
+ return status;
+}
+
+static struct usb_configuration cdc_config_driver = {
+ .label = "CDC Composite (ECM + ACM)",
+ .bConfigurationValue = 1,
+ /* .iConfiguration = DYNAMIC */
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int cdc_bind(struct usb_composite_dev *cdev)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ struct f_ecm_opts *ecm_opts;
+ int status;
+
+ if (!can_support_ecm(cdev->gadget)) {
+ dev_err(&gadget->dev, "controller '%s' not usable\n",
+ gadget->name);
+ return -EINVAL;
+ }
+
+ fi_ecm = usb_get_function_instance("ecm");
+ if (IS_ERR(fi_ecm))
+ return PTR_ERR(fi_ecm);
+
+ ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst);
+
+ gether_set_qmult(ecm_opts->net, qmult);
+ if (!gether_set_host_addr(ecm_opts->net, host_addr))
+ pr_info("using host ethernet address: %s", host_addr);
+ if (!gether_set_dev_addr(ecm_opts->net, dev_addr))
+ pr_info("using self ethernet address: %s", dev_addr);
+
+ fi_serial = usb_get_function_instance("acm");
+ if (IS_ERR(fi_serial)) {
+ status = PTR_ERR(fi_serial);
+ goto fail;
+ }
+
+ /* Allocate string descriptor numbers ... note that string
+ * contents can be overridden by the composite_dev glue.
+ */
+
+ status = usb_string_ids_tab(cdev, strings_dev);
+ if (status < 0)
+ goto fail1;
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+
+ /* register our configuration */
+ status = usb_add_config(cdev, &cdc_config_driver, cdc_do_config);
+ if (status < 0)
+ goto fail1;
+
+ usb_composite_overwrite_options(cdev, &coverwrite);
+ dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",
+ DRIVER_DESC);
+
+ return 0;
+
+fail1:
+ usb_put_function_instance(fi_serial);
+fail:
+ usb_put_function_instance(fi_ecm);
+ return status;
+}
+
+static int cdc_unbind(struct usb_composite_dev *cdev)
+{
+ usb_put_function(f_acm);
+ usb_put_function_instance(fi_serial);
+ if (!IS_ERR_OR_NULL(f_ecm))
+ usb_put_function(f_ecm);
+ if (!IS_ERR_OR_NULL(fi_ecm))
+ usb_put_function_instance(fi_ecm);
+ return 0;
+}
+
+static struct usb_composite_driver cdc_driver = {
+ .name = "g_cdc",
+ .dev = &device_desc,
+ .strings = dev_strings,
+ .max_speed = USB_SPEED_HIGH,
+ .bind = cdc_bind,
+ .unbind = cdc_unbind,
+};
+
+module_usb_composite_driver(cdc_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("David Brownell");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/legacy/dbgp.c b/drivers/usb/gadget/legacy/dbgp.c
new file mode 100644
index 000000000..204b10b1a
--- /dev/null
+++ b/drivers/usb/gadget/legacy/dbgp.c
@@ -0,0 +1,439 @@
+/*
+ * dbgp.c -- EHCI Debug Port device gadget
+ *
+ * Copyright (C) 2010 Stephane Duverger
+ *
+ * Released under the GPLv2.
+ */
+
+/* verbose messages */
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include "u_serial.h"
+
+#define DRIVER_VENDOR_ID 0x0525 /* NetChip */
+#define DRIVER_PRODUCT_ID 0xc0de /* undefined */
+
+#define USB_DEBUG_MAX_PACKET_SIZE 8
+#define DBGP_REQ_EP0_LEN 128
+#define DBGP_REQ_LEN 512
+
+static struct dbgp {
+ struct usb_gadget *gadget;
+ struct usb_request *req;
+ struct usb_ep *i_ep;
+ struct usb_ep *o_ep;
+#ifdef CONFIG_USB_G_DBGP_SERIAL
+ struct gserial *serial;
+#endif
+} dbgp;
+
+static struct usb_device_descriptor device_desc = {
+ .bLength = sizeof device_desc,
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = __constant_cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_VENDOR_SPEC,
+ .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_ID),
+ .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_ID),
+ .bNumConfigurations = 1,
+};
+
+static struct usb_debug_descriptor dbg_desc = {
+ .bLength = sizeof dbg_desc,
+ .bDescriptorType = USB_DT_DEBUG,
+};
+
+static struct usb_endpoint_descriptor i_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .bEndpointAddress = USB_DIR_IN,
+};
+
+static struct usb_endpoint_descriptor o_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .bEndpointAddress = USB_DIR_OUT,
+};
+
+#ifdef CONFIG_USB_G_DBGP_PRINTK
+static int dbgp_consume(char *buf, unsigned len)
+{
+ char c;
+
+ if (!len)
+ return 0;
+
+ c = buf[len-1];
+ if (c != 0)
+ buf[len-1] = 0;
+
+ printk(KERN_NOTICE "%s%c", buf, c);
+ return 0;
+}
+
+static void __disable_ep(struct usb_ep *ep)
+{
+ if (ep && ep->driver_data == dbgp.gadget) {
+ usb_ep_disable(ep);
+ ep->driver_data = NULL;
+ }
+}
+
+static void dbgp_disable_ep(void)
+{
+ __disable_ep(dbgp.i_ep);
+ __disable_ep(dbgp.o_ep);
+}
+
+static void dbgp_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ int stp;
+ int err = 0;
+ int status = req->status;
+
+ if (ep == dbgp.i_ep) {
+ stp = 1;
+ goto fail;
+ }
+
+ if (status != 0) {
+ stp = 2;
+ goto release_req;
+ }
+
+ dbgp_consume(req->buf, req->actual);
+
+ req->length = DBGP_REQ_LEN;
+ err = usb_ep_queue(ep, req, GFP_ATOMIC);
+ if (err < 0) {
+ stp = 3;
+ goto release_req;
+ }
+
+ return;
+
+release_req:
+ kfree(req->buf);
+ usb_ep_free_request(dbgp.o_ep, req);
+ dbgp_disable_ep();
+fail:
+ dev_dbg(&dbgp.gadget->dev,
+ "complete: failure (%d:%d) ==> %d\n", stp, err, status);
+}
+
+static int dbgp_enable_ep_req(struct usb_ep *ep)
+{
+ int err, stp;
+ struct usb_request *req;
+
+ req = usb_ep_alloc_request(ep, GFP_KERNEL);
+ if (!req) {
+ err = -ENOMEM;
+ stp = 1;
+ goto fail_1;
+ }
+
+ req->buf = kmalloc(DBGP_REQ_LEN, GFP_KERNEL);
+ if (!req->buf) {
+ err = -ENOMEM;
+ stp = 2;
+ goto fail_2;
+ }
+
+ req->complete = dbgp_complete;
+ req->length = DBGP_REQ_LEN;
+ err = usb_ep_queue(ep, req, GFP_ATOMIC);
+ if (err < 0) {
+ stp = 3;
+ goto fail_3;
+ }
+
+ return 0;
+
+fail_3:
+ kfree(req->buf);
+fail_2:
+ usb_ep_free_request(dbgp.o_ep, req);
+fail_1:
+ dev_dbg(&dbgp.gadget->dev,
+ "enable ep req: failure (%d:%d)\n", stp, err);
+ return err;
+}
+
+static int __enable_ep(struct usb_ep *ep, struct usb_endpoint_descriptor *desc)
+{
+ int err;
+ ep->desc = desc;
+ err = usb_ep_enable(ep);
+ ep->driver_data = dbgp.gadget;
+ return err;
+}
+
+static int dbgp_enable_ep(void)
+{
+ int err, stp;
+
+ err = __enable_ep(dbgp.i_ep, &i_desc);
+ if (err < 0) {
+ stp = 1;
+ goto fail_1;
+ }
+
+ err = __enable_ep(dbgp.o_ep, &o_desc);
+ if (err < 0) {
+ stp = 2;
+ goto fail_2;
+ }
+
+ err = dbgp_enable_ep_req(dbgp.o_ep);
+ if (err < 0) {
+ stp = 3;
+ goto fail_3;
+ }
+
+ return 0;
+
+fail_3:
+ __disable_ep(dbgp.o_ep);
+fail_2:
+ __disable_ep(dbgp.i_ep);
+fail_1:
+ dev_dbg(&dbgp.gadget->dev, "enable ep: failure (%d:%d)\n", stp, err);
+ return err;
+}
+#endif
+
+static void dbgp_disconnect(struct usb_gadget *gadget)
+{
+#ifdef CONFIG_USB_G_DBGP_PRINTK
+ dbgp_disable_ep();
+#else
+ gserial_disconnect(dbgp.serial);
+#endif
+}
+
+static void dbgp_unbind(struct usb_gadget *gadget)
+{
+#ifdef CONFIG_USB_G_DBGP_SERIAL
+ kfree(dbgp.serial);
+ dbgp.serial = NULL;
+#endif
+ if (dbgp.req) {
+ kfree(dbgp.req->buf);
+ usb_ep_free_request(gadget->ep0, dbgp.req);
+ dbgp.req = NULL;
+ }
+
+ gadget->ep0->driver_data = NULL;
+}
+
+#ifdef CONFIG_USB_G_DBGP_SERIAL
+static unsigned char tty_line;
+#endif
+
+static int dbgp_configure_endpoints(struct usb_gadget *gadget)
+{
+ int stp;
+
+ usb_ep_autoconfig_reset(gadget);
+
+ dbgp.i_ep = usb_ep_autoconfig(gadget, &i_desc);
+ if (!dbgp.i_ep) {
+ stp = 1;
+ goto fail_1;
+ }
+
+ dbgp.i_ep->driver_data = gadget;
+ i_desc.wMaxPacketSize =
+ __constant_cpu_to_le16(USB_DEBUG_MAX_PACKET_SIZE);
+
+ dbgp.o_ep = usb_ep_autoconfig(gadget, &o_desc);
+ if (!dbgp.o_ep) {
+ dbgp.i_ep->driver_data = NULL;
+ stp = 2;
+ goto fail_2;
+ }
+
+ dbgp.o_ep->driver_data = gadget;
+ o_desc.wMaxPacketSize =
+ __constant_cpu_to_le16(USB_DEBUG_MAX_PACKET_SIZE);
+
+ dbg_desc.bDebugInEndpoint = i_desc.bEndpointAddress;
+ dbg_desc.bDebugOutEndpoint = o_desc.bEndpointAddress;
+
+#ifdef CONFIG_USB_G_DBGP_SERIAL
+ dbgp.serial->in = dbgp.i_ep;
+ dbgp.serial->out = dbgp.o_ep;
+
+ dbgp.serial->in->desc = &i_desc;
+ dbgp.serial->out->desc = &o_desc;
+#endif
+
+ return 0;
+
+fail_2:
+ dbgp.i_ep->driver_data = NULL;
+fail_1:
+ dev_dbg(&dbgp.gadget->dev, "ep config: failure (%d)\n", stp);
+ return -ENODEV;
+}
+
+static int dbgp_bind(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ int err, stp;
+
+ dbgp.gadget = gadget;
+
+ dbgp.req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
+ if (!dbgp.req) {
+ err = -ENOMEM;
+ stp = 1;
+ goto fail;
+ }
+
+ dbgp.req->buf = kmalloc(DBGP_REQ_EP0_LEN, GFP_KERNEL);
+ if (!dbgp.req->buf) {
+ err = -ENOMEM;
+ stp = 2;
+ goto fail;
+ }
+
+ dbgp.req->length = DBGP_REQ_EP0_LEN;
+ gadget->ep0->driver_data = gadget;
+
+#ifdef CONFIG_USB_G_DBGP_SERIAL
+ dbgp.serial = kzalloc(sizeof(struct gserial), GFP_KERNEL);
+ if (!dbgp.serial) {
+ stp = 3;
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ if (gserial_alloc_line(&tty_line)) {
+ stp = 4;
+ err = -ENODEV;
+ goto fail;
+ }
+#endif
+
+ err = dbgp_configure_endpoints(gadget);
+ if (err < 0) {
+ stp = 5;
+ goto fail;
+ }
+
+ dev_dbg(&dbgp.gadget->dev, "bind: success\n");
+ return 0;
+
+fail:
+ dev_dbg(&gadget->dev, "bind: failure (%d:%d)\n", stp, err);
+ dbgp_unbind(gadget);
+ return err;
+}
+
+static void dbgp_setup_complete(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ dev_dbg(&dbgp.gadget->dev, "setup complete: %d, %d/%d\n",
+ req->status, req->actual, req->length);
+}
+
+static int dbgp_setup(struct usb_gadget *gadget,
+ const struct usb_ctrlrequest *ctrl)
+{
+ struct usb_request *req = dbgp.req;
+ u8 request = ctrl->bRequest;
+ u16 value = le16_to_cpu(ctrl->wValue);
+ u16 length = le16_to_cpu(ctrl->wLength);
+ int err = -EOPNOTSUPP;
+ void *data = NULL;
+ u16 len = 0;
+
+ gadget->ep0->driver_data = gadget;
+
+ if (request == USB_REQ_GET_DESCRIPTOR) {
+ switch (value>>8) {
+ case USB_DT_DEVICE:
+ dev_dbg(&dbgp.gadget->dev, "setup: desc device\n");
+ len = sizeof device_desc;
+ data = &device_desc;
+ device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
+ break;
+ case USB_DT_DEBUG:
+ dev_dbg(&dbgp.gadget->dev, "setup: desc debug\n");
+ len = sizeof dbg_desc;
+ data = &dbg_desc;
+ break;
+ default:
+ goto fail;
+ }
+ err = 0;
+ } else if (request == USB_REQ_SET_FEATURE &&
+ value == USB_DEVICE_DEBUG_MODE) {
+ dev_dbg(&dbgp.gadget->dev, "setup: feat debug\n");
+#ifdef CONFIG_USB_G_DBGP_PRINTK
+ err = dbgp_enable_ep();
+#else
+ err = dbgp_configure_endpoints(gadget);
+ if (err < 0) {
+ goto fail;
+ }
+ err = gserial_connect(dbgp.serial, tty_line);
+#endif
+ if (err < 0)
+ goto fail;
+ } else
+ goto fail;
+
+ req->length = min(length, len);
+ req->zero = len < req->length;
+ if (data && req->length)
+ memcpy(req->buf, data, req->length);
+
+ req->complete = dbgp_setup_complete;
+ return usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
+
+fail:
+ dev_dbg(&dbgp.gadget->dev,
+ "setup: failure req %x v %x\n", request, value);
+ return err;
+}
+
+static struct usb_gadget_driver dbgp_driver = {
+ .function = "dbgp",
+ .max_speed = USB_SPEED_HIGH,
+ .bind = dbgp_bind,
+ .unbind = dbgp_unbind,
+ .setup = dbgp_setup,
+ .reset = dbgp_disconnect,
+ .disconnect = dbgp_disconnect,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "dbgp"
+ },
+};
+
+static int __init dbgp_init(void)
+{
+ return usb_gadget_probe_driver(&dbgp_driver);
+}
+
+static void __exit dbgp_exit(void)
+{
+ usb_gadget_unregister_driver(&dbgp_driver);
+#ifdef CONFIG_USB_G_DBGP_SERIAL
+ gserial_free_line(tty_line);
+#endif
+}
+
+MODULE_AUTHOR("Stephane Duverger");
+MODULE_LICENSE("GPL");
+module_init(dbgp_init);
+module_exit(dbgp_exit);
diff --git a/drivers/usb/gadget/legacy/ether.c b/drivers/usb/gadget/legacy/ether.c
new file mode 100644
index 000000000..a3323dca2
--- /dev/null
+++ b/drivers/usb/gadget/legacy/ether.c
@@ -0,0 +1,482 @@
+/*
+ * ether.c -- Ethernet gadget driver, with CDC and non-CDC options
+ *
+ * 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/netdevice.h>
+
+#if defined USB_ETH_RNDIS
+# undef USB_ETH_RNDIS
+#endif
+#ifdef CONFIG_USB_ETH_RNDIS
+# define USB_ETH_RNDIS y
+#endif
+
+#include "u_ether.h"
+
+
+/*
+ * Ethernet gadget driver -- with CDC and non-CDC options
+ * Builds on hardware support for a full duplex link.
+ *
+ * CDC Ethernet is the standard USB solution for sending Ethernet frames
+ * using USB. Real hardware tends to use the same framing protocol but look
+ * different for control features. This driver strongly prefers to use
+ * this USB-IF standard as its open-systems interoperability solution;
+ * most host side USB stacks (except from Microsoft) support it.
+ *
+ * This is sometimes called "CDC ECM" (Ethernet Control Model) to support
+ * TLA-soup. "CDC ACM" (Abstract Control Model) is for modems, and a new
+ * "CDC EEM" (Ethernet Emulation Model) is starting to spread.
+ *
+ * There's some hardware that can't talk CDC ECM. We make that hardware
+ * implement a "minimalist" vendor-agnostic CDC core: same framing, but
+ * link-level setup only requires activating the configuration. Only the
+ * endpoint descriptors, and product/vendor IDs, are relevant; no control
+ * operations are available. Linux supports it, but other host operating
+ * systems may not. (This is a subset of CDC Ethernet.)
+ *
+ * It turns out that if you add a few descriptors to that "CDC Subset",
+ * (Windows) host side drivers from MCCI can treat it as one submode of
+ * a proprietary scheme called "SAFE" ... without needing to know about
+ * specific product/vendor IDs. So we do that, making it easier to use
+ * those MS-Windows drivers. Those added descriptors make it resemble a
+ * CDC MDLM device, but they don't change device behavior at all. (See
+ * MCCI Engineering report 950198 "SAFE Networking Functions".)
+ *
+ * A third option is also in use. Rather than CDC Ethernet, or something
+ * simpler, Microsoft pushes their own approach: RNDIS. The published
+ * RNDIS specs are ambiguous and appear to be incomplete, and are also
+ * needlessly complex. They borrow more from CDC ACM than CDC ECM.
+ */
+
+#define DRIVER_DESC "Ethernet Gadget"
+#define DRIVER_VERSION "Memorial Day 2008"
+
+#ifdef USB_ETH_RNDIS
+#define PREFIX "RNDIS/"
+#else
+#define PREFIX ""
+#endif
+
+/*
+ * This driver aims for interoperability by using CDC ECM unless
+ *
+ * can_support_ecm()
+ *
+ * returns false, in which case it supports the CDC Subset. By default,
+ * that returns true; most hardware has no problems with CDC ECM, that's
+ * a good default. Previous versions of this driver had no default; this
+ * version changes that, removing overhead for new controller support.
+ *
+ * IF YOUR HARDWARE CAN'T SUPPORT CDC ECM, UPDATE THAT ROUTINE!
+ */
+
+static inline bool has_rndis(void)
+{
+#ifdef USB_ETH_RNDIS
+ return true;
+#else
+ return false;
+#endif
+}
+
+#include <linux/module.h>
+
+#include "u_ecm.h"
+#include "u_gether.h"
+#ifdef USB_ETH_RNDIS
+#include "u_rndis.h"
+#include "rndis.h"
+#else
+#define rndis_borrow_net(...) do {} while (0)
+#endif
+#include "u_eem.h"
+
+/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
+
+USB_ETHERNET_MODULE_PARAMETERS();
+
+/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
+ * Instead: allocate your own, using normal USB-IF procedures.
+ */
+
+/* Thanks to NetChip Technologies for donating this product ID.
+ * It's for devices with only CDC Ethernet configurations.
+ */
+#define CDC_VENDOR_NUM 0x0525 /* NetChip */
+#define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */
+
+/* For hardware that can't talk CDC, we use the same vendor ID that
+ * ARM Linux has used for ethernet-over-usb, both with sa1100 and
+ * with pxa250. We're protocol-compatible, if the host-side drivers
+ * use the endpoint descriptors. bcdDevice (version) is nonzero, so
+ * drivers that need to hard-wire endpoint numbers have a hook.
+ *
+ * The protocol is a minimal subset of CDC Ether, which works on any bulk
+ * hardware that's not deeply broken ... even on hardware that can't talk
+ * RNDIS (like SA-1100, with no interrupt endpoint, or anything that
+ * doesn't handle control-OUT).
+ */
+#define SIMPLE_VENDOR_NUM 0x049f
+#define SIMPLE_PRODUCT_NUM 0x505a
+
+/* For hardware that can talk RNDIS and either of the above protocols,
+ * use this ID ... the windows INF files will know it. Unless it's
+ * used with CDC Ethernet, Linux 2.4 hosts will need updates to choose
+ * the non-RNDIS configuration.
+ */
+#define RNDIS_VENDOR_NUM 0x0525 /* NetChip */
+#define RNDIS_PRODUCT_NUM 0xa4a2 /* Ethernet/RNDIS Gadget */
+
+/* For EEM gadgets */
+#define EEM_VENDOR_NUM 0x1d6b /* Linux Foundation */
+#define EEM_PRODUCT_NUM 0x0102 /* EEM Gadget */
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_device_descriptor device_desc = {
+ .bLength = sizeof device_desc,
+ .bDescriptorType = USB_DT_DEVICE,
+
+ .bcdUSB = cpu_to_le16 (0x0200),
+
+ .bDeviceClass = USB_CLASS_COMM,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+ /* .bMaxPacketSize0 = f(hardware) */
+
+ /* Vendor and product id defaults change according to what configs
+ * we support. (As does bNumConfigurations.) These values can
+ * also be overridden by module parameters.
+ */
+ .idVendor = cpu_to_le16 (CDC_VENDOR_NUM),
+ .idProduct = cpu_to_le16 (CDC_PRODUCT_NUM),
+ /* .bcdDevice = f(hardware) */
+ /* .iManufacturer = DYNAMIC */
+ /* .iProduct = DYNAMIC */
+ /* NO SERIAL NUMBER */
+ .bNumConfigurations = 1,
+};
+
+static struct usb_otg_descriptor otg_descriptor = {
+ .bLength = sizeof otg_descriptor,
+ .bDescriptorType = USB_DT_OTG,
+
+ /* REVISIT SRP-only hardware is possible, although
+ * it would not be called "OTG" ...
+ */
+ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
+};
+
+static const struct usb_descriptor_header *otg_desc[] = {
+ (struct usb_descriptor_header *) &otg_descriptor,
+ NULL,
+};
+
+static struct usb_string strings_dev[] = {
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = PREFIX DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+ .language = 0x0409, /* en-us */
+ .strings = strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+ &stringtab_dev,
+ NULL,
+};
+
+static struct usb_function_instance *fi_ecm;
+static struct usb_function *f_ecm;
+
+static struct usb_function_instance *fi_eem;
+static struct usb_function *f_eem;
+
+static struct usb_function_instance *fi_geth;
+static struct usb_function *f_geth;
+
+static struct usb_function_instance *fi_rndis;
+static struct usb_function *f_rndis;
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * We may not have an RNDIS configuration, but if we do it needs to be
+ * the first one present. That's to make Microsoft's drivers happy,
+ * and to follow DOCSIS 1.0 (cable modem standard).
+ */
+static int rndis_do_config(struct usb_configuration *c)
+{
+ int status;
+
+ /* FIXME alloc iConfiguration string, set it in c->strings */
+
+ if (gadget_is_otg(c->cdev->gadget)) {
+ c->descriptors = otg_desc;
+ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
+ f_rndis = usb_get_function(fi_rndis);
+ if (IS_ERR(f_rndis))
+ return PTR_ERR(f_rndis);
+
+ status = usb_add_function(c, f_rndis);
+ if (status < 0)
+ usb_put_function(f_rndis);
+
+ return status;
+}
+
+static struct usb_configuration rndis_config_driver = {
+ .label = "RNDIS",
+ .bConfigurationValue = 2,
+ /* .iConfiguration = DYNAMIC */
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+};
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_USB_ETH_EEM
+static bool use_eem = 1;
+#else
+static bool use_eem;
+#endif
+module_param(use_eem, bool, 0);
+MODULE_PARM_DESC(use_eem, "use CDC EEM mode");
+
+/*
+ * We _always_ have an ECM, CDC Subset, or EEM configuration.
+ */
+static int eth_do_config(struct usb_configuration *c)
+{
+ int status = 0;
+
+ /* FIXME alloc iConfiguration string, set it in c->strings */
+
+ if (gadget_is_otg(c->cdev->gadget)) {
+ c->descriptors = otg_desc;
+ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
+ if (use_eem) {
+ f_eem = usb_get_function(fi_eem);
+ if (IS_ERR(f_eem))
+ return PTR_ERR(f_eem);
+
+ status = usb_add_function(c, f_eem);
+ if (status < 0)
+ usb_put_function(f_eem);
+
+ return status;
+ } else if (can_support_ecm(c->cdev->gadget)) {
+ f_ecm = usb_get_function(fi_ecm);
+ if (IS_ERR(f_ecm))
+ return PTR_ERR(f_ecm);
+
+ status = usb_add_function(c, f_ecm);
+ if (status < 0)
+ usb_put_function(f_ecm);
+
+ return status;
+ } else {
+ f_geth = usb_get_function(fi_geth);
+ if (IS_ERR(f_geth))
+ return PTR_ERR(f_geth);
+
+ status = usb_add_function(c, f_geth);
+ if (status < 0)
+ usb_put_function(f_geth);
+
+ return status;
+ }
+
+}
+
+static struct usb_configuration eth_config_driver = {
+ /* .label = f(hardware) */
+ .bConfigurationValue = 1,
+ /* .iConfiguration = DYNAMIC */
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int eth_bind(struct usb_composite_dev *cdev)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ struct f_eem_opts *eem_opts = NULL;
+ struct f_ecm_opts *ecm_opts = NULL;
+ struct f_gether_opts *geth_opts = NULL;
+ struct net_device *net;
+ int status;
+
+ /* set up main config label and device descriptor */
+ if (use_eem) {
+ /* EEM */
+ fi_eem = usb_get_function_instance("eem");
+ if (IS_ERR(fi_eem))
+ return PTR_ERR(fi_eem);
+
+ eem_opts = container_of(fi_eem, struct f_eem_opts, func_inst);
+
+ net = eem_opts->net;
+
+ eth_config_driver.label = "CDC Ethernet (EEM)";
+ device_desc.idVendor = cpu_to_le16(EEM_VENDOR_NUM);
+ device_desc.idProduct = cpu_to_le16(EEM_PRODUCT_NUM);
+ } else if (can_support_ecm(gadget)) {
+ /* ECM */
+
+ fi_ecm = usb_get_function_instance("ecm");
+ if (IS_ERR(fi_ecm))
+ return PTR_ERR(fi_ecm);
+
+ ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst);
+
+ net = ecm_opts->net;
+
+ eth_config_driver.label = "CDC Ethernet (ECM)";
+ } else {
+ /* CDC Subset */
+
+ fi_geth = usb_get_function_instance("geth");
+ if (IS_ERR(fi_geth))
+ return PTR_ERR(fi_geth);
+
+ geth_opts = container_of(fi_geth, struct f_gether_opts,
+ func_inst);
+
+ net = geth_opts->net;
+
+ eth_config_driver.label = "CDC Subset/SAFE";
+
+ device_desc.idVendor = cpu_to_le16(SIMPLE_VENDOR_NUM);
+ device_desc.idProduct = cpu_to_le16(SIMPLE_PRODUCT_NUM);
+ if (!has_rndis())
+ device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC;
+ }
+
+ gether_set_qmult(net, qmult);
+ if (!gether_set_host_addr(net, host_addr))
+ pr_info("using host ethernet address: %s", host_addr);
+ if (!gether_set_dev_addr(net, dev_addr))
+ pr_info("using self ethernet address: %s", dev_addr);
+
+ if (has_rndis()) {
+ /* RNDIS plus ECM-or-Subset */
+ gether_set_gadget(net, cdev->gadget);
+ status = gether_register_netdev(net);
+ if (status)
+ goto fail;
+
+ if (use_eem)
+ eem_opts->bound = true;
+ else if (can_support_ecm(gadget))
+ ecm_opts->bound = true;
+ else
+ geth_opts->bound = true;
+
+ fi_rndis = usb_get_function_instance("rndis");
+ if (IS_ERR(fi_rndis)) {
+ status = PTR_ERR(fi_rndis);
+ goto fail;
+ }
+
+ rndis_borrow_net(fi_rndis, net);
+
+ device_desc.idVendor = cpu_to_le16(RNDIS_VENDOR_NUM);
+ device_desc.idProduct = cpu_to_le16(RNDIS_PRODUCT_NUM);
+ device_desc.bNumConfigurations = 2;
+ }
+
+ /* Allocate string descriptor numbers ... note that string
+ * contents can be overridden by the composite_dev glue.
+ */
+
+ status = usb_string_ids_tab(cdev, strings_dev);
+ if (status < 0)
+ goto fail1;
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+
+ /* register our configuration(s); RNDIS first, if it's used */
+ if (has_rndis()) {
+ status = usb_add_config(cdev, &rndis_config_driver,
+ rndis_do_config);
+ if (status < 0)
+ goto fail1;
+ }
+
+ status = usb_add_config(cdev, &eth_config_driver, eth_do_config);
+ if (status < 0)
+ goto fail1;
+
+ usb_composite_overwrite_options(cdev, &coverwrite);
+ dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",
+ DRIVER_DESC);
+
+ return 0;
+
+fail1:
+ if (has_rndis())
+ usb_put_function_instance(fi_rndis);
+fail:
+ if (use_eem)
+ usb_put_function_instance(fi_eem);
+ else if (can_support_ecm(gadget))
+ usb_put_function_instance(fi_ecm);
+ else
+ usb_put_function_instance(fi_geth);
+ return status;
+}
+
+static int eth_unbind(struct usb_composite_dev *cdev)
+{
+ if (has_rndis()) {
+ usb_put_function(f_rndis);
+ usb_put_function_instance(fi_rndis);
+ }
+ if (use_eem) {
+ usb_put_function(f_eem);
+ usb_put_function_instance(fi_eem);
+ } else if (can_support_ecm(cdev->gadget)) {
+ usb_put_function(f_ecm);
+ usb_put_function_instance(fi_ecm);
+ } else {
+ usb_put_function(f_geth);
+ usb_put_function_instance(fi_geth);
+ }
+ return 0;
+}
+
+static struct usb_composite_driver eth_driver = {
+ .name = "g_ether",
+ .dev = &device_desc,
+ .strings = dev_strings,
+ .max_speed = USB_SPEED_SUPER,
+ .bind = eth_bind,
+ .unbind = eth_unbind,
+};
+
+module_usb_composite_driver(eth_driver);
+
+MODULE_DESCRIPTION(PREFIX DRIVER_DESC);
+MODULE_AUTHOR("David Brownell, Benedikt Spanger");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/legacy/g_ffs.c b/drivers/usb/gadget/legacy/g_ffs.c
new file mode 100644
index 000000000..e821931c9
--- /dev/null
+++ b/drivers/usb/gadget/legacy/g_ffs.c
@@ -0,0 +1,586 @@
+/*
+ * g_ffs.c -- user mode file system API for USB composite function controllers
+ *
+ * Copyright (C) 2010 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 pr_fmt(fmt) "g_ffs: " fmt
+
+#include <linux/module.h>
+
+#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS
+#include <linux/netdevice.h>
+
+# if defined USB_ETH_RNDIS
+# undef USB_ETH_RNDIS
+# endif
+# ifdef CONFIG_USB_FUNCTIONFS_RNDIS
+# define USB_ETH_RNDIS y
+# endif
+
+# include "u_ecm.h"
+# include "u_gether.h"
+# ifdef USB_ETH_RNDIS
+# include "u_rndis.h"
+# include "rndis.h"
+# endif
+# include "u_ether.h"
+
+USB_ETHERNET_MODULE_PARAMETERS();
+
+# ifdef CONFIG_USB_FUNCTIONFS_ETH
+static int eth_bind_config(struct usb_configuration *c);
+static struct usb_function_instance *fi_ecm;
+static struct usb_function *f_ecm;
+static struct usb_function_instance *fi_geth;
+static struct usb_function *f_geth;
+# endif
+# ifdef CONFIG_USB_FUNCTIONFS_RNDIS
+static int bind_rndis_config(struct usb_configuration *c);
+static struct usb_function_instance *fi_rndis;
+static struct usb_function *f_rndis;
+# endif
+#endif
+
+#include "u_fs.h"
+
+#define DRIVER_NAME "g_ffs"
+#define DRIVER_DESC "USB Function Filesystem"
+#define DRIVER_VERSION "24 Aug 2004"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Michal Nazarewicz");
+MODULE_LICENSE("GPL");
+
+#define GFS_VENDOR_ID 0x1d6b /* Linux Foundation */
+#define GFS_PRODUCT_ID 0x0105 /* FunctionFS Gadget */
+
+#define GFS_MAX_DEVS 10
+
+USB_GADGET_COMPOSITE_OPTIONS();
+
+static struct usb_device_descriptor gfs_dev_desc = {
+ .bLength = sizeof gfs_dev_desc,
+ .bDescriptorType = USB_DT_DEVICE,
+
+ .bcdUSB = cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_PER_INTERFACE,
+
+ .idVendor = cpu_to_le16(GFS_VENDOR_ID),
+ .idProduct = cpu_to_le16(GFS_PRODUCT_ID),
+};
+
+static char *func_names[GFS_MAX_DEVS];
+static unsigned int func_num;
+
+module_param_named(bDeviceClass, gfs_dev_desc.bDeviceClass, byte, 0644);
+MODULE_PARM_DESC(bDeviceClass, "USB Device class");
+module_param_named(bDeviceSubClass, gfs_dev_desc.bDeviceSubClass, byte, 0644);
+MODULE_PARM_DESC(bDeviceSubClass, "USB Device subclass");
+module_param_named(bDeviceProtocol, gfs_dev_desc.bDeviceProtocol, byte, 0644);
+MODULE_PARM_DESC(bDeviceProtocol, "USB Device protocol");
+module_param_array_named(functions, func_names, charp, &func_num, 0);
+MODULE_PARM_DESC(functions, "USB Functions list");
+
+static const struct usb_descriptor_header *gfs_otg_desc[] = {
+ (const struct usb_descriptor_header *)
+ &(const struct usb_otg_descriptor) {
+ .bLength = sizeof(struct usb_otg_descriptor),
+ .bDescriptorType = USB_DT_OTG,
+
+ /*
+ * REVISIT SRP-only hardware is possible, although
+ * it would not be called "OTG" ...
+ */
+ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
+ },
+
+ NULL
+};
+
+/* String IDs are assigned dynamically */
+static struct usb_string gfs_strings[] = {
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
+#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
+ { .s = "FunctionFS + RNDIS" },
+#endif
+#ifdef CONFIG_USB_FUNCTIONFS_ETH
+ { .s = "FunctionFS + ECM" },
+#endif
+#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
+ { .s = "FunctionFS" },
+#endif
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings *gfs_dev_strings[] = {
+ &(struct usb_gadget_strings) {
+ .language = 0x0409, /* en-us */
+ .strings = gfs_strings,
+ },
+ NULL,
+};
+
+struct gfs_configuration {
+ struct usb_configuration c;
+ int (*eth)(struct usb_configuration *c);
+ int num;
+};
+
+static struct gfs_configuration gfs_configurations[] = {
+#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
+ {
+ .eth = bind_rndis_config,
+ },
+#endif
+
+#ifdef CONFIG_USB_FUNCTIONFS_ETH
+ {
+ .eth = eth_bind_config,
+ },
+#endif
+
+#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
+ {
+ },
+#endif
+};
+
+static void *functionfs_acquire_dev(struct ffs_dev *dev);
+static void functionfs_release_dev(struct ffs_dev *dev);
+static int functionfs_ready_callback(struct ffs_data *ffs);
+static void functionfs_closed_callback(struct ffs_data *ffs);
+static int gfs_bind(struct usb_composite_dev *cdev);
+static int gfs_unbind(struct usb_composite_dev *cdev);
+static int gfs_do_config(struct usb_configuration *c);
+
+
+static struct usb_composite_driver gfs_driver = {
+ .name = DRIVER_NAME,
+ .dev = &gfs_dev_desc,
+ .strings = gfs_dev_strings,
+ .max_speed = USB_SPEED_HIGH,
+ .bind = gfs_bind,
+ .unbind = gfs_unbind,
+};
+
+static unsigned int missing_funcs;
+static bool gfs_registered;
+static bool gfs_single_func;
+static struct usb_function_instance **fi_ffs;
+static struct usb_function **f_ffs[] = {
+#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
+ NULL,
+#endif
+
+#ifdef CONFIG_USB_FUNCTIONFS_ETH
+ NULL,
+#endif
+
+#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
+ NULL,
+#endif
+};
+
+#define N_CONF ARRAY_SIZE(f_ffs)
+
+static int __init gfs_init(void)
+{
+ struct f_fs_opts *opts;
+ int i;
+ int ret = 0;
+
+ ENTER();
+
+ if (func_num < 2) {
+ gfs_single_func = true;
+ func_num = 1;
+ }
+
+ /*
+ * Allocate in one chunk for easier maintenance
+ */
+ f_ffs[0] = kcalloc(func_num * N_CONF, sizeof(*f_ffs), GFP_KERNEL);
+ if (!f_ffs[0]) {
+ ret = -ENOMEM;
+ goto no_func;
+ }
+ for (i = 1; i < N_CONF; ++i)
+ f_ffs[i] = f_ffs[0] + i * func_num;
+
+ fi_ffs = kcalloc(func_num, sizeof(*fi_ffs), GFP_KERNEL);
+ if (!fi_ffs) {
+ ret = -ENOMEM;
+ goto no_func;
+ }
+
+ for (i = 0; i < func_num; i++) {
+ fi_ffs[i] = usb_get_function_instance("ffs");
+ if (IS_ERR(fi_ffs[i])) {
+ ret = PTR_ERR(fi_ffs[i]);
+ --i;
+ goto no_dev;
+ }
+ opts = to_f_fs_opts(fi_ffs[i]);
+ if (gfs_single_func)
+ ret = ffs_single_dev(opts->dev);
+ else
+ ret = ffs_name_dev(opts->dev, func_names[i]);
+ if (ret)
+ goto no_dev;
+ opts->dev->ffs_ready_callback = functionfs_ready_callback;
+ opts->dev->ffs_closed_callback = functionfs_closed_callback;
+ opts->dev->ffs_acquire_dev_callback = functionfs_acquire_dev;
+ opts->dev->ffs_release_dev_callback = functionfs_release_dev;
+ opts->no_configfs = true;
+ }
+
+ missing_funcs = func_num;
+
+ return 0;
+no_dev:
+ while (i >= 0)
+ usb_put_function_instance(fi_ffs[i--]);
+ kfree(fi_ffs);
+no_func:
+ kfree(f_ffs[0]);
+ return ret;
+}
+module_init(gfs_init);
+
+static void __exit gfs_exit(void)
+{
+ int i;
+
+ ENTER();
+
+ if (gfs_registered)
+ usb_composite_unregister(&gfs_driver);
+ gfs_registered = false;
+
+ kfree(f_ffs[0]);
+
+ for (i = 0; i < func_num; i++)
+ usb_put_function_instance(fi_ffs[i]);
+
+ kfree(fi_ffs);
+}
+module_exit(gfs_exit);
+
+static void *functionfs_acquire_dev(struct ffs_dev *dev)
+{
+ if (!try_module_get(THIS_MODULE))
+ return ERR_PTR(-ENOENT);
+
+ return NULL;
+}
+
+static void functionfs_release_dev(struct ffs_dev *dev)
+{
+ module_put(THIS_MODULE);
+}
+
+/*
+ * The caller of this function takes ffs_lock
+ */
+static int functionfs_ready_callback(struct ffs_data *ffs)
+{
+ int ret = 0;
+
+ if (--missing_funcs)
+ return 0;
+
+ if (gfs_registered)
+ return -EBUSY;
+
+ gfs_registered = true;
+
+ ret = usb_composite_probe(&gfs_driver);
+ if (unlikely(ret < 0)) {
+ ++missing_funcs;
+ gfs_registered = false;
+ }
+
+ return ret;
+}
+
+/*
+ * The caller of this function takes ffs_lock
+ */
+static void functionfs_closed_callback(struct ffs_data *ffs)
+{
+ missing_funcs++;
+
+ if (gfs_registered)
+ usb_composite_unregister(&gfs_driver);
+ gfs_registered = false;
+}
+
+/*
+ * It is assumed that gfs_bind is called from a context where ffs_lock is held
+ */
+static int gfs_bind(struct usb_composite_dev *cdev)
+{
+#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS
+ struct net_device *net;
+#endif
+ int ret, i;
+
+ ENTER();
+
+ if (missing_funcs)
+ return -ENODEV;
+#if defined CONFIG_USB_FUNCTIONFS_ETH
+ if (can_support_ecm(cdev->gadget)) {
+ struct f_ecm_opts *ecm_opts;
+
+ fi_ecm = usb_get_function_instance("ecm");
+ if (IS_ERR(fi_ecm))
+ return PTR_ERR(fi_ecm);
+ ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst);
+ net = ecm_opts->net;
+ } else {
+ struct f_gether_opts *geth_opts;
+
+ fi_geth = usb_get_function_instance("geth");
+ if (IS_ERR(fi_geth))
+ return PTR_ERR(fi_geth);
+ geth_opts = container_of(fi_geth, struct f_gether_opts,
+ func_inst);
+ net = geth_opts->net;
+ }
+#endif
+
+#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
+ {
+ struct f_rndis_opts *rndis_opts;
+
+ fi_rndis = usb_get_function_instance("rndis");
+ if (IS_ERR(fi_rndis)) {
+ ret = PTR_ERR(fi_rndis);
+ goto error;
+ }
+ rndis_opts = container_of(fi_rndis, struct f_rndis_opts,
+ func_inst);
+#ifndef CONFIG_USB_FUNCTIONFS_ETH
+ net = rndis_opts->net;
+#endif
+ }
+#endif
+
+#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS
+ gether_set_qmult(net, qmult);
+ if (!gether_set_host_addr(net, host_addr))
+ pr_info("using host ethernet address: %s", host_addr);
+ if (!gether_set_dev_addr(net, dev_addr))
+ pr_info("using self ethernet address: %s", dev_addr);
+#endif
+
+#if defined CONFIG_USB_FUNCTIONFS_RNDIS && defined CONFIG_USB_FUNCTIONFS_ETH
+ gether_set_gadget(net, cdev->gadget);
+ ret = gether_register_netdev(net);
+ if (ret)
+ goto error_rndis;
+
+ if (can_support_ecm(cdev->gadget)) {
+ struct f_ecm_opts *ecm_opts;
+
+ ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst);
+ ecm_opts->bound = true;
+ } else {
+ struct f_gether_opts *geth_opts;
+
+ geth_opts = container_of(fi_geth, struct f_gether_opts,
+ func_inst);
+ geth_opts->bound = true;
+ }
+
+ rndis_borrow_net(fi_rndis, net);
+#endif
+
+ /* TODO: gstrings_attach? */
+ ret = usb_string_ids_tab(cdev, gfs_strings);
+ if (unlikely(ret < 0))
+ goto error_rndis;
+ gfs_dev_desc.iProduct = gfs_strings[USB_GADGET_PRODUCT_IDX].id;
+
+ for (i = 0; i < ARRAY_SIZE(gfs_configurations); ++i) {
+ struct gfs_configuration *c = gfs_configurations + i;
+ int sid = USB_GADGET_FIRST_AVAIL_IDX + i;
+
+ c->c.label = gfs_strings[sid].s;
+ c->c.iConfiguration = gfs_strings[sid].id;
+ c->c.bConfigurationValue = 1 + i;
+ c->c.bmAttributes = USB_CONFIG_ATT_SELFPOWER;
+
+ c->num = i;
+
+ ret = usb_add_config(cdev, &c->c, gfs_do_config);
+ if (unlikely(ret < 0))
+ goto error_unbind;
+ }
+ usb_composite_overwrite_options(cdev, &coverwrite);
+ return 0;
+
+/* TODO */
+error_unbind:
+error_rndis:
+#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
+ usb_put_function_instance(fi_rndis);
+error:
+#endif
+#if defined CONFIG_USB_FUNCTIONFS_ETH
+ if (can_support_ecm(cdev->gadget))
+ usb_put_function_instance(fi_ecm);
+ else
+ usb_put_function_instance(fi_geth);
+#endif
+ return ret;
+}
+
+/*
+ * It is assumed that gfs_unbind is called from a context where ffs_lock is held
+ */
+static int gfs_unbind(struct usb_composite_dev *cdev)
+{
+ int i;
+
+ ENTER();
+
+
+#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
+ usb_put_function(f_rndis);
+ usb_put_function_instance(fi_rndis);
+#endif
+
+#if defined CONFIG_USB_FUNCTIONFS_ETH
+ if (can_support_ecm(cdev->gadget)) {
+ usb_put_function(f_ecm);
+ usb_put_function_instance(fi_ecm);
+ } else {
+ usb_put_function(f_geth);
+ usb_put_function_instance(fi_geth);
+ }
+#endif
+ for (i = 0; i < N_CONF * func_num; ++i)
+ usb_put_function(*(f_ffs[0] + i));
+
+ return 0;
+}
+
+/*
+ * It is assumed that gfs_do_config is called from a context where
+ * ffs_lock is held
+ */
+static int gfs_do_config(struct usb_configuration *c)
+{
+ struct gfs_configuration *gc =
+ container_of(c, struct gfs_configuration, c);
+ int i;
+ int ret;
+
+ if (missing_funcs)
+ return -ENODEV;
+
+ if (gadget_is_otg(c->cdev->gadget)) {
+ c->descriptors = gfs_otg_desc;
+ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
+ if (gc->eth) {
+ ret = gc->eth(c);
+ if (unlikely(ret < 0))
+ return ret;
+ }
+
+ for (i = 0; i < func_num; i++) {
+ f_ffs[gc->num][i] = usb_get_function(fi_ffs[i]);
+ if (IS_ERR(f_ffs[gc->num][i])) {
+ ret = PTR_ERR(f_ffs[gc->num][i]);
+ goto error;
+ }
+ ret = usb_add_function(c, f_ffs[gc->num][i]);
+ if (ret < 0) {
+ usb_put_function(f_ffs[gc->num][i]);
+ goto error;
+ }
+ }
+
+ /*
+ * After previous do_configs there may be some invalid
+ * pointers in c->interface array. This happens every time
+ * a user space function with fewer interfaces than a user
+ * space function that was run before the new one is run. The
+ * compasit's set_config() assumes that if there is no more
+ * then MAX_CONFIG_INTERFACES interfaces in a configuration
+ * then there is a NULL pointer after the last interface in
+ * c->interface array. We need to make sure this is true.
+ */
+ if (c->next_interface_id < ARRAY_SIZE(c->interface))
+ c->interface[c->next_interface_id] = NULL;
+
+ return 0;
+error:
+ while (--i >= 0) {
+ if (!IS_ERR(f_ffs[gc->num][i]))
+ usb_remove_function(c, f_ffs[gc->num][i]);
+ usb_put_function(f_ffs[gc->num][i]);
+ }
+ return ret;
+}
+
+#ifdef CONFIG_USB_FUNCTIONFS_ETH
+
+static int eth_bind_config(struct usb_configuration *c)
+{
+ int status = 0;
+
+ if (can_support_ecm(c->cdev->gadget)) {
+ f_ecm = usb_get_function(fi_ecm);
+ if (IS_ERR(f_ecm))
+ return PTR_ERR(f_ecm);
+
+ status = usb_add_function(c, f_ecm);
+ if (status < 0)
+ usb_put_function(f_ecm);
+
+ } else {
+ f_geth = usb_get_function(fi_geth);
+ if (IS_ERR(f_geth))
+ return PTR_ERR(f_geth);
+
+ status = usb_add_function(c, f_geth);
+ if (status < 0)
+ usb_put_function(f_geth);
+ }
+ return status;
+}
+
+#endif
+
+#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
+
+static int bind_rndis_config(struct usb_configuration *c)
+{
+ int status = 0;
+
+ f_rndis = usb_get_function(fi_rndis);
+ if (IS_ERR(f_rndis))
+ return PTR_ERR(f_rndis);
+
+ status = usb_add_function(c, f_rndis);
+ if (status < 0)
+ usb_put_function(f_rndis);
+
+ return status;
+}
+
+#endif
diff --git a/drivers/usb/gadget/legacy/gmidi.c b/drivers/usb/gadget/legacy/gmidi.c
new file mode 100644
index 000000000..da19c486b
--- /dev/null
+++ b/drivers/usb/gadget/legacy/gmidi.c
@@ -0,0 +1,197 @@
+/*
+ * gmidi.c -- USB MIDI Gadget Driver
+ *
+ * Copyright (C) 2006 Thumtronics Pty Ltd.
+ * Developed for Thumtronics by Grey Innovation
+ * Ben Williamson <ben.williamson@greyinnovation.com>
+ *
+ * This software is distributed under the terms of the GNU General Public
+ * License ("GPL") version 2, as published by the Free Software Foundation.
+ *
+ * This code is based in part on:
+ *
+ * Gadget Zero driver, Copyright (C) 2003-2004 David Brownell.
+ * USB Audio driver, Copyright (C) 2002 by Takashi Iwai.
+ * USB MIDI driver, Copyright (C) 2002-2005 Clemens Ladisch.
+ *
+ * Refer to the USB Device Class Definition for MIDI Devices:
+ * http://www.usb.org/developers/devclass_docs/midi10.pdf
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.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/composite.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/midi.h>
+
+#include "gadget_chips.h"
+
+#include "u_midi.h"
+
+/*-------------------------------------------------------------------------*/
+
+MODULE_AUTHOR("Ben Williamson");
+MODULE_LICENSE("GPL v2");
+
+static const char shortname[] = "g_midi";
+static const char longname[] = "MIDI Gadget";
+
+USB_GADGET_COMPOSITE_OPTIONS();
+
+static int index = SNDRV_DEFAULT_IDX1;
+module_param(index, int, S_IRUGO);
+MODULE_PARM_DESC(index, "Index value for the USB MIDI Gadget adapter.");
+
+static char *id = SNDRV_DEFAULT_STR1;
+module_param(id, charp, S_IRUGO);
+MODULE_PARM_DESC(id, "ID string for the USB MIDI Gadget adapter.");
+
+static unsigned int buflen = 256;
+module_param(buflen, uint, S_IRUGO);
+MODULE_PARM_DESC(buflen, "MIDI buffer length");
+
+static unsigned int qlen = 32;
+module_param(qlen, uint, S_IRUGO);
+MODULE_PARM_DESC(qlen, "USB read request queue length");
+
+static unsigned int in_ports = 1;
+module_param(in_ports, uint, S_IRUGO);
+MODULE_PARM_DESC(in_ports, "Number of MIDI input ports");
+
+static unsigned int out_ports = 1;
+module_param(out_ports, uint, S_IRUGO);
+MODULE_PARM_DESC(out_ports, "Number of MIDI output ports");
+
+/* Thanks to Grey Innovation for donating this product ID.
+ *
+ * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
+ * Instead: allocate your own, using normal USB-IF procedures.
+ */
+#define DRIVER_VENDOR_NUM 0x17b3 /* Grey Innovation */
+#define DRIVER_PRODUCT_NUM 0x0004 /* Linux-USB "MIDI Gadget" */
+
+/* string IDs are assigned dynamically */
+
+#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX
+
+static struct usb_device_descriptor device_desc = {
+ .bLength = USB_DT_DEVICE_SIZE,
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = __constant_cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_PER_INTERFACE,
+ .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_NUM),
+ .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_NUM),
+ /* .iManufacturer = DYNAMIC */
+ /* .iProduct = DYNAMIC */
+ .bNumConfigurations = 1,
+};
+
+static struct usb_string strings_dev[] = {
+ [USB_GADGET_MANUFACTURER_IDX].s = "Grey Innovation",
+ [USB_GADGET_PRODUCT_IDX].s = "MIDI Gadget",
+ [USB_GADGET_SERIAL_IDX].s = "",
+ [STRING_DESCRIPTION_IDX].s = "MIDI",
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+ .language = 0x0409, /* en-us */
+ .strings = strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+ &stringtab_dev,
+ NULL,
+};
+
+static struct usb_function_instance *fi_midi;
+static struct usb_function *f_midi;
+
+static int midi_unbind(struct usb_composite_dev *dev)
+{
+ usb_put_function(f_midi);
+ usb_put_function_instance(fi_midi);
+ return 0;
+}
+
+static struct usb_configuration midi_config = {
+ .label = "MIDI Gadget",
+ .bConfigurationValue = 1,
+ /* .iConfiguration = DYNAMIC */
+ .bmAttributes = USB_CONFIG_ATT_ONE,
+ .MaxPower = CONFIG_USB_GADGET_VBUS_DRAW,
+};
+
+static int midi_bind_config(struct usb_configuration *c)
+{
+ int status;
+
+ f_midi = usb_get_function(fi_midi);
+ if (IS_ERR(f_midi))
+ return PTR_ERR(f_midi);
+
+ status = usb_add_function(c, f_midi);
+ if (status < 0) {
+ usb_put_function(f_midi);
+ return status;
+ }
+
+ return 0;
+}
+
+static int midi_bind(struct usb_composite_dev *cdev)
+{
+ struct f_midi_opts *midi_opts;
+ int status;
+
+ fi_midi = usb_get_function_instance("midi");
+ if (IS_ERR(fi_midi))
+ return PTR_ERR(fi_midi);
+
+ midi_opts = container_of(fi_midi, struct f_midi_opts, func_inst);
+ midi_opts->index = index;
+ midi_opts->id = id;
+ midi_opts->in_ports = in_ports;
+ midi_opts->out_ports = out_ports;
+ midi_opts->buflen = buflen;
+ midi_opts->qlen = qlen;
+
+ status = usb_string_ids_tab(cdev, strings_dev);
+ if (status < 0)
+ goto put;
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+ midi_config.iConfiguration = strings_dev[STRING_DESCRIPTION_IDX].id;
+
+ status = usb_add_config(cdev, &midi_config, midi_bind_config);
+ if (status < 0)
+ goto put;
+ usb_composite_overwrite_options(cdev, &coverwrite);
+ pr_info("%s\n", longname);
+ return 0;
+put:
+ usb_put_function_instance(fi_midi);
+ return status;
+}
+
+static struct usb_composite_driver midi_driver = {
+ .name = (char *) longname,
+ .dev = &device_desc,
+ .strings = dev_strings,
+ .max_speed = USB_SPEED_HIGH,
+ .bind = midi_bind,
+ .unbind = midi_unbind,
+};
+
+module_usb_composite_driver(midi_driver);
diff --git a/drivers/usb/gadget/legacy/hid.c b/drivers/usb/gadget/legacy/hid.c
new file mode 100644
index 000000000..2baa57268
--- /dev/null
+++ b/drivers/usb/gadget/legacy/hid.c
@@ -0,0 +1,298 @@
+/*
+ * hid.c -- HID Composite driver
+ *
+ * Based on multi.c
+ *
+ * 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/platform_device.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/g_hid.h>
+
+#include "gadget_chips.h"
+#define DRIVER_DESC "HID Gadget"
+#define DRIVER_VERSION "2010/03/16"
+
+#include "u_hid.h"
+
+/*-------------------------------------------------------------------------*/
+
+#define HIDG_VENDOR_NUM 0x0525 /* XXX NetChip */
+#define HIDG_PRODUCT_NUM 0xa4ac /* Linux-USB HID gadget */
+
+/*-------------------------------------------------------------------------*/
+
+struct hidg_func_node {
+ struct usb_function_instance *fi;
+ struct usb_function *f;
+ struct list_head node;
+ struct hidg_func_descriptor *func;
+};
+
+static LIST_HEAD(hidg_func_list);
+
+/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
+
+static struct usb_device_descriptor device_desc = {
+ .bLength = sizeof device_desc,
+ .bDescriptorType = USB_DT_DEVICE,
+
+ .bcdUSB = cpu_to_le16(0x0200),
+
+ /* .bDeviceClass = USB_CLASS_COMM, */
+ /* .bDeviceSubClass = 0, */
+ /* .bDeviceProtocol = 0, */
+ .bDeviceClass = USB_CLASS_PER_INTERFACE,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+ /* .bMaxPacketSize0 = f(hardware) */
+
+ /* Vendor and product id can be overridden by module parameters. */
+ .idVendor = cpu_to_le16(HIDG_VENDOR_NUM),
+ .idProduct = cpu_to_le16(HIDG_PRODUCT_NUM),
+ /* .bcdDevice = f(hardware) */
+ /* .iManufacturer = DYNAMIC */
+ /* .iProduct = DYNAMIC */
+ /* NO SERIAL NUMBER */
+ .bNumConfigurations = 1,
+};
+
+static struct usb_otg_descriptor otg_descriptor = {
+ .bLength = sizeof otg_descriptor,
+ .bDescriptorType = USB_DT_OTG,
+
+ /* REVISIT SRP-only hardware is possible, although
+ * it would not be called "OTG" ...
+ */
+ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
+};
+
+static const struct usb_descriptor_header *otg_desc[] = {
+ (struct usb_descriptor_header *) &otg_descriptor,
+ NULL,
+};
+
+
+/* string IDs are assigned dynamically */
+static struct usb_string strings_dev[] = {
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+ .language = 0x0409, /* en-us */
+ .strings = strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+ &stringtab_dev,
+ NULL,
+};
+
+
+
+/****************************** Configurations ******************************/
+
+static int do_config(struct usb_configuration *c)
+{
+ struct hidg_func_node *e, *n;
+ int status = 0;
+
+ if (gadget_is_otg(c->cdev->gadget)) {
+ c->descriptors = otg_desc;
+ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
+ list_for_each_entry(e, &hidg_func_list, node) {
+ e->f = usb_get_function(e->fi);
+ if (IS_ERR(e->f))
+ goto put;
+ status = usb_add_function(c, e->f);
+ if (status < 0) {
+ usb_put_function(e->f);
+ goto put;
+ }
+ }
+
+ return 0;
+put:
+ list_for_each_entry(n, &hidg_func_list, node) {
+ if (n == e)
+ break;
+ usb_remove_function(c, n->f);
+ usb_put_function(n->f);
+ }
+ return status;
+}
+
+static struct usb_configuration config_driver = {
+ .label = "HID Gadget",
+ .bConfigurationValue = 1,
+ /* .iConfiguration = DYNAMIC */
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+};
+
+/****************************** Gadget Bind ******************************/
+
+static int hid_bind(struct usb_composite_dev *cdev)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ struct list_head *tmp;
+ struct hidg_func_node *n, *m;
+ struct f_hid_opts *hid_opts;
+ int status, funcs = 0;
+
+ list_for_each(tmp, &hidg_func_list)
+ funcs++;
+
+ if (!funcs)
+ return -ENODEV;
+
+ list_for_each_entry(n, &hidg_func_list, node) {
+ n->fi = usb_get_function_instance("hid");
+ if (IS_ERR(n->fi)) {
+ status = PTR_ERR(n->fi);
+ goto put;
+ }
+ hid_opts = container_of(n->fi, struct f_hid_opts, func_inst);
+ hid_opts->subclass = n->func->subclass;
+ hid_opts->protocol = n->func->protocol;
+ hid_opts->report_length = n->func->report_length;
+ hid_opts->report_desc_length = n->func->report_desc_length;
+ hid_opts->report_desc = n->func->report_desc;
+ }
+
+
+ /* Allocate string descriptor numbers ... note that string
+ * contents can be overridden by the composite_dev glue.
+ */
+
+ status = usb_string_ids_tab(cdev, strings_dev);
+ if (status < 0)
+ goto put;
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+
+ /* register our configuration */
+ status = usb_add_config(cdev, &config_driver, do_config);
+ if (status < 0)
+ goto put;
+
+ usb_composite_overwrite_options(cdev, &coverwrite);
+ dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n");
+
+ return 0;
+
+put:
+ list_for_each_entry(m, &hidg_func_list, node) {
+ if (m == n)
+ break;
+ usb_put_function_instance(m->fi);
+ }
+ return status;
+}
+
+static int hid_unbind(struct usb_composite_dev *cdev)
+{
+ struct hidg_func_node *n;
+
+ list_for_each_entry(n, &hidg_func_list, node) {
+ usb_put_function(n->f);
+ usb_put_function_instance(n->fi);
+ }
+ return 0;
+}
+
+static int hidg_plat_driver_probe(struct platform_device *pdev)
+{
+ struct hidg_func_descriptor *func = dev_get_platdata(&pdev->dev);
+ struct hidg_func_node *entry;
+
+ if (!func) {
+ dev_err(&pdev->dev, "Platform data missing\n");
+ return -ENODEV;
+ }
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ entry->func = func;
+ list_add_tail(&entry->node, &hidg_func_list);
+
+ return 0;
+}
+
+static int hidg_plat_driver_remove(struct platform_device *pdev)
+{
+ struct hidg_func_node *e, *n;
+
+ list_for_each_entry_safe(e, n, &hidg_func_list, node) {
+ list_del(&e->node);
+ kfree(e);
+ }
+
+ return 0;
+}
+
+
+/****************************** Some noise ******************************/
+
+
+static struct usb_composite_driver hidg_driver = {
+ .name = "g_hid",
+ .dev = &device_desc,
+ .strings = dev_strings,
+ .max_speed = USB_SPEED_HIGH,
+ .bind = hid_bind,
+ .unbind = hid_unbind,
+};
+
+static struct platform_driver hidg_plat_driver = {
+ .remove = hidg_plat_driver_remove,
+ .driver = {
+ .name = "hidg",
+ },
+};
+
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Fabien Chouteau, Peter Korsgaard");
+MODULE_LICENSE("GPL");
+
+static int __init hidg_init(void)
+{
+ int status;
+
+ status = platform_driver_probe(&hidg_plat_driver,
+ hidg_plat_driver_probe);
+ if (status < 0)
+ return status;
+
+ status = usb_composite_probe(&hidg_driver);
+ if (status < 0)
+ platform_driver_unregister(&hidg_plat_driver);
+
+ return status;
+}
+module_init(hidg_init);
+
+static void __exit hidg_cleanup(void)
+{
+ usb_composite_unregister(&hidg_driver);
+ platform_driver_unregister(&hidg_plat_driver);
+}
+module_exit(hidg_cleanup);
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
new file mode 100644
index 000000000..2030565c6
--- /dev/null
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -0,0 +1,2064 @@
+/*
+ * inode.c -- user mode filesystem api for usb gadget controllers
+ *
+ * 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 VERBOSE_DEBUG */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <linux/uts.h>
+#include <linux/wait.h>
+#include <linux/compiler.h>
+#include <asm/uaccess.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/mmu_context.h>
+#include <linux/aio.h>
+#include <linux/uio.h>
+
+#include <linux/device.h>
+#include <linux/moduleparam.h>
+
+#include <linux/usb/gadgetfs.h>
+#include <linux/usb/gadget.h>
+
+
+/*
+ * The gadgetfs API maps each endpoint to a file descriptor so that you
+ * can use standard synchronous read/write calls for I/O. There's some
+ * O_NONBLOCK and O_ASYNC/FASYNC style i/o support. Example usermode
+ * drivers show how this works in practice. You can also use AIO to
+ * eliminate I/O gaps between requests, to help when streaming data.
+ *
+ * Key parts that must be USB-specific are protocols defining how the
+ * read/write operations relate to the hardware state machines. There
+ * are two types of files. One type is for the device, implementing ep0.
+ * The other type is for each IN or OUT endpoint. In both cases, the
+ * user mode driver must configure the hardware before using it.
+ *
+ * - First, dev_config() is called when /dev/gadget/$CHIP is configured
+ * (by writing configuration and device descriptors). Afterwards it
+ * may serve as a source of device events, used to handle all control
+ * requests other than basic enumeration.
+ *
+ * - Then, after a SET_CONFIGURATION control request, ep_config() is
+ * called when each /dev/gadget/ep* file is configured (by writing
+ * endpoint descriptors). Afterwards these files are used to write()
+ * IN data or to read() OUT data. To halt the endpoint, a "wrong
+ * direction" request is issued (like reading an IN endpoint).
+ *
+ * Unlike "usbfs" the only ioctl()s are for things that are rare, and maybe
+ * not possible on all hardware. For example, precise fault handling with
+ * respect to data left in endpoint fifos after aborted operations; or
+ * selective clearing of endpoint halts, to implement SET_INTERFACE.
+ */
+
+#define DRIVER_DESC "USB Gadget filesystem"
+#define DRIVER_VERSION "24 Aug 2004"
+
+static const char driver_desc [] = DRIVER_DESC;
+static const char shortname [] = "gadgetfs";
+
+MODULE_DESCRIPTION (DRIVER_DESC);
+MODULE_AUTHOR ("David Brownell");
+MODULE_LICENSE ("GPL");
+
+static int ep_open(struct inode *, struct file *);
+
+
+/*----------------------------------------------------------------------*/
+
+#define GADGETFS_MAGIC 0xaee71ee7
+
+/* /dev/gadget/$CHIP represents ep0 and the whole device */
+enum ep0_state {
+ /* DISBLED is the initial state.
+ */
+ STATE_DEV_DISABLED = 0,
+
+ /* Only one open() of /dev/gadget/$CHIP; only one file tracks
+ * ep0/device i/o modes and binding to the controller. Driver
+ * must always write descriptors to initialize the device, then
+ * the device becomes UNCONNECTED until enumeration.
+ */
+ STATE_DEV_OPENED,
+
+ /* From then on, ep0 fd is in either of two basic modes:
+ * - (UN)CONNECTED: read usb_gadgetfs_event(s) from it
+ * - SETUP: read/write will transfer control data and succeed;
+ * or if "wrong direction", performs protocol stall
+ */
+ STATE_DEV_UNCONNECTED,
+ STATE_DEV_CONNECTED,
+ STATE_DEV_SETUP,
+
+ /* UNBOUND means the driver closed ep0, so the device won't be
+ * accessible again (DEV_DISABLED) until all fds are closed.
+ */
+ STATE_DEV_UNBOUND,
+};
+
+/* enough for the whole queue: most events invalidate others */
+#define N_EVENT 5
+
+struct dev_data {
+ spinlock_t lock;
+ atomic_t count;
+ enum ep0_state state; /* P: lock */
+ struct usb_gadgetfs_event event [N_EVENT];
+ unsigned ev_next;
+ struct fasync_struct *fasync;
+ u8 current_config;
+
+ /* drivers reading ep0 MUST handle control requests (SETUP)
+ * reported that way; else the host will time out.
+ */
+ unsigned usermode_setup : 1,
+ setup_in : 1,
+ setup_can_stall : 1,
+ setup_out_ready : 1,
+ setup_out_error : 1,
+ setup_abort : 1;
+ unsigned setup_wLength;
+
+ /* the rest is basically write-once */
+ struct usb_config_descriptor *config, *hs_config;
+ struct usb_device_descriptor *dev;
+ struct usb_request *req;
+ struct usb_gadget *gadget;
+ struct list_head epfiles;
+ void *buf;
+ wait_queue_head_t wait;
+ struct super_block *sb;
+ struct dentry *dentry;
+
+ /* except this scratch i/o buffer for ep0 */
+ u8 rbuf [256];
+};
+
+static inline void get_dev (struct dev_data *data)
+{
+ atomic_inc (&data->count);
+}
+
+static void put_dev (struct dev_data *data)
+{
+ if (likely (!atomic_dec_and_test (&data->count)))
+ return;
+ /* needs no more cleanup */
+ BUG_ON (waitqueue_active (&data->wait));
+ kfree (data);
+}
+
+static struct dev_data *dev_new (void)
+{
+ struct dev_data *dev;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return NULL;
+ dev->state = STATE_DEV_DISABLED;
+ atomic_set (&dev->count, 1);
+ spin_lock_init (&dev->lock);
+ INIT_LIST_HEAD (&dev->epfiles);
+ init_waitqueue_head (&dev->wait);
+ return dev;
+}
+
+/*----------------------------------------------------------------------*/
+
+/* other /dev/gadget/$ENDPOINT files represent endpoints */
+enum ep_state {
+ STATE_EP_DISABLED = 0,
+ STATE_EP_READY,
+ STATE_EP_ENABLED,
+ STATE_EP_UNBOUND,
+};
+
+struct ep_data {
+ struct mutex lock;
+ enum ep_state state;
+ atomic_t count;
+ struct dev_data *dev;
+ /* must hold dev->lock before accessing ep or req */
+ struct usb_ep *ep;
+ struct usb_request *req;
+ ssize_t status;
+ char name [16];
+ struct usb_endpoint_descriptor desc, hs_desc;
+ struct list_head epfiles;
+ wait_queue_head_t wait;
+ struct dentry *dentry;
+};
+
+static inline void get_ep (struct ep_data *data)
+{
+ atomic_inc (&data->count);
+}
+
+static void put_ep (struct ep_data *data)
+{
+ if (likely (!atomic_dec_and_test (&data->count)))
+ return;
+ put_dev (data->dev);
+ /* needs no more cleanup */
+ BUG_ON (!list_empty (&data->epfiles));
+ BUG_ON (waitqueue_active (&data->wait));
+ kfree (data);
+}
+
+/*----------------------------------------------------------------------*/
+
+/* most "how to use the hardware" policy choices are in userspace:
+ * mapping endpoint roles (which the driver needs) to the capabilities
+ * which the usb controller has. most of those capabilities are exposed
+ * implicitly, starting with the driver name and then endpoint names.
+ */
+
+static const char *CHIP;
+
+/*----------------------------------------------------------------------*/
+
+/* NOTE: don't use dev_printk calls before binding to the gadget
+ * at the end of ep0 configuration, or after unbind.
+ */
+
+/* too wordy: dev_printk(level , &(d)->gadget->dev , fmt , ## args) */
+#define xprintk(d,level,fmt,args...) \
+ printk(level "%s: " fmt , shortname , ## args)
+
+#ifdef 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 VDEBUG DBG
+#else
+#define VDEBUG(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)
+
+
+/*----------------------------------------------------------------------*/
+
+/* SYNCHRONOUS ENDPOINT OPERATIONS (bulk/intr/iso)
+ *
+ * After opening, configure non-control endpoints. Then use normal
+ * stream read() and write() requests; and maybe ioctl() to get more
+ * precise FIFO status when recovering from cancellation.
+ */
+
+static void epio_complete (struct usb_ep *ep, struct usb_request *req)
+{
+ struct ep_data *epdata = ep->driver_data;
+
+ if (!req->context)
+ return;
+ if (req->status)
+ epdata->status = req->status;
+ else
+ epdata->status = req->actual;
+ complete ((struct completion *)req->context);
+}
+
+/* tasklock endpoint, returning when it's connected.
+ * still need dev->lock to use epdata->ep.
+ */
+static int
+get_ready_ep (unsigned f_flags, struct ep_data *epdata, bool is_write)
+{
+ int val;
+
+ if (f_flags & O_NONBLOCK) {
+ if (!mutex_trylock(&epdata->lock))
+ goto nonblock;
+ if (epdata->state != STATE_EP_ENABLED &&
+ (!is_write || epdata->state != STATE_EP_READY)) {
+ mutex_unlock(&epdata->lock);
+nonblock:
+ val = -EAGAIN;
+ } else
+ val = 0;
+ return val;
+ }
+
+ val = mutex_lock_interruptible(&epdata->lock);
+ if (val < 0)
+ return val;
+
+ switch (epdata->state) {
+ case STATE_EP_ENABLED:
+ return 0;
+ case STATE_EP_READY: /* not configured yet */
+ if (is_write)
+ return 0;
+ // FALLTHRU
+ case STATE_EP_UNBOUND: /* clean disconnect */
+ break;
+ // case STATE_EP_DISABLED: /* "can't happen" */
+ default: /* error! */
+ pr_debug ("%s: ep %p not available, state %d\n",
+ shortname, epdata, epdata->state);
+ }
+ mutex_unlock(&epdata->lock);
+ return -ENODEV;
+}
+
+static ssize_t
+ep_io (struct ep_data *epdata, void *buf, unsigned len)
+{
+ DECLARE_COMPLETION_ONSTACK (done);
+ int value;
+
+ spin_lock_irq (&epdata->dev->lock);
+ if (likely (epdata->ep != NULL)) {
+ struct usb_request *req = epdata->req;
+
+ req->context = &done;
+ req->complete = epio_complete;
+ req->buf = buf;
+ req->length = len;
+ value = usb_ep_queue (epdata->ep, req, GFP_ATOMIC);
+ } else
+ value = -ENODEV;
+ spin_unlock_irq (&epdata->dev->lock);
+
+ if (likely (value == 0)) {
+ value = wait_event_interruptible (done.wait, done.done);
+ if (value != 0) {
+ spin_lock_irq (&epdata->dev->lock);
+ if (likely (epdata->ep != NULL)) {
+ DBG (epdata->dev, "%s i/o interrupted\n",
+ epdata->name);
+ usb_ep_dequeue (epdata->ep, epdata->req);
+ spin_unlock_irq (&epdata->dev->lock);
+
+ wait_event (done.wait, done.done);
+ if (epdata->status == -ECONNRESET)
+ epdata->status = -EINTR;
+ } else {
+ spin_unlock_irq (&epdata->dev->lock);
+
+ DBG (epdata->dev, "endpoint gone\n");
+ epdata->status = -ENODEV;
+ }
+ }
+ return epdata->status;
+ }
+ return value;
+}
+
+static int
+ep_release (struct inode *inode, struct file *fd)
+{
+ struct ep_data *data = fd->private_data;
+ int value;
+
+ value = mutex_lock_interruptible(&data->lock);
+ if (value < 0)
+ return value;
+
+ /* clean up if this can be reopened */
+ if (data->state != STATE_EP_UNBOUND) {
+ data->state = STATE_EP_DISABLED;
+ data->desc.bDescriptorType = 0;
+ data->hs_desc.bDescriptorType = 0;
+ usb_ep_disable(data->ep);
+ }
+ mutex_unlock(&data->lock);
+ put_ep (data);
+ return 0;
+}
+
+static long ep_ioctl(struct file *fd, unsigned code, unsigned long value)
+{
+ struct ep_data *data = fd->private_data;
+ int status;
+
+ if ((status = get_ready_ep (fd->f_flags, data, false)) < 0)
+ return status;
+
+ spin_lock_irq (&data->dev->lock);
+ if (likely (data->ep != NULL)) {
+ switch (code) {
+ case GADGETFS_FIFO_STATUS:
+ status = usb_ep_fifo_status (data->ep);
+ break;
+ case GADGETFS_FIFO_FLUSH:
+ usb_ep_fifo_flush (data->ep);
+ break;
+ case GADGETFS_CLEAR_HALT:
+ status = usb_ep_clear_halt (data->ep);
+ break;
+ default:
+ status = -ENOTTY;
+ }
+ } else
+ status = -ENODEV;
+ spin_unlock_irq (&data->dev->lock);
+ mutex_unlock(&data->lock);
+ return status;
+}
+
+/*----------------------------------------------------------------------*/
+
+/* ASYNCHRONOUS ENDPOINT I/O OPERATIONS (bulk/intr/iso) */
+
+struct kiocb_priv {
+ struct usb_request *req;
+ struct ep_data *epdata;
+ struct kiocb *iocb;
+ struct mm_struct *mm;
+ struct work_struct work;
+ void *buf;
+ struct iov_iter to;
+ const void *to_free;
+ unsigned actual;
+};
+
+static int ep_aio_cancel(struct kiocb *iocb)
+{
+ struct kiocb_priv *priv = iocb->private;
+ struct ep_data *epdata;
+ int value;
+
+ local_irq_disable();
+ epdata = priv->epdata;
+ // spin_lock(&epdata->dev->lock);
+ if (likely(epdata && epdata->ep && priv->req))
+ value = usb_ep_dequeue (epdata->ep, priv->req);
+ else
+ value = -EINVAL;
+ // spin_unlock(&epdata->dev->lock);
+ local_irq_enable();
+
+ return value;
+}
+
+static void ep_user_copy_worker(struct work_struct *work)
+{
+ struct kiocb_priv *priv = container_of(work, struct kiocb_priv, work);
+ struct mm_struct *mm = priv->mm;
+ struct kiocb *iocb = priv->iocb;
+ size_t ret;
+
+ use_mm(mm);
+ ret = copy_to_iter(priv->buf, priv->actual, &priv->to);
+ unuse_mm(mm);
+ if (!ret)
+ ret = -EFAULT;
+
+ /* completing the iocb can drop the ctx and mm, don't touch mm after */
+ iocb->ki_complete(iocb, ret, ret);
+
+ kfree(priv->buf);
+ kfree(priv->to_free);
+ kfree(priv);
+}
+
+static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct kiocb *iocb = req->context;
+ struct kiocb_priv *priv = iocb->private;
+ struct ep_data *epdata = priv->epdata;
+
+ /* lock against disconnect (and ideally, cancel) */
+ spin_lock(&epdata->dev->lock);
+ priv->req = NULL;
+ priv->epdata = NULL;
+
+ /* if this was a write or a read returning no data then we
+ * don't need to copy anything to userspace, so we can
+ * complete the aio request immediately.
+ */
+ if (priv->to_free == NULL || unlikely(req->actual == 0)) {
+ kfree(req->buf);
+ kfree(priv->to_free);
+ kfree(priv);
+ iocb->private = NULL;
+ /* aio_complete() reports bytes-transferred _and_ faults */
+
+ iocb->ki_complete(iocb, req->actual ? req->actual : req->status,
+ req->status);
+ } else {
+ /* ep_copy_to_user() won't report both; we hide some faults */
+ if (unlikely(0 != req->status))
+ DBG(epdata->dev, "%s fault %d len %d\n",
+ ep->name, req->status, req->actual);
+
+ priv->buf = req->buf;
+ priv->actual = req->actual;
+ INIT_WORK(&priv->work, ep_user_copy_worker);
+ schedule_work(&priv->work);
+ }
+ spin_unlock(&epdata->dev->lock);
+
+ usb_ep_free_request(ep, req);
+ put_ep(epdata);
+}
+
+static ssize_t ep_aio(struct kiocb *iocb,
+ struct kiocb_priv *priv,
+ struct ep_data *epdata,
+ char *buf,
+ size_t len)
+{
+ struct usb_request *req;
+ ssize_t value;
+
+ iocb->private = priv;
+ priv->iocb = iocb;
+
+ kiocb_set_cancel_fn(iocb, ep_aio_cancel);
+ get_ep(epdata);
+ priv->epdata = epdata;
+ priv->actual = 0;
+ priv->mm = current->mm; /* mm teardown waits for iocbs in exit_aio() */
+
+ /* each kiocb is coupled to one usb_request, but we can't
+ * allocate or submit those if the host disconnected.
+ */
+ spin_lock_irq(&epdata->dev->lock);
+ value = -ENODEV;
+ if (unlikely(epdata->ep))
+ goto fail;
+
+ req = usb_ep_alloc_request(epdata->ep, GFP_ATOMIC);
+ value = -ENOMEM;
+ if (unlikely(!req))
+ goto fail;
+
+ priv->req = req;
+ req->buf = buf;
+ req->length = len;
+ req->complete = ep_aio_complete;
+ req->context = iocb;
+ value = usb_ep_queue(epdata->ep, req, GFP_ATOMIC);
+ if (unlikely(0 != value)) {
+ usb_ep_free_request(epdata->ep, req);
+ goto fail;
+ }
+ spin_unlock_irq(&epdata->dev->lock);
+ return -EIOCBQUEUED;
+
+fail:
+ spin_unlock_irq(&epdata->dev->lock);
+ kfree(priv->to_free);
+ kfree(priv);
+ put_ep(epdata);
+ return value;
+}
+
+static ssize_t
+ep_read_iter(struct kiocb *iocb, struct iov_iter *to)
+{
+ struct file *file = iocb->ki_filp;
+ struct ep_data *epdata = file->private_data;
+ size_t len = iov_iter_count(to);
+ ssize_t value;
+ char *buf;
+
+ if ((value = get_ready_ep(file->f_flags, epdata, false)) < 0)
+ return value;
+
+ /* halt any endpoint by doing a "wrong direction" i/o call */
+ if (usb_endpoint_dir_in(&epdata->desc)) {
+ if (usb_endpoint_xfer_isoc(&epdata->desc) ||
+ !is_sync_kiocb(iocb)) {
+ mutex_unlock(&epdata->lock);
+ return -EINVAL;
+ }
+ DBG (epdata->dev, "%s halt\n", epdata->name);
+ spin_lock_irq(&epdata->dev->lock);
+ if (likely(epdata->ep != NULL))
+ usb_ep_set_halt(epdata->ep);
+ spin_unlock_irq(&epdata->dev->lock);
+ mutex_unlock(&epdata->lock);
+ return -EBADMSG;
+ }
+
+ buf = kmalloc(len, GFP_KERNEL);
+ if (unlikely(!buf)) {
+ mutex_unlock(&epdata->lock);
+ return -ENOMEM;
+ }
+ if (is_sync_kiocb(iocb)) {
+ value = ep_io(epdata, buf, len);
+ if (value >= 0 && copy_to_iter(buf, value, to))
+ value = -EFAULT;
+ } else {
+ struct kiocb_priv *priv = kzalloc(sizeof *priv, GFP_KERNEL);
+ value = -ENOMEM;
+ if (!priv)
+ goto fail;
+ priv->to_free = dup_iter(&priv->to, to, GFP_KERNEL);
+ if (!priv->to_free) {
+ kfree(priv);
+ goto fail;
+ }
+ value = ep_aio(iocb, priv, epdata, buf, len);
+ if (value == -EIOCBQUEUED)
+ buf = NULL;
+ }
+fail:
+ kfree(buf);
+ mutex_unlock(&epdata->lock);
+ return value;
+}
+
+static ssize_t ep_config(struct ep_data *, const char *, size_t);
+
+static ssize_t
+ep_write_iter(struct kiocb *iocb, struct iov_iter *from)
+{
+ struct file *file = iocb->ki_filp;
+ struct ep_data *epdata = file->private_data;
+ size_t len = iov_iter_count(from);
+ bool configured;
+ ssize_t value;
+ char *buf;
+
+ if ((value = get_ready_ep(file->f_flags, epdata, true)) < 0)
+ return value;
+
+ configured = epdata->state == STATE_EP_ENABLED;
+
+ /* halt any endpoint by doing a "wrong direction" i/o call */
+ if (configured && !usb_endpoint_dir_in(&epdata->desc)) {
+ if (usb_endpoint_xfer_isoc(&epdata->desc) ||
+ !is_sync_kiocb(iocb)) {
+ mutex_unlock(&epdata->lock);
+ return -EINVAL;
+ }
+ DBG (epdata->dev, "%s halt\n", epdata->name);
+ spin_lock_irq(&epdata->dev->lock);
+ if (likely(epdata->ep != NULL))
+ usb_ep_set_halt(epdata->ep);
+ spin_unlock_irq(&epdata->dev->lock);
+ mutex_unlock(&epdata->lock);
+ return -EBADMSG;
+ }
+
+ buf = kmalloc(len, GFP_KERNEL);
+ if (unlikely(!buf)) {
+ mutex_unlock(&epdata->lock);
+ return -ENOMEM;
+ }
+
+ if (unlikely(copy_from_iter(buf, len, from) != len)) {
+ value = -EFAULT;
+ goto out;
+ }
+
+ if (unlikely(!configured)) {
+ value = ep_config(epdata, buf, len);
+ } else if (is_sync_kiocb(iocb)) {
+ value = ep_io(epdata, buf, len);
+ } else {
+ struct kiocb_priv *priv = kzalloc(sizeof *priv, GFP_KERNEL);
+ value = -ENOMEM;
+ if (priv) {
+ value = ep_aio(iocb, priv, epdata, buf, len);
+ if (value == -EIOCBQUEUED)
+ buf = NULL;
+ }
+ }
+out:
+ kfree(buf);
+ mutex_unlock(&epdata->lock);
+ return value;
+}
+
+/*----------------------------------------------------------------------*/
+
+/* used after endpoint configuration */
+static const struct file_operations ep_io_operations = {
+ .owner = THIS_MODULE,
+
+ .open = ep_open,
+ .release = ep_release,
+ .llseek = no_llseek,
+ .unlocked_ioctl = ep_ioctl,
+ .read_iter = ep_read_iter,
+ .write_iter = ep_write_iter,
+};
+
+/* ENDPOINT INITIALIZATION
+ *
+ * fd = open ("/dev/gadget/$ENDPOINT", O_RDWR)
+ * status = write (fd, descriptors, sizeof descriptors)
+ *
+ * That write establishes the endpoint configuration, configuring
+ * the controller to process bulk, interrupt, or isochronous transfers
+ * at the right maxpacket size, and so on.
+ *
+ * The descriptors are message type 1, identified by a host order u32
+ * at the beginning of what's written. Descriptor order is: full/low
+ * speed descriptor, then optional high speed descriptor.
+ */
+static ssize_t
+ep_config (struct ep_data *data, const char *buf, size_t len)
+{
+ struct usb_ep *ep;
+ u32 tag;
+ int value, length = len;
+
+ if (data->state != STATE_EP_READY) {
+ value = -EL2HLT;
+ goto fail;
+ }
+
+ value = len;
+ if (len < USB_DT_ENDPOINT_SIZE + 4)
+ goto fail0;
+
+ /* we might need to change message format someday */
+ memcpy(&tag, buf, 4);
+ if (tag != 1) {
+ DBG(data->dev, "config %s, bad tag %d\n", data->name, tag);
+ goto fail0;
+ }
+ buf += 4;
+ len -= 4;
+
+ /* NOTE: audio endpoint extensions not accepted here;
+ * just don't include the extra bytes.
+ */
+
+ /* full/low speed descriptor, then high speed */
+ memcpy(&data->desc, buf, USB_DT_ENDPOINT_SIZE);
+ if (data->desc.bLength != USB_DT_ENDPOINT_SIZE
+ || data->desc.bDescriptorType != USB_DT_ENDPOINT)
+ goto fail0;
+ if (len != USB_DT_ENDPOINT_SIZE) {
+ if (len != 2 * USB_DT_ENDPOINT_SIZE)
+ goto fail0;
+ memcpy(&data->hs_desc, buf + USB_DT_ENDPOINT_SIZE,
+ USB_DT_ENDPOINT_SIZE);
+ if (data->hs_desc.bLength != USB_DT_ENDPOINT_SIZE
+ || data->hs_desc.bDescriptorType
+ != USB_DT_ENDPOINT) {
+ DBG(data->dev, "config %s, bad hs length or type\n",
+ data->name);
+ goto fail0;
+ }
+ }
+
+ spin_lock_irq (&data->dev->lock);
+ if (data->dev->state == STATE_DEV_UNBOUND) {
+ value = -ENOENT;
+ goto gone;
+ } else if ((ep = data->ep) == NULL) {
+ value = -ENODEV;
+ goto gone;
+ }
+ switch (data->dev->gadget->speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ ep->desc = &data->desc;
+ break;
+ case USB_SPEED_HIGH:
+ /* fails if caller didn't provide that descriptor... */
+ ep->desc = &data->hs_desc;
+ break;
+ default:
+ DBG(data->dev, "unconnected, %s init abandoned\n",
+ data->name);
+ value = -EINVAL;
+ goto gone;
+ }
+ value = usb_ep_enable(ep);
+ if (value == 0) {
+ data->state = STATE_EP_ENABLED;
+ value = length;
+ }
+gone:
+ spin_unlock_irq (&data->dev->lock);
+ if (value < 0) {
+fail:
+ data->desc.bDescriptorType = 0;
+ data->hs_desc.bDescriptorType = 0;
+ }
+ return value;
+fail0:
+ value = -EINVAL;
+ goto fail;
+}
+
+static int
+ep_open (struct inode *inode, struct file *fd)
+{
+ struct ep_data *data = inode->i_private;
+ int value = -EBUSY;
+
+ if (mutex_lock_interruptible(&data->lock) != 0)
+ return -EINTR;
+ spin_lock_irq (&data->dev->lock);
+ if (data->dev->state == STATE_DEV_UNBOUND)
+ value = -ENOENT;
+ else if (data->state == STATE_EP_DISABLED) {
+ value = 0;
+ data->state = STATE_EP_READY;
+ get_ep (data);
+ fd->private_data = data;
+ VDEBUG (data->dev, "%s ready\n", data->name);
+ } else
+ DBG (data->dev, "%s state %d\n",
+ data->name, data->state);
+ spin_unlock_irq (&data->dev->lock);
+ mutex_unlock(&data->lock);
+ return value;
+}
+
+/*----------------------------------------------------------------------*/
+
+/* EP0 IMPLEMENTATION can be partly in userspace.
+ *
+ * Drivers that use this facility receive various events, including
+ * control requests the kernel doesn't handle. Drivers that don't
+ * use this facility may be too simple-minded for real applications.
+ */
+
+static inline void ep0_readable (struct dev_data *dev)
+{
+ wake_up (&dev->wait);
+ kill_fasync (&dev->fasync, SIGIO, POLL_IN);
+}
+
+static void clean_req (struct usb_ep *ep, struct usb_request *req)
+{
+ struct dev_data *dev = ep->driver_data;
+
+ if (req->buf != dev->rbuf) {
+ kfree(req->buf);
+ req->buf = dev->rbuf;
+ }
+ req->complete = epio_complete;
+ dev->setup_out_ready = 0;
+}
+
+static void ep0_complete (struct usb_ep *ep, struct usb_request *req)
+{
+ struct dev_data *dev = ep->driver_data;
+ unsigned long flags;
+ int free = 1;
+
+ /* for control OUT, data must still get to userspace */
+ spin_lock_irqsave(&dev->lock, flags);
+ if (!dev->setup_in) {
+ dev->setup_out_error = (req->status != 0);
+ if (!dev->setup_out_error)
+ free = 0;
+ dev->setup_out_ready = 1;
+ ep0_readable (dev);
+ }
+
+ /* clean up as appropriate */
+ if (free && req->buf != &dev->rbuf)
+ clean_req (ep, req);
+ req->complete = epio_complete;
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static int setup_req (struct usb_ep *ep, struct usb_request *req, u16 len)
+{
+ struct dev_data *dev = ep->driver_data;
+
+ if (dev->setup_out_ready) {
+ DBG (dev, "ep0 request busy!\n");
+ return -EBUSY;
+ }
+ if (len > sizeof (dev->rbuf))
+ req->buf = kmalloc(len, GFP_ATOMIC);
+ if (req->buf == NULL) {
+ req->buf = dev->rbuf;
+ return -ENOMEM;
+ }
+ req->complete = ep0_complete;
+ req->length = len;
+ req->zero = 0;
+ return 0;
+}
+
+static ssize_t
+ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
+{
+ struct dev_data *dev = fd->private_data;
+ ssize_t retval;
+ enum ep0_state state;
+
+ spin_lock_irq (&dev->lock);
+ if (dev->state <= STATE_DEV_OPENED) {
+ retval = -EINVAL;
+ goto done;
+ }
+
+ /* report fd mode change before acting on it */
+ if (dev->setup_abort) {
+ dev->setup_abort = 0;
+ retval = -EIDRM;
+ goto done;
+ }
+
+ /* control DATA stage */
+ if ((state = dev->state) == STATE_DEV_SETUP) {
+
+ if (dev->setup_in) { /* stall IN */
+ VDEBUG(dev, "ep0in stall\n");
+ (void) usb_ep_set_halt (dev->gadget->ep0);
+ retval = -EL2HLT;
+ dev->state = STATE_DEV_CONNECTED;
+
+ } else if (len == 0) { /* ack SET_CONFIGURATION etc */
+ struct usb_ep *ep = dev->gadget->ep0;
+ struct usb_request *req = dev->req;
+
+ if ((retval = setup_req (ep, req, 0)) == 0)
+ retval = usb_ep_queue (ep, req, GFP_ATOMIC);
+ dev->state = STATE_DEV_CONNECTED;
+
+ /* assume that was SET_CONFIGURATION */
+ if (dev->current_config) {
+ unsigned power;
+
+ if (gadget_is_dualspeed(dev->gadget)
+ && (dev->gadget->speed
+ == USB_SPEED_HIGH))
+ power = dev->hs_config->bMaxPower;
+ else
+ power = dev->config->bMaxPower;
+ usb_gadget_vbus_draw(dev->gadget, 2 * power);
+ }
+
+ } else { /* collect OUT data */
+ if ((fd->f_flags & O_NONBLOCK) != 0
+ && !dev->setup_out_ready) {
+ retval = -EAGAIN;
+ goto done;
+ }
+ spin_unlock_irq (&dev->lock);
+ retval = wait_event_interruptible (dev->wait,
+ dev->setup_out_ready != 0);
+
+ /* FIXME state could change from under us */
+ spin_lock_irq (&dev->lock);
+ if (retval)
+ goto done;
+
+ if (dev->state != STATE_DEV_SETUP) {
+ retval = -ECANCELED;
+ goto done;
+ }
+ dev->state = STATE_DEV_CONNECTED;
+
+ if (dev->setup_out_error)
+ retval = -EIO;
+ else {
+ len = min (len, (size_t)dev->req->actual);
+// FIXME don't call this with the spinlock held ...
+ if (copy_to_user (buf, dev->req->buf, len))
+ retval = -EFAULT;
+ else
+ retval = len;
+ clean_req (dev->gadget->ep0, dev->req);
+ /* NOTE userspace can't yet choose to stall */
+ }
+ }
+ goto done;
+ }
+
+ /* else normal: return event data */
+ if (len < sizeof dev->event [0]) {
+ retval = -EINVAL;
+ goto done;
+ }
+ len -= len % sizeof (struct usb_gadgetfs_event);
+ dev->usermode_setup = 1;
+
+scan:
+ /* return queued events right away */
+ if (dev->ev_next != 0) {
+ unsigned i, n;
+
+ n = len / sizeof (struct usb_gadgetfs_event);
+ if (dev->ev_next < n)
+ n = dev->ev_next;
+
+ /* ep0 i/o has special semantics during STATE_DEV_SETUP */
+ for (i = 0; i < n; i++) {
+ if (dev->event [i].type == GADGETFS_SETUP) {
+ dev->state = STATE_DEV_SETUP;
+ n = i + 1;
+ break;
+ }
+ }
+ spin_unlock_irq (&dev->lock);
+ len = n * sizeof (struct usb_gadgetfs_event);
+ if (copy_to_user (buf, &dev->event, len))
+ retval = -EFAULT;
+ else
+ retval = len;
+ if (len > 0) {
+ /* NOTE this doesn't guard against broken drivers;
+ * concurrent ep0 readers may lose events.
+ */
+ spin_lock_irq (&dev->lock);
+ if (dev->ev_next > n) {
+ memmove(&dev->event[0], &dev->event[n],
+ sizeof (struct usb_gadgetfs_event)
+ * (dev->ev_next - n));
+ }
+ dev->ev_next -= n;
+ spin_unlock_irq (&dev->lock);
+ }
+ return retval;
+ }
+ if (fd->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ goto done;
+ }
+
+ switch (state) {
+ default:
+ DBG (dev, "fail %s, state %d\n", __func__, state);
+ retval = -ESRCH;
+ break;
+ case STATE_DEV_UNCONNECTED:
+ case STATE_DEV_CONNECTED:
+ spin_unlock_irq (&dev->lock);
+ DBG (dev, "%s wait\n", __func__);
+
+ /* wait for events */
+ retval = wait_event_interruptible (dev->wait,
+ dev->ev_next != 0);
+ if (retval < 0)
+ return retval;
+ spin_lock_irq (&dev->lock);
+ goto scan;
+ }
+
+done:
+ spin_unlock_irq (&dev->lock);
+ return retval;
+}
+
+static struct usb_gadgetfs_event *
+next_event (struct dev_data *dev, enum usb_gadgetfs_event_type type)
+{
+ struct usb_gadgetfs_event *event;
+ unsigned i;
+
+ switch (type) {
+ /* these events purge the queue */
+ case GADGETFS_DISCONNECT:
+ if (dev->state == STATE_DEV_SETUP)
+ dev->setup_abort = 1;
+ // FALL THROUGH
+ case GADGETFS_CONNECT:
+ dev->ev_next = 0;
+ break;
+ case GADGETFS_SETUP: /* previous request timed out */
+ case GADGETFS_SUSPEND: /* same effect */
+ /* these events can't be repeated */
+ for (i = 0; i != dev->ev_next; i++) {
+ if (dev->event [i].type != type)
+ continue;
+ DBG(dev, "discard old event[%d] %d\n", i, type);
+ dev->ev_next--;
+ if (i == dev->ev_next)
+ break;
+ /* indices start at zero, for simplicity */
+ memmove (&dev->event [i], &dev->event [i + 1],
+ sizeof (struct usb_gadgetfs_event)
+ * (dev->ev_next - i));
+ }
+ break;
+ default:
+ BUG ();
+ }
+ VDEBUG(dev, "event[%d] = %d\n", dev->ev_next, type);
+ event = &dev->event [dev->ev_next++];
+ BUG_ON (dev->ev_next > N_EVENT);
+ memset (event, 0, sizeof *event);
+ event->type = type;
+ return event;
+}
+
+static ssize_t
+ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
+{
+ struct dev_data *dev = fd->private_data;
+ ssize_t retval = -ESRCH;
+
+ /* report fd mode change before acting on it */
+ if (dev->setup_abort) {
+ dev->setup_abort = 0;
+ retval = -EIDRM;
+
+ /* data and/or status stage for control request */
+ } else if (dev->state == STATE_DEV_SETUP) {
+
+ /* IN DATA+STATUS caller makes len <= wLength */
+ if (dev->setup_in) {
+ retval = setup_req (dev->gadget->ep0, dev->req, len);
+ if (retval == 0) {
+ dev->state = STATE_DEV_CONNECTED;
+ spin_unlock_irq (&dev->lock);
+ if (copy_from_user (dev->req->buf, buf, len))
+ retval = -EFAULT;
+ else {
+ if (len < dev->setup_wLength)
+ dev->req->zero = 1;
+ retval = usb_ep_queue (
+ dev->gadget->ep0, dev->req,
+ GFP_KERNEL);
+ }
+ if (retval < 0) {
+ spin_lock_irq (&dev->lock);
+ clean_req (dev->gadget->ep0, dev->req);
+ spin_unlock_irq (&dev->lock);
+ } else
+ retval = len;
+
+ return retval;
+ }
+
+ /* can stall some OUT transfers */
+ } else if (dev->setup_can_stall) {
+ VDEBUG(dev, "ep0out stall\n");
+ (void) usb_ep_set_halt (dev->gadget->ep0);
+ retval = -EL2HLT;
+ dev->state = STATE_DEV_CONNECTED;
+ } else {
+ DBG(dev, "bogus ep0out stall!\n");
+ }
+ } else
+ DBG (dev, "fail %s, state %d\n", __func__, dev->state);
+
+ return retval;
+}
+
+static int
+ep0_fasync (int f, struct file *fd, int on)
+{
+ struct dev_data *dev = fd->private_data;
+ // caller must F_SETOWN before signal delivery happens
+ VDEBUG (dev, "%s %s\n", __func__, on ? "on" : "off");
+ return fasync_helper (f, fd, on, &dev->fasync);
+}
+
+static struct usb_gadget_driver gadgetfs_driver;
+
+static int
+dev_release (struct inode *inode, struct file *fd)
+{
+ struct dev_data *dev = fd->private_data;
+
+ /* closing ep0 === shutdown all */
+
+ usb_gadget_unregister_driver (&gadgetfs_driver);
+
+ /* at this point "good" hardware has disconnected the
+ * device from USB; the host won't see it any more.
+ * alternatively, all host requests will time out.
+ */
+
+ kfree (dev->buf);
+ dev->buf = NULL;
+
+ /* other endpoints were all decoupled from this device */
+ spin_lock_irq(&dev->lock);
+ dev->state = STATE_DEV_DISABLED;
+ spin_unlock_irq(&dev->lock);
+
+ put_dev (dev);
+ return 0;
+}
+
+static unsigned int
+ep0_poll (struct file *fd, poll_table *wait)
+{
+ struct dev_data *dev = fd->private_data;
+ int mask = 0;
+
+ if (dev->state <= STATE_DEV_OPENED)
+ return DEFAULT_POLLMASK;
+
+ poll_wait(fd, &dev->wait, wait);
+
+ spin_lock_irq (&dev->lock);
+
+ /* report fd mode change before acting on it */
+ if (dev->setup_abort) {
+ dev->setup_abort = 0;
+ mask = POLLHUP;
+ goto out;
+ }
+
+ if (dev->state == STATE_DEV_SETUP) {
+ if (dev->setup_in || dev->setup_can_stall)
+ mask = POLLOUT;
+ } else {
+ if (dev->ev_next != 0)
+ mask = POLLIN;
+ }
+out:
+ spin_unlock_irq(&dev->lock);
+ return mask;
+}
+
+static long dev_ioctl (struct file *fd, unsigned code, unsigned long value)
+{
+ struct dev_data *dev = fd->private_data;
+ struct usb_gadget *gadget = dev->gadget;
+ long ret = -ENOTTY;
+
+ if (gadget->ops->ioctl)
+ ret = gadget->ops->ioctl (gadget, code, value);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------*/
+
+/* The in-kernel gadget driver handles most ep0 issues, in particular
+ * enumerating the single configuration (as provided from user space).
+ *
+ * Unrecognized ep0 requests may be handled in user space.
+ */
+
+static void make_qualifier (struct dev_data *dev)
+{
+ struct usb_qualifier_descriptor qual;
+ struct usb_device_descriptor *desc;
+
+ qual.bLength = sizeof qual;
+ qual.bDescriptorType = USB_DT_DEVICE_QUALIFIER;
+ qual.bcdUSB = cpu_to_le16 (0x0200);
+
+ desc = dev->dev;
+ qual.bDeviceClass = desc->bDeviceClass;
+ qual.bDeviceSubClass = desc->bDeviceSubClass;
+ qual.bDeviceProtocol = desc->bDeviceProtocol;
+
+ /* assumes ep0 uses the same value for both speeds ... */
+ qual.bMaxPacketSize0 = dev->gadget->ep0->maxpacket;
+
+ qual.bNumConfigurations = 1;
+ qual.bRESERVED = 0;
+
+ memcpy (dev->rbuf, &qual, sizeof qual);
+}
+
+static int
+config_buf (struct dev_data *dev, u8 type, unsigned index)
+{
+ int len;
+ int hs = 0;
+
+ /* only one configuration */
+ if (index > 0)
+ return -EINVAL;
+
+ if (gadget_is_dualspeed(dev->gadget)) {
+ hs = (dev->gadget->speed == USB_SPEED_HIGH);
+ if (type == USB_DT_OTHER_SPEED_CONFIG)
+ hs = !hs;
+ }
+ if (hs) {
+ dev->req->buf = dev->hs_config;
+ len = le16_to_cpu(dev->hs_config->wTotalLength);
+ } else {
+ dev->req->buf = dev->config;
+ len = le16_to_cpu(dev->config->wTotalLength);
+ }
+ ((u8 *)dev->req->buf) [1] = type;
+ return len;
+}
+
+static int
+gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
+{
+ struct dev_data *dev = get_gadget_data (gadget);
+ struct usb_request *req = dev->req;
+ int value = -EOPNOTSUPP;
+ struct usb_gadgetfs_event *event;
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+
+ spin_lock (&dev->lock);
+ dev->setup_abort = 0;
+ if (dev->state == STATE_DEV_UNCONNECTED) {
+ if (gadget_is_dualspeed(gadget)
+ && gadget->speed == USB_SPEED_HIGH
+ && dev->hs_config == NULL) {
+ spin_unlock(&dev->lock);
+ ERROR (dev, "no high speed config??\n");
+ return -EINVAL;
+ }
+
+ dev->state = STATE_DEV_CONNECTED;
+
+ INFO (dev, "connected\n");
+ event = next_event (dev, GADGETFS_CONNECT);
+ event->u.speed = gadget->speed;
+ ep0_readable (dev);
+
+ /* host may have given up waiting for response. we can miss control
+ * requests handled lower down (device/endpoint status and features);
+ * then ep0_{read,write} will report the wrong status. controller
+ * driver will have aborted pending i/o.
+ */
+ } else if (dev->state == STATE_DEV_SETUP)
+ dev->setup_abort = 1;
+
+ req->buf = dev->rbuf;
+ req->context = NULL;
+ value = -EOPNOTSUPP;
+ switch (ctrl->bRequest) {
+
+ case USB_REQ_GET_DESCRIPTOR:
+ if (ctrl->bRequestType != USB_DIR_IN)
+ goto unrecognized;
+ switch (w_value >> 8) {
+
+ case USB_DT_DEVICE:
+ value = min (w_length, (u16) sizeof *dev->dev);
+ dev->dev->bMaxPacketSize0 = dev->gadget->ep0->maxpacket;
+ req->buf = dev->dev;
+ break;
+ case USB_DT_DEVICE_QUALIFIER:
+ if (!dev->hs_config)
+ break;
+ value = min (w_length, (u16)
+ sizeof (struct usb_qualifier_descriptor));
+ make_qualifier (dev);
+ break;
+ case USB_DT_OTHER_SPEED_CONFIG:
+ // FALLTHROUGH
+ case USB_DT_CONFIG:
+ value = config_buf (dev,
+ w_value >> 8,
+ w_value & 0xff);
+ if (value >= 0)
+ value = min (w_length, (u16) value);
+ break;
+ case USB_DT_STRING:
+ goto unrecognized;
+
+ default: // all others are errors
+ break;
+ }
+ break;
+
+ /* currently one config, two speeds */
+ case USB_REQ_SET_CONFIGURATION:
+ if (ctrl->bRequestType != 0)
+ goto unrecognized;
+ if (0 == (u8) w_value) {
+ value = 0;
+ dev->current_config = 0;
+ usb_gadget_vbus_draw(gadget, 8 /* mA */ );
+ // user mode expected to disable endpoints
+ } else {
+ u8 config, power;
+
+ if (gadget_is_dualspeed(gadget)
+ && gadget->speed == USB_SPEED_HIGH) {
+ config = dev->hs_config->bConfigurationValue;
+ power = dev->hs_config->bMaxPower;
+ } else {
+ config = dev->config->bConfigurationValue;
+ power = dev->config->bMaxPower;
+ }
+
+ if (config == (u8) w_value) {
+ value = 0;
+ dev->current_config = config;
+ usb_gadget_vbus_draw(gadget, 2 * power);
+ }
+ }
+
+ /* report SET_CONFIGURATION like any other control request,
+ * except that usermode may not stall this. the next
+ * request mustn't be allowed start until this finishes:
+ * endpoints and threads set up, etc.
+ *
+ * NOTE: older PXA hardware (before PXA 255: without UDCCFR)
+ * has bad/racey automagic that prevents synchronizing here.
+ * even kernel mode drivers often miss them.
+ */
+ if (value == 0) {
+ INFO (dev, "configuration #%d\n", dev->current_config);
+ usb_gadget_set_state(gadget, USB_STATE_CONFIGURED);
+ if (dev->usermode_setup) {
+ dev->setup_can_stall = 0;
+ goto delegate;
+ }
+ }
+ break;
+
+#ifndef CONFIG_USB_PXA25X
+ /* PXA automagically handles this request too */
+ case USB_REQ_GET_CONFIGURATION:
+ if (ctrl->bRequestType != 0x80)
+ goto unrecognized;
+ *(u8 *)req->buf = dev->current_config;
+ value = min (w_length, (u16) 1);
+ break;
+#endif
+
+ default:
+unrecognized:
+ VDEBUG (dev, "%s req%02x.%02x v%04x i%04x l%d\n",
+ dev->usermode_setup ? "delegate" : "fail",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, le16_to_cpu(ctrl->wIndex), w_length);
+
+ /* if there's an ep0 reader, don't stall */
+ if (dev->usermode_setup) {
+ dev->setup_can_stall = 1;
+delegate:
+ dev->setup_in = (ctrl->bRequestType & USB_DIR_IN)
+ ? 1 : 0;
+ dev->setup_wLength = w_length;
+ dev->setup_out_ready = 0;
+ dev->setup_out_error = 0;
+ value = 0;
+
+ /* read DATA stage for OUT right away */
+ if (unlikely (!dev->setup_in && w_length)) {
+ value = setup_req (gadget->ep0, dev->req,
+ w_length);
+ if (value < 0)
+ break;
+ value = usb_ep_queue (gadget->ep0, dev->req,
+ GFP_ATOMIC);
+ if (value < 0) {
+ clean_req (gadget->ep0, dev->req);
+ break;
+ }
+
+ /* we can't currently stall these */
+ dev->setup_can_stall = 0;
+ }
+
+ /* state changes when reader collects event */
+ event = next_event (dev, GADGETFS_SETUP);
+ event->u.setup = *ctrl;
+ ep0_readable (dev);
+ spin_unlock (&dev->lock);
+ return 0;
+ }
+ }
+
+ /* proceed with data transfer and status phases? */
+ if (value >= 0 && dev->state != STATE_DEV_SETUP) {
+ req->length = value;
+ req->zero = value < w_length;
+ value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC);
+ if (value < 0) {
+ DBG (dev, "ep_queue --> %d\n", value);
+ req->status = 0;
+ }
+ }
+
+ /* device stalls when value < 0 */
+ spin_unlock (&dev->lock);
+ return value;
+}
+
+static void destroy_ep_files (struct dev_data *dev)
+{
+ DBG (dev, "%s %d\n", __func__, dev->state);
+
+ /* dev->state must prevent interference */
+ spin_lock_irq (&dev->lock);
+ while (!list_empty(&dev->epfiles)) {
+ struct ep_data *ep;
+ struct inode *parent;
+ struct dentry *dentry;
+
+ /* break link to FS */
+ ep = list_first_entry (&dev->epfiles, struct ep_data, epfiles);
+ list_del_init (&ep->epfiles);
+ dentry = ep->dentry;
+ ep->dentry = NULL;
+ parent = d_inode(dentry->d_parent);
+
+ /* break link to controller */
+ if (ep->state == STATE_EP_ENABLED)
+ (void) usb_ep_disable (ep->ep);
+ ep->state = STATE_EP_UNBOUND;
+ usb_ep_free_request (ep->ep, ep->req);
+ ep->ep = NULL;
+ wake_up (&ep->wait);
+ put_ep (ep);
+
+ spin_unlock_irq (&dev->lock);
+
+ /* break link to dcache */
+ mutex_lock (&parent->i_mutex);
+ d_delete (dentry);
+ dput (dentry);
+ mutex_unlock (&parent->i_mutex);
+
+ spin_lock_irq (&dev->lock);
+ }
+ spin_unlock_irq (&dev->lock);
+}
+
+
+static struct dentry *
+gadgetfs_create_file (struct super_block *sb, char const *name,
+ void *data, const struct file_operations *fops);
+
+static int activate_ep_files (struct dev_data *dev)
+{
+ struct usb_ep *ep;
+ struct ep_data *data;
+
+ gadget_for_each_ep (ep, dev->gadget) {
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ goto enomem0;
+ data->state = STATE_EP_DISABLED;
+ mutex_init(&data->lock);
+ init_waitqueue_head (&data->wait);
+
+ strncpy (data->name, ep->name, sizeof (data->name) - 1);
+ atomic_set (&data->count, 1);
+ data->dev = dev;
+ get_dev (dev);
+
+ data->ep = ep;
+ ep->driver_data = data;
+
+ data->req = usb_ep_alloc_request (ep, GFP_KERNEL);
+ if (!data->req)
+ goto enomem1;
+
+ data->dentry = gadgetfs_create_file (dev->sb, data->name,
+ data, &ep_io_operations);
+ if (!data->dentry)
+ goto enomem2;
+ list_add_tail (&data->epfiles, &dev->epfiles);
+ }
+ return 0;
+
+enomem2:
+ usb_ep_free_request (ep, data->req);
+enomem1:
+ put_dev (dev);
+ kfree (data);
+enomem0:
+ DBG (dev, "%s enomem\n", __func__);
+ destroy_ep_files (dev);
+ return -ENOMEM;
+}
+
+static void
+gadgetfs_unbind (struct usb_gadget *gadget)
+{
+ struct dev_data *dev = get_gadget_data (gadget);
+
+ DBG (dev, "%s\n", __func__);
+
+ spin_lock_irq (&dev->lock);
+ dev->state = STATE_DEV_UNBOUND;
+ spin_unlock_irq (&dev->lock);
+
+ destroy_ep_files (dev);
+ gadget->ep0->driver_data = NULL;
+ set_gadget_data (gadget, NULL);
+
+ /* we've already been disconnected ... no i/o is active */
+ if (dev->req)
+ usb_ep_free_request (gadget->ep0, dev->req);
+ DBG (dev, "%s done\n", __func__);
+ put_dev (dev);
+}
+
+static struct dev_data *the_device;
+
+static int gadgetfs_bind(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ struct dev_data *dev = the_device;
+
+ if (!dev)
+ return -ESRCH;
+ if (0 != strcmp (CHIP, gadget->name)) {
+ pr_err("%s expected %s controller not %s\n",
+ shortname, CHIP, gadget->name);
+ return -ENODEV;
+ }
+
+ set_gadget_data (gadget, dev);
+ dev->gadget = gadget;
+ gadget->ep0->driver_data = dev;
+
+ /* preallocate control response and buffer */
+ dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
+ if (!dev->req)
+ goto enomem;
+ dev->req->context = NULL;
+ dev->req->complete = epio_complete;
+
+ if (activate_ep_files (dev) < 0)
+ goto enomem;
+
+ INFO (dev, "bound to %s driver\n", gadget->name);
+ spin_lock_irq(&dev->lock);
+ dev->state = STATE_DEV_UNCONNECTED;
+ spin_unlock_irq(&dev->lock);
+ get_dev (dev);
+ return 0;
+
+enomem:
+ gadgetfs_unbind (gadget);
+ return -ENOMEM;
+}
+
+static void
+gadgetfs_disconnect (struct usb_gadget *gadget)
+{
+ struct dev_data *dev = get_gadget_data (gadget);
+ unsigned long flags;
+
+ spin_lock_irqsave (&dev->lock, flags);
+ if (dev->state == STATE_DEV_UNCONNECTED)
+ goto exit;
+ dev->state = STATE_DEV_UNCONNECTED;
+
+ INFO (dev, "disconnected\n");
+ next_event (dev, GADGETFS_DISCONNECT);
+ ep0_readable (dev);
+exit:
+ spin_unlock_irqrestore (&dev->lock, flags);
+}
+
+static void
+gadgetfs_suspend (struct usb_gadget *gadget)
+{
+ struct dev_data *dev = get_gadget_data (gadget);
+
+ INFO (dev, "suspended from state %d\n", dev->state);
+ spin_lock (&dev->lock);
+ switch (dev->state) {
+ case STATE_DEV_SETUP: // VERY odd... host died??
+ case STATE_DEV_CONNECTED:
+ case STATE_DEV_UNCONNECTED:
+ next_event (dev, GADGETFS_SUSPEND);
+ ep0_readable (dev);
+ /* FALLTHROUGH */
+ default:
+ break;
+ }
+ spin_unlock (&dev->lock);
+}
+
+static struct usb_gadget_driver gadgetfs_driver = {
+ .function = (char *) driver_desc,
+ .bind = gadgetfs_bind,
+ .unbind = gadgetfs_unbind,
+ .setup = gadgetfs_setup,
+ .reset = gadgetfs_disconnect,
+ .disconnect = gadgetfs_disconnect,
+ .suspend = gadgetfs_suspend,
+
+ .driver = {
+ .name = (char *) shortname,
+ },
+};
+
+/*----------------------------------------------------------------------*/
+
+static void gadgetfs_nop(struct usb_gadget *arg) { }
+
+static int gadgetfs_probe(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ CHIP = gadget->name;
+ return -EISNAM;
+}
+
+static struct usb_gadget_driver probe_driver = {
+ .max_speed = USB_SPEED_HIGH,
+ .bind = gadgetfs_probe,
+ .unbind = gadgetfs_nop,
+ .setup = (void *)gadgetfs_nop,
+ .disconnect = gadgetfs_nop,
+ .driver = {
+ .name = "nop",
+ },
+};
+
+
+/* DEVICE INITIALIZATION
+ *
+ * fd = open ("/dev/gadget/$CHIP", O_RDWR)
+ * status = write (fd, descriptors, sizeof descriptors)
+ *
+ * That write establishes the device configuration, so the kernel can
+ * bind to the controller ... guaranteeing it can handle enumeration
+ * at all necessary speeds. Descriptor order is:
+ *
+ * . message tag (u32, host order) ... for now, must be zero; it
+ * would change to support features like multi-config devices
+ * . full/low speed config ... all wTotalLength bytes (with interface,
+ * class, altsetting, endpoint, and other descriptors)
+ * . high speed config ... all descriptors, for high speed operation;
+ * this one's optional except for high-speed hardware
+ * . device descriptor
+ *
+ * Endpoints are not yet enabled. Drivers must wait until device
+ * configuration and interface altsetting changes create
+ * the need to configure (or unconfigure) them.
+ *
+ * After initialization, the device stays active for as long as that
+ * $CHIP file is open. Events must then be read from that descriptor,
+ * such as configuration notifications.
+ */
+
+static int is_valid_config (struct usb_config_descriptor *config)
+{
+ return config->bDescriptorType == USB_DT_CONFIG
+ && config->bLength == USB_DT_CONFIG_SIZE
+ && config->bConfigurationValue != 0
+ && (config->bmAttributes & USB_CONFIG_ATT_ONE) != 0
+ && (config->bmAttributes & USB_CONFIG_ATT_WAKEUP) == 0;
+ /* FIXME if gadget->is_otg, _must_ include an otg descriptor */
+ /* FIXME check lengths: walk to end */
+}
+
+static ssize_t
+dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
+{
+ struct dev_data *dev = fd->private_data;
+ ssize_t value = len, length = len;
+ unsigned total;
+ u32 tag;
+ char *kbuf;
+
+ spin_lock_irq(&dev->lock);
+ if (dev->state > STATE_DEV_OPENED) {
+ value = ep0_write(fd, buf, len, ptr);
+ spin_unlock_irq(&dev->lock);
+ return value;
+ }
+ spin_unlock_irq(&dev->lock);
+
+ if (len < (USB_DT_CONFIG_SIZE + USB_DT_DEVICE_SIZE + 4))
+ return -EINVAL;
+
+ /* we might need to change message format someday */
+ if (copy_from_user (&tag, buf, 4))
+ return -EFAULT;
+ if (tag != 0)
+ return -EINVAL;
+ buf += 4;
+ length -= 4;
+
+ kbuf = memdup_user(buf, length);
+ if (IS_ERR(kbuf))
+ return PTR_ERR(kbuf);
+
+ spin_lock_irq (&dev->lock);
+ value = -EINVAL;
+ if (dev->buf)
+ goto fail;
+ dev->buf = kbuf;
+
+ /* full or low speed config */
+ dev->config = (void *) kbuf;
+ total = le16_to_cpu(dev->config->wTotalLength);
+ if (!is_valid_config (dev->config) || total >= length)
+ goto fail;
+ kbuf += total;
+ length -= total;
+
+ /* optional high speed config */
+ if (kbuf [1] == USB_DT_CONFIG) {
+ dev->hs_config = (void *) kbuf;
+ total = le16_to_cpu(dev->hs_config->wTotalLength);
+ if (!is_valid_config (dev->hs_config) || total >= length)
+ goto fail;
+ kbuf += total;
+ length -= total;
+ }
+
+ /* could support multiple configs, using another encoding! */
+
+ /* device descriptor (tweaked for paranoia) */
+ if (length != USB_DT_DEVICE_SIZE)
+ goto fail;
+ dev->dev = (void *)kbuf;
+ if (dev->dev->bLength != USB_DT_DEVICE_SIZE
+ || dev->dev->bDescriptorType != USB_DT_DEVICE
+ || dev->dev->bNumConfigurations != 1)
+ goto fail;
+ dev->dev->bNumConfigurations = 1;
+ dev->dev->bcdUSB = cpu_to_le16 (0x0200);
+
+ /* triggers gadgetfs_bind(); then we can enumerate. */
+ spin_unlock_irq (&dev->lock);
+ if (dev->hs_config)
+ gadgetfs_driver.max_speed = USB_SPEED_HIGH;
+ else
+ gadgetfs_driver.max_speed = USB_SPEED_FULL;
+
+ value = usb_gadget_probe_driver(&gadgetfs_driver);
+ if (value != 0) {
+ kfree (dev->buf);
+ dev->buf = NULL;
+ } else {
+ /* at this point "good" hardware has for the first time
+ * let the USB the host see us. alternatively, if users
+ * unplug/replug that will clear all the error state.
+ *
+ * note: everything running before here was guaranteed
+ * to choke driver model style diagnostics. from here
+ * on, they can work ... except in cleanup paths that
+ * kick in after the ep0 descriptor is closed.
+ */
+ value = len;
+ }
+ return value;
+
+fail:
+ spin_unlock_irq (&dev->lock);
+ pr_debug ("%s: %s fail %Zd, %p\n", shortname, __func__, value, dev);
+ kfree (dev->buf);
+ dev->buf = NULL;
+ return value;
+}
+
+static int
+dev_open (struct inode *inode, struct file *fd)
+{
+ struct dev_data *dev = inode->i_private;
+ int value = -EBUSY;
+
+ spin_lock_irq(&dev->lock);
+ if (dev->state == STATE_DEV_DISABLED) {
+ dev->ev_next = 0;
+ dev->state = STATE_DEV_OPENED;
+ fd->private_data = dev;
+ get_dev (dev);
+ value = 0;
+ }
+ spin_unlock_irq(&dev->lock);
+ return value;
+}
+
+static const struct file_operations ep0_operations = {
+ .llseek = no_llseek,
+
+ .open = dev_open,
+ .read = ep0_read,
+ .write = dev_config,
+ .fasync = ep0_fasync,
+ .poll = ep0_poll,
+ .unlocked_ioctl = dev_ioctl,
+ .release = dev_release,
+};
+
+/*----------------------------------------------------------------------*/
+
+/* FILESYSTEM AND SUPERBLOCK OPERATIONS
+ *
+ * Mounting the filesystem creates a controller file, used first for
+ * device configuration then later for event monitoring.
+ */
+
+
+/* FIXME PAM etc could set this security policy without mount options
+ * if epfiles inherited ownership and permissons from ep0 ...
+ */
+
+static unsigned default_uid;
+static unsigned default_gid;
+static unsigned default_perm = S_IRUSR | S_IWUSR;
+
+module_param (default_uid, uint, 0644);
+module_param (default_gid, uint, 0644);
+module_param (default_perm, uint, 0644);
+
+
+static struct inode *
+gadgetfs_make_inode (struct super_block *sb,
+ void *data, const struct file_operations *fops,
+ int mode)
+{
+ struct inode *inode = new_inode (sb);
+
+ if (inode) {
+ inode->i_ino = get_next_ino();
+ inode->i_mode = mode;
+ inode->i_uid = make_kuid(&init_user_ns, default_uid);
+ inode->i_gid = make_kgid(&init_user_ns, default_gid);
+ inode->i_atime = inode->i_mtime = inode->i_ctime
+ = CURRENT_TIME;
+ inode->i_private = data;
+ inode->i_fop = fops;
+ }
+ return inode;
+}
+
+/* creates in fs root directory, so non-renamable and non-linkable.
+ * so inode and dentry are paired, until device reconfig.
+ */
+static struct dentry *
+gadgetfs_create_file (struct super_block *sb, char const *name,
+ void *data, const struct file_operations *fops)
+{
+ struct dentry *dentry;
+ struct inode *inode;
+
+ dentry = d_alloc_name(sb->s_root, name);
+ if (!dentry)
+ return NULL;
+
+ inode = gadgetfs_make_inode (sb, data, fops,
+ S_IFREG | (default_perm & S_IRWXUGO));
+ if (!inode) {
+ dput(dentry);
+ return NULL;
+ }
+ d_add (dentry, inode);
+ return dentry;
+}
+
+static const struct super_operations gadget_fs_operations = {
+ .statfs = simple_statfs,
+ .drop_inode = generic_delete_inode,
+};
+
+static int
+gadgetfs_fill_super (struct super_block *sb, void *opts, int silent)
+{
+ struct inode *inode;
+ struct dev_data *dev;
+
+ if (the_device)
+ return -ESRCH;
+
+ /* fake probe to determine $CHIP */
+ CHIP = NULL;
+ usb_gadget_probe_driver(&probe_driver);
+ if (!CHIP)
+ return -ENODEV;
+
+ /* superblock */
+ sb->s_blocksize = PAGE_CACHE_SIZE;
+ sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+ sb->s_magic = GADGETFS_MAGIC;
+ sb->s_op = &gadget_fs_operations;
+ sb->s_time_gran = 1;
+
+ /* root inode */
+ inode = gadgetfs_make_inode (sb,
+ NULL, &simple_dir_operations,
+ S_IFDIR | S_IRUGO | S_IXUGO);
+ if (!inode)
+ goto Enomem;
+ inode->i_op = &simple_dir_inode_operations;
+ if (!(sb->s_root = d_make_root (inode)))
+ goto Enomem;
+
+ /* the ep0 file is named after the controller we expect;
+ * user mode code can use it for sanity checks, like we do.
+ */
+ dev = dev_new ();
+ if (!dev)
+ goto Enomem;
+
+ dev->sb = sb;
+ dev->dentry = gadgetfs_create_file(sb, CHIP, dev, &ep0_operations);
+ if (!dev->dentry) {
+ put_dev(dev);
+ goto Enomem;
+ }
+
+ /* other endpoint files are available after hardware setup,
+ * from binding to a controller.
+ */
+ the_device = dev;
+ return 0;
+
+Enomem:
+ return -ENOMEM;
+}
+
+/* "mount -t gadgetfs path /dev/gadget" ends up here */
+static struct dentry *
+gadgetfs_mount (struct file_system_type *t, int flags,
+ const char *path, void *opts)
+{
+ return mount_single (t, flags, opts, gadgetfs_fill_super);
+}
+
+static void
+gadgetfs_kill_sb (struct super_block *sb)
+{
+ kill_litter_super (sb);
+ if (the_device) {
+ put_dev (the_device);
+ the_device = NULL;
+ }
+}
+
+/*----------------------------------------------------------------------*/
+
+static struct file_system_type gadgetfs_type = {
+ .owner = THIS_MODULE,
+ .name = shortname,
+ .mount = gadgetfs_mount,
+ .kill_sb = gadgetfs_kill_sb,
+};
+MODULE_ALIAS_FS("gadgetfs");
+
+/*----------------------------------------------------------------------*/
+
+static int __init init (void)
+{
+ int status;
+
+ status = register_filesystem (&gadgetfs_type);
+ if (status == 0)
+ pr_info ("%s: %s, version " DRIVER_VERSION "\n",
+ shortname, driver_desc);
+ return status;
+}
+module_init (init);
+
+static void __exit cleanup (void)
+{
+ pr_debug ("unregister %s\n", shortname);
+ unregister_filesystem (&gadgetfs_type);
+}
+module_exit (cleanup);
+
diff --git a/drivers/usb/gadget/legacy/mass_storage.c b/drivers/usb/gadget/legacy/mass_storage.c
new file mode 100644
index 000000000..e7bfb081f
--- /dev/null
+++ b/drivers/usb/gadget/legacy/mass_storage.c
@@ -0,0 +1,276 @@
+/*
+ * mass_storage.c -- Mass Storage USB Gadget
+ *
+ * Copyright (C) 2003-2008 Alan Stern
+ * Copyright (C) 2009 Samsung Electronics
+ * Author: Michal Nazarewicz <mina86@mina86.com>
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+
+/*
+ * The Mass Storage Gadget 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 gadget
+ * driver for a USB device, it also illustrates a technique of
+ * double-buffering for increased throughput. Last but not least, it
+ * gives an easy way to probe the behavior of the Mass Storage drivers
+ * in a USB host.
+ *
+ * Since this file serves only administrative purposes and all the
+ * business logic is implemented in f_mass_storage.* file. Read
+ * comments in this file for more detailed description.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/usb/ch9.h>
+#include <linux/module.h>
+
+/*-------------------------------------------------------------------------*/
+
+#define DRIVER_DESC "Mass Storage Gadget"
+#define DRIVER_VERSION "2009/09/11"
+
+/*
+ * Thanks to NetChip Technologies for donating this product ID.
+ *
+ * DO NOT REUSE THESE IDs with any other driver!! Ever!!
+ * Instead: allocate your own, using normal USB-IF procedures.
+ */
+#define FSG_VENDOR_ID 0x0525 /* NetChip */
+#define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */
+
+#include "f_mass_storage.h"
+
+/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
+
+static struct usb_device_descriptor msg_device_desc = {
+ .bLength = sizeof msg_device_desc,
+ .bDescriptorType = USB_DT_DEVICE,
+
+ .bcdUSB = cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_PER_INTERFACE,
+
+ /* Vendor and product id can be overridden by module parameters. */
+ .idVendor = cpu_to_le16(FSG_VENDOR_ID),
+ .idProduct = cpu_to_le16(FSG_PRODUCT_ID),
+ .bNumConfigurations = 1,
+};
+
+static struct usb_otg_descriptor otg_descriptor = {
+ .bLength = sizeof otg_descriptor,
+ .bDescriptorType = USB_DT_OTG,
+
+ /*
+ * REVISIT SRP-only hardware is possible, although
+ * it would not be called "OTG" ...
+ */
+ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
+};
+
+static const struct usb_descriptor_header *otg_desc[] = {
+ (struct usb_descriptor_header *) &otg_descriptor,
+ NULL,
+};
+
+static struct usb_string strings_dev[] = {
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+ .language = 0x0409, /* en-us */
+ .strings = strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+ &stringtab_dev,
+ NULL,
+};
+
+static struct usb_function_instance *fi_msg;
+static struct usb_function *f_msg;
+
+/****************************** Configurations ******************************/
+
+static struct fsg_module_parameters mod_data = {
+ .stall = 1
+};
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
+
+#else
+
+/*
+ * Number of buffers we will use.
+ * 2 is usually enough for good buffering pipeline
+ */
+#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
+
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
+
+FSG_MODULE_PARAMETERS(/* no prefix */, mod_data);
+
+static unsigned long msg_registered;
+static void msg_cleanup(void);
+
+static int msg_thread_exits(struct fsg_common *common)
+{
+ msg_cleanup();
+ return 0;
+}
+
+static int msg_do_config(struct usb_configuration *c)
+{
+ struct fsg_opts *opts;
+ int ret;
+
+ if (gadget_is_otg(c->cdev->gadget)) {
+ c->descriptors = otg_desc;
+ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
+ opts = fsg_opts_from_func_inst(fi_msg);
+
+ f_msg = usb_get_function(fi_msg);
+ if (IS_ERR(f_msg))
+ return PTR_ERR(f_msg);
+
+ ret = fsg_common_run_thread(opts->common);
+ if (ret)
+ goto put_func;
+
+ ret = usb_add_function(c, f_msg);
+ if (ret)
+ goto put_func;
+
+ return 0;
+
+put_func:
+ usb_put_function(f_msg);
+ return ret;
+}
+
+static struct usb_configuration msg_config_driver = {
+ .label = "Linux File-Backed Storage",
+ .bConfigurationValue = 1,
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+};
+
+
+/****************************** Gadget Bind ******************************/
+
+static int msg_bind(struct usb_composite_dev *cdev)
+{
+ static const struct fsg_operations ops = {
+ .thread_exits = msg_thread_exits,
+ };
+ struct fsg_opts *opts;
+ struct fsg_config config;
+ int status;
+
+ fi_msg = usb_get_function_instance("mass_storage");
+ if (IS_ERR(fi_msg))
+ return PTR_ERR(fi_msg);
+
+ fsg_config_from_params(&config, &mod_data, fsg_num_buffers);
+ opts = fsg_opts_from_func_inst(fi_msg);
+
+ opts->no_configfs = true;
+ status = fsg_common_set_num_buffers(opts->common, fsg_num_buffers);
+ if (status)
+ goto fail;
+
+ status = fsg_common_set_nluns(opts->common, config.nluns);
+ if (status)
+ goto fail_set_nluns;
+
+ fsg_common_set_ops(opts->common, &ops);
+
+ status = fsg_common_set_cdev(opts->common, cdev, config.can_stall);
+ if (status)
+ goto fail_set_cdev;
+
+ fsg_common_set_sysfs(opts->common, true);
+ status = fsg_common_create_luns(opts->common, &config);
+ if (status)
+ goto fail_set_cdev;
+
+ fsg_common_set_inquiry_string(opts->common, config.vendor_name,
+ config.product_name);
+
+ status = usb_string_ids_tab(cdev, strings_dev);
+ if (status < 0)
+ goto fail_string_ids;
+ msg_device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+
+ status = usb_add_config(cdev, &msg_config_driver, msg_do_config);
+ if (status < 0)
+ goto fail_string_ids;
+
+ usb_composite_overwrite_options(cdev, &coverwrite);
+ dev_info(&cdev->gadget->dev,
+ DRIVER_DESC ", version: " DRIVER_VERSION "\n");
+ set_bit(0, &msg_registered);
+ return 0;
+
+fail_string_ids:
+ fsg_common_remove_luns(opts->common);
+fail_set_cdev:
+ fsg_common_free_luns(opts->common);
+fail_set_nluns:
+ fsg_common_free_buffers(opts->common);
+fail:
+ usb_put_function_instance(fi_msg);
+ return status;
+}
+
+static int msg_unbind(struct usb_composite_dev *cdev)
+{
+ if (!IS_ERR(f_msg))
+ usb_put_function(f_msg);
+
+ if (!IS_ERR(fi_msg))
+ usb_put_function_instance(fi_msg);
+
+ return 0;
+}
+
+/****************************** Some noise ******************************/
+
+static struct usb_composite_driver msg_driver = {
+ .name = "g_mass_storage",
+ .dev = &msg_device_desc,
+ .max_speed = USB_SPEED_SUPER,
+ .needs_serial = 1,
+ .strings = dev_strings,
+ .bind = msg_bind,
+ .unbind = msg_unbind,
+};
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Michal Nazarewicz");
+MODULE_LICENSE("GPL");
+
+static int __init msg_init(void)
+{
+ return usb_composite_probe(&msg_driver);
+}
+module_init(msg_init);
+
+static void msg_cleanup(void)
+{
+ if (test_and_clear_bit(0, &msg_registered))
+ usb_composite_unregister(&msg_driver);
+}
+module_exit(msg_cleanup);
diff --git a/drivers/usb/gadget/legacy/multi.c b/drivers/usb/gadget/legacy/multi.c
new file mode 100644
index 000000000..b21b51f0c
--- /dev/null
+++ b/drivers/usb/gadget/legacy/multi.c
@@ -0,0 +1,510 @@
+/*
+ * multi.c -- Multifunction Composite driver
+ *
+ * Copyright (C) 2008 David Brownell
+ * 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.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+
+#include "u_serial.h"
+#if defined USB_ETH_RNDIS
+# undef USB_ETH_RNDIS
+#endif
+#ifdef CONFIG_USB_G_MULTI_RNDIS
+# define USB_ETH_RNDIS y
+#endif
+
+
+#define DRIVER_DESC "Multifunction Composite Gadget"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Michal Nazarewicz");
+MODULE_LICENSE("GPL");
+
+
+#include "f_mass_storage.h"
+
+#include "u_ecm.h"
+#ifdef USB_ETH_RNDIS
+# include "u_rndis.h"
+# include "rndis.h"
+#endif
+#include "u_ether.h"
+
+USB_GADGET_COMPOSITE_OPTIONS();
+
+USB_ETHERNET_MODULE_PARAMETERS();
+
+/***************************** Device Descriptor ****************************/
+
+#define MULTI_VENDOR_NUM 0x1d6b /* Linux Foundation */
+#define MULTI_PRODUCT_NUM 0x0104 /* Multifunction Composite Gadget */
+
+
+enum {
+ __MULTI_NO_CONFIG,
+#ifdef CONFIG_USB_G_MULTI_RNDIS
+ MULTI_RNDIS_CONFIG_NUM,
+#endif
+#ifdef CONFIG_USB_G_MULTI_CDC
+ MULTI_CDC_CONFIG_NUM,
+#endif
+};
+
+
+static struct usb_device_descriptor device_desc = {
+ .bLength = sizeof device_desc,
+ .bDescriptorType = USB_DT_DEVICE,
+
+ .bcdUSB = cpu_to_le16(0x0200),
+
+ .bDeviceClass = USB_CLASS_MISC /* 0xEF */,
+ .bDeviceSubClass = 2,
+ .bDeviceProtocol = 1,
+
+ /* Vendor and product id can be overridden by module parameters. */
+ .idVendor = cpu_to_le16(MULTI_VENDOR_NUM),
+ .idProduct = cpu_to_le16(MULTI_PRODUCT_NUM),
+};
+
+
+static const struct usb_descriptor_header *otg_desc[] = {
+ (struct usb_descriptor_header *) &(struct usb_otg_descriptor){
+ .bLength = sizeof(struct usb_otg_descriptor),
+ .bDescriptorType = USB_DT_OTG,
+
+ /*
+ * REVISIT SRP-only hardware is possible, although
+ * it would not be called "OTG" ...
+ */
+ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
+ },
+ NULL,
+};
+
+
+enum {
+ MULTI_STRING_RNDIS_CONFIG_IDX = USB_GADGET_FIRST_AVAIL_IDX,
+ MULTI_STRING_CDC_CONFIG_IDX,
+};
+
+static struct usb_string strings_dev[] = {
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
+ [MULTI_STRING_RNDIS_CONFIG_IDX].s = "Multifunction with RNDIS",
+ [MULTI_STRING_CDC_CONFIG_IDX].s = "Multifunction with CDC ECM",
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+ &(struct usb_gadget_strings){
+ .language = 0x0409, /* en-us */
+ .strings = strings_dev,
+ },
+ NULL,
+};
+
+
+
+
+/****************************** Configurations ******************************/
+
+static struct fsg_module_parameters fsg_mod_data = { .stall = 1 };
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
+
+#else
+
+/*
+ * Number of buffers we will use.
+ * 2 is usually enough for good buffering pipeline
+ */
+#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
+
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
+
+FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
+
+static struct usb_function_instance *fi_acm;
+static struct usb_function_instance *fi_msg;
+
+/********** RNDIS **********/
+
+#ifdef USB_ETH_RNDIS
+static struct usb_function_instance *fi_rndis;
+static struct usb_function *f_acm_rndis;
+static struct usb_function *f_rndis;
+static struct usb_function *f_msg_rndis;
+
+static int rndis_do_config(struct usb_configuration *c)
+{
+ struct fsg_opts *fsg_opts;
+ int ret;
+
+ if (gadget_is_otg(c->cdev->gadget)) {
+ c->descriptors = otg_desc;
+ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
+ f_rndis = usb_get_function(fi_rndis);
+ if (IS_ERR(f_rndis))
+ return PTR_ERR(f_rndis);
+
+ ret = usb_add_function(c, f_rndis);
+ if (ret < 0)
+ goto err_func_rndis;
+
+ f_acm_rndis = usb_get_function(fi_acm);
+ if (IS_ERR(f_acm_rndis)) {
+ ret = PTR_ERR(f_acm_rndis);
+ goto err_func_acm;
+ }
+
+ ret = usb_add_function(c, f_acm_rndis);
+ if (ret)
+ goto err_conf;
+
+ f_msg_rndis = usb_get_function(fi_msg);
+ if (IS_ERR(f_msg_rndis)) {
+ ret = PTR_ERR(f_msg_rndis);
+ goto err_fsg;
+ }
+
+ fsg_opts = fsg_opts_from_func_inst(fi_msg);
+ ret = fsg_common_run_thread(fsg_opts->common);
+ if (ret)
+ goto err_run;
+
+ ret = usb_add_function(c, f_msg_rndis);
+ if (ret)
+ goto err_run;
+
+ return 0;
+err_run:
+ usb_put_function(f_msg_rndis);
+err_fsg:
+ usb_remove_function(c, f_acm_rndis);
+err_conf:
+ usb_put_function(f_acm_rndis);
+err_func_acm:
+ usb_remove_function(c, f_rndis);
+err_func_rndis:
+ usb_put_function(f_rndis);
+ return ret;
+}
+
+static __ref int rndis_config_register(struct usb_composite_dev *cdev)
+{
+ static struct usb_configuration config = {
+ .bConfigurationValue = MULTI_RNDIS_CONFIG_NUM,
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+ };
+
+ config.label = strings_dev[MULTI_STRING_RNDIS_CONFIG_IDX].s;
+ config.iConfiguration = strings_dev[MULTI_STRING_RNDIS_CONFIG_IDX].id;
+
+ return usb_add_config(cdev, &config, rndis_do_config);
+}
+
+#else
+
+static __ref int rndis_config_register(struct usb_composite_dev *cdev)
+{
+ return 0;
+}
+
+#endif
+
+
+/********** CDC ECM **********/
+
+#ifdef CONFIG_USB_G_MULTI_CDC
+static struct usb_function_instance *fi_ecm;
+static struct usb_function *f_acm_multi;
+static struct usb_function *f_ecm;
+static struct usb_function *f_msg_multi;
+
+static int cdc_do_config(struct usb_configuration *c)
+{
+ struct fsg_opts *fsg_opts;
+ int ret;
+
+ if (gadget_is_otg(c->cdev->gadget)) {
+ c->descriptors = otg_desc;
+ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
+ f_ecm = usb_get_function(fi_ecm);
+ if (IS_ERR(f_ecm))
+ return PTR_ERR(f_ecm);
+
+ ret = usb_add_function(c, f_ecm);
+ if (ret < 0)
+ goto err_func_ecm;
+
+ /* implicit port_num is zero */
+ f_acm_multi = usb_get_function(fi_acm);
+ if (IS_ERR(f_acm_multi)) {
+ ret = PTR_ERR(f_acm_multi);
+ goto err_func_acm;
+ }
+
+ ret = usb_add_function(c, f_acm_multi);
+ if (ret)
+ goto err_conf;
+
+ f_msg_multi = usb_get_function(fi_msg);
+ if (IS_ERR(f_msg_multi)) {
+ ret = PTR_ERR(f_msg_multi);
+ goto err_fsg;
+ }
+
+ fsg_opts = fsg_opts_from_func_inst(fi_msg);
+ ret = fsg_common_run_thread(fsg_opts->common);
+ if (ret)
+ goto err_run;
+
+ ret = usb_add_function(c, f_msg_multi);
+ if (ret)
+ goto err_run;
+
+ return 0;
+err_run:
+ usb_put_function(f_msg_multi);
+err_fsg:
+ usb_remove_function(c, f_acm_multi);
+err_conf:
+ usb_put_function(f_acm_multi);
+err_func_acm:
+ usb_remove_function(c, f_ecm);
+err_func_ecm:
+ usb_put_function(f_ecm);
+ return ret;
+}
+
+static __ref int cdc_config_register(struct usb_composite_dev *cdev)
+{
+ static struct usb_configuration config = {
+ .bConfigurationValue = MULTI_CDC_CONFIG_NUM,
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+ };
+
+ config.label = strings_dev[MULTI_STRING_CDC_CONFIG_IDX].s;
+ config.iConfiguration = strings_dev[MULTI_STRING_CDC_CONFIG_IDX].id;
+
+ return usb_add_config(cdev, &config, cdc_do_config);
+}
+
+#else
+
+static __ref int cdc_config_register(struct usb_composite_dev *cdev)
+{
+ return 0;
+}
+
+#endif
+
+
+
+/****************************** Gadget Bind ******************************/
+
+static int __ref multi_bind(struct usb_composite_dev *cdev)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+#ifdef CONFIG_USB_G_MULTI_CDC
+ struct f_ecm_opts *ecm_opts;
+#endif
+#ifdef USB_ETH_RNDIS
+ struct f_rndis_opts *rndis_opts;
+#endif
+ struct fsg_opts *fsg_opts;
+ struct fsg_config config;
+ int status;
+
+ if (!can_support_ecm(cdev->gadget)) {
+ dev_err(&gadget->dev, "controller '%s' not usable\n",
+ gadget->name);
+ return -EINVAL;
+ }
+
+#ifdef CONFIG_USB_G_MULTI_CDC
+ fi_ecm = usb_get_function_instance("ecm");
+ if (IS_ERR(fi_ecm))
+ return PTR_ERR(fi_ecm);
+
+ ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst);
+
+ gether_set_qmult(ecm_opts->net, qmult);
+ if (!gether_set_host_addr(ecm_opts->net, host_addr))
+ pr_info("using host ethernet address: %s", host_addr);
+ if (!gether_set_dev_addr(ecm_opts->net, dev_addr))
+ pr_info("using self ethernet address: %s", dev_addr);
+#endif
+
+#ifdef USB_ETH_RNDIS
+ fi_rndis = usb_get_function_instance("rndis");
+ if (IS_ERR(fi_rndis)) {
+ status = PTR_ERR(fi_rndis);
+ goto fail;
+ }
+
+ rndis_opts = container_of(fi_rndis, struct f_rndis_opts, func_inst);
+
+ gether_set_qmult(rndis_opts->net, qmult);
+ if (!gether_set_host_addr(rndis_opts->net, host_addr))
+ pr_info("using host ethernet address: %s", host_addr);
+ if (!gether_set_dev_addr(rndis_opts->net, dev_addr))
+ pr_info("using self ethernet address: %s", dev_addr);
+#endif
+
+#if (defined CONFIG_USB_G_MULTI_CDC && defined USB_ETH_RNDIS)
+ /*
+ * If both ecm and rndis are selected then:
+ * 1) rndis borrows the net interface from ecm
+ * 2) since the interface is shared it must not be bound
+ * twice - in ecm's _and_ rndis' binds, so do it here.
+ */
+ gether_set_gadget(ecm_opts->net, cdev->gadget);
+ status = gether_register_netdev(ecm_opts->net);
+ if (status)
+ goto fail0;
+
+ rndis_borrow_net(fi_rndis, ecm_opts->net);
+ ecm_opts->bound = true;
+#endif
+
+ /* set up serial link layer */
+ fi_acm = usb_get_function_instance("acm");
+ if (IS_ERR(fi_acm)) {
+ status = PTR_ERR(fi_acm);
+ goto fail0;
+ }
+
+ /* set up mass storage function */
+ fi_msg = usb_get_function_instance("mass_storage");
+ if (IS_ERR(fi_msg)) {
+ status = PTR_ERR(fi_msg);
+ goto fail1;
+ }
+ fsg_config_from_params(&config, &fsg_mod_data, fsg_num_buffers);
+ fsg_opts = fsg_opts_from_func_inst(fi_msg);
+
+ fsg_opts->no_configfs = true;
+ status = fsg_common_set_num_buffers(fsg_opts->common, fsg_num_buffers);
+ if (status)
+ goto fail2;
+
+ status = fsg_common_set_nluns(fsg_opts->common, config.nluns);
+ if (status)
+ goto fail_set_nluns;
+
+ status = fsg_common_set_cdev(fsg_opts->common, cdev, config.can_stall);
+ if (status)
+ goto fail_set_cdev;
+
+ fsg_common_set_sysfs(fsg_opts->common, true);
+ status = fsg_common_create_luns(fsg_opts->common, &config);
+ if (status)
+ goto fail_set_cdev;
+
+ fsg_common_set_inquiry_string(fsg_opts->common, config.vendor_name,
+ config.product_name);
+
+ /* allocate string IDs */
+ status = usb_string_ids_tab(cdev, strings_dev);
+ if (unlikely(status < 0))
+ goto fail_string_ids;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+
+ /* register configurations */
+ status = rndis_config_register(cdev);
+ if (unlikely(status < 0))
+ goto fail_string_ids;
+
+ status = cdc_config_register(cdev);
+ if (unlikely(status < 0))
+ goto fail_string_ids;
+ usb_composite_overwrite_options(cdev, &coverwrite);
+
+ /* we're done */
+ dev_info(&gadget->dev, DRIVER_DESC "\n");
+ return 0;
+
+
+ /* error recovery */
+fail_string_ids:
+ fsg_common_remove_luns(fsg_opts->common);
+fail_set_cdev:
+ fsg_common_free_luns(fsg_opts->common);
+fail_set_nluns:
+ fsg_common_free_buffers(fsg_opts->common);
+fail2:
+ usb_put_function_instance(fi_msg);
+fail1:
+ usb_put_function_instance(fi_acm);
+fail0:
+#ifdef USB_ETH_RNDIS
+ usb_put_function_instance(fi_rndis);
+fail:
+#endif
+#ifdef CONFIG_USB_G_MULTI_CDC
+ usb_put_function_instance(fi_ecm);
+#endif
+ return status;
+}
+
+static int multi_unbind(struct usb_composite_dev *cdev)
+{
+#ifdef CONFIG_USB_G_MULTI_CDC
+ usb_put_function(f_msg_multi);
+#endif
+#ifdef USB_ETH_RNDIS
+ usb_put_function(f_msg_rndis);
+#endif
+ usb_put_function_instance(fi_msg);
+#ifdef CONFIG_USB_G_MULTI_CDC
+ usb_put_function(f_acm_multi);
+#endif
+#ifdef USB_ETH_RNDIS
+ usb_put_function(f_acm_rndis);
+#endif
+ usb_put_function_instance(fi_acm);
+#ifdef USB_ETH_RNDIS
+ usb_put_function(f_rndis);
+ usb_put_function_instance(fi_rndis);
+#endif
+#ifdef CONFIG_USB_G_MULTI_CDC
+ usb_put_function(f_ecm);
+ usb_put_function_instance(fi_ecm);
+#endif
+ return 0;
+}
+
+
+/****************************** Some noise ******************************/
+
+
+static struct usb_composite_driver multi_driver = {
+ .name = "g_multi",
+ .dev = &device_desc,
+ .strings = dev_strings,
+ .max_speed = USB_SPEED_HIGH,
+ .bind = multi_bind,
+ .unbind = multi_unbind,
+ .needs_serial = 1,
+};
+
+module_usb_composite_driver(multi_driver);
diff --git a/drivers/usb/gadget/legacy/ncm.c b/drivers/usb/gadget/legacy/ncm.c
new file mode 100644
index 000000000..6ce742141
--- /dev/null
+++ b/drivers/usb/gadget/legacy/ncm.c
@@ -0,0 +1,211 @@
+/*
+ * ncm.c -- NCM gadget driver
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Contact: Yauheni Kaliuta <yauheni.kaliuta@nokia.com>
+ *
+ * The driver borrows from ether.c which is:
+ *
+ * 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 DEBUG */
+/* #define VERBOSE_DEBUG */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb/composite.h>
+
+#include "u_ether.h"
+#include "u_ncm.h"
+
+#define DRIVER_DESC "NCM Gadget"
+
+/*-------------------------------------------------------------------------*/
+
+/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
+ * Instead: allocate your own, using normal USB-IF procedures.
+ */
+
+/* Thanks to NetChip Technologies for donating this product ID.
+ * It's for devices with only CDC Ethernet configurations.
+ */
+#define CDC_VENDOR_NUM 0x0525 /* NetChip */
+#define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */
+
+/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
+
+USB_ETHERNET_MODULE_PARAMETERS();
+
+static struct usb_device_descriptor device_desc = {
+ .bLength = sizeof device_desc,
+ .bDescriptorType = USB_DT_DEVICE,
+
+ .bcdUSB = cpu_to_le16 (0x0200),
+
+ .bDeviceClass = USB_CLASS_COMM,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+ /* .bMaxPacketSize0 = f(hardware) */
+
+ /* Vendor and product id defaults change according to what configs
+ * we support. (As does bNumConfigurations.) These values can
+ * also be overridden by module parameters.
+ */
+ .idVendor = cpu_to_le16 (CDC_VENDOR_NUM),
+ .idProduct = cpu_to_le16 (CDC_PRODUCT_NUM),
+ /* .bcdDevice = f(hardware) */
+ /* .iManufacturer = DYNAMIC */
+ /* .iProduct = DYNAMIC */
+ /* NO SERIAL NUMBER */
+ .bNumConfigurations = 1,
+};
+
+static struct usb_otg_descriptor otg_descriptor = {
+ .bLength = sizeof otg_descriptor,
+ .bDescriptorType = USB_DT_OTG,
+
+ /* REVISIT SRP-only hardware is possible, although
+ * it would not be called "OTG" ...
+ */
+ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
+};
+
+static const struct usb_descriptor_header *otg_desc[] = {
+ (struct usb_descriptor_header *) &otg_descriptor,
+ NULL,
+};
+
+/* string IDs are assigned dynamically */
+static struct usb_string strings_dev[] = {
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+ .language = 0x0409, /* en-us */
+ .strings = strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+ &stringtab_dev,
+ NULL,
+};
+
+static struct usb_function_instance *f_ncm_inst;
+static struct usb_function *f_ncm;
+
+/*-------------------------------------------------------------------------*/
+
+static int ncm_do_config(struct usb_configuration *c)
+{
+ int status;
+
+ /* FIXME alloc iConfiguration string, set it in c->strings */
+
+ if (gadget_is_otg(c->cdev->gadget)) {
+ c->descriptors = otg_desc;
+ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
+ f_ncm = usb_get_function(f_ncm_inst);
+ if (IS_ERR(f_ncm)) {
+ status = PTR_ERR(f_ncm);
+ return status;
+ }
+
+ status = usb_add_function(c, f_ncm);
+ if (status < 0) {
+ usb_put_function(f_ncm);
+ return status;
+ }
+
+ return 0;
+}
+
+static struct usb_configuration ncm_config_driver = {
+ /* .label = f(hardware) */
+ .label = "CDC Ethernet (NCM)",
+ .bConfigurationValue = 1,
+ /* .iConfiguration = DYNAMIC */
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int gncm_bind(struct usb_composite_dev *cdev)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ struct f_ncm_opts *ncm_opts;
+ int status;
+
+ f_ncm_inst = usb_get_function_instance("ncm");
+ if (IS_ERR(f_ncm_inst))
+ return PTR_ERR(f_ncm_inst);
+
+ ncm_opts = container_of(f_ncm_inst, struct f_ncm_opts, func_inst);
+
+ gether_set_qmult(ncm_opts->net, qmult);
+ if (!gether_set_host_addr(ncm_opts->net, host_addr))
+ pr_info("using host ethernet address: %s", host_addr);
+ if (!gether_set_dev_addr(ncm_opts->net, dev_addr))
+ pr_info("using self ethernet address: %s", dev_addr);
+
+ /* Allocate string descriptor numbers ... note that string
+ * contents can be overridden by the composite_dev glue.
+ */
+
+ status = usb_string_ids_tab(cdev, strings_dev);
+ if (status < 0)
+ goto fail;
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+
+ status = usb_add_config(cdev, &ncm_config_driver,
+ ncm_do_config);
+ if (status < 0)
+ goto fail;
+
+ usb_composite_overwrite_options(cdev, &coverwrite);
+ dev_info(&gadget->dev, "%s\n", DRIVER_DESC);
+
+ return 0;
+
+fail:
+ usb_put_function_instance(f_ncm_inst);
+ return status;
+}
+
+static int gncm_unbind(struct usb_composite_dev *cdev)
+{
+ if (!IS_ERR_OR_NULL(f_ncm))
+ usb_put_function(f_ncm);
+ if (!IS_ERR_OR_NULL(f_ncm_inst))
+ usb_put_function_instance(f_ncm_inst);
+ return 0;
+}
+
+static struct usb_composite_driver ncm_driver = {
+ .name = "g_ncm",
+ .dev = &device_desc,
+ .strings = dev_strings,
+ .max_speed = USB_SPEED_HIGH,
+ .bind = gncm_bind,
+ .unbind = gncm_unbind,
+};
+
+module_usb_composite_driver(ncm_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Yauheni Kaliuta");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/legacy/nokia.c b/drivers/usb/gadget/legacy/nokia.c
new file mode 100644
index 000000000..4bb498a38
--- /dev/null
+++ b/drivers/usb/gadget/legacy/nokia.c
@@ -0,0 +1,350 @@
+/*
+ * nokia.c -- Nokia Composite Gadget Driver
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ * Contact: Felipe Balbi <felipe.balbi@nokia.com>
+ *
+ * This gadget driver borrows from serial.c which is:
+ *
+ * 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,
+ * version 2 of that License.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+
+#include "u_serial.h"
+#include "u_ether.h"
+#include "u_phonet.h"
+#include "u_ecm.h"
+#include "gadget_chips.h"
+
+/* Defines */
+
+#define NOKIA_VERSION_NUM 0x0211
+#define NOKIA_LONG_NAME "N900 (PC-Suite Mode)"
+
+USB_GADGET_COMPOSITE_OPTIONS();
+
+USB_ETHERNET_MODULE_PARAMETERS();
+
+#define NOKIA_VENDOR_ID 0x0421 /* Nokia */
+#define NOKIA_PRODUCT_ID 0x01c8 /* Nokia Gadget */
+
+/* string IDs are assigned dynamically */
+
+#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX
+
+static char manufacturer_nokia[] = "Nokia";
+static const char product_nokia[] = NOKIA_LONG_NAME;
+static const char description_nokia[] = "PC-Suite Configuration";
+
+static struct usb_string strings_dev[] = {
+ [USB_GADGET_MANUFACTURER_IDX].s = manufacturer_nokia,
+ [USB_GADGET_PRODUCT_IDX].s = NOKIA_LONG_NAME,
+ [USB_GADGET_SERIAL_IDX].s = "",
+ [STRING_DESCRIPTION_IDX].s = description_nokia,
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+ .language = 0x0409, /* en-us */
+ .strings = strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+ &stringtab_dev,
+ NULL,
+};
+
+static struct usb_device_descriptor device_desc = {
+ .bLength = USB_DT_DEVICE_SIZE,
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = __constant_cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_COMM,
+ .idVendor = __constant_cpu_to_le16(NOKIA_VENDOR_ID),
+ .idProduct = __constant_cpu_to_le16(NOKIA_PRODUCT_ID),
+ .bcdDevice = cpu_to_le16(NOKIA_VERSION_NUM),
+ /* .iManufacturer = DYNAMIC */
+ /* .iProduct = DYNAMIC */
+ .bNumConfigurations = 1,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* Module */
+MODULE_DESCRIPTION("Nokia composite gadget driver for N900");
+MODULE_AUTHOR("Felipe Balbi");
+MODULE_LICENSE("GPL");
+
+/*-------------------------------------------------------------------------*/
+static struct usb_function *f_acm_cfg1;
+static struct usb_function *f_acm_cfg2;
+static struct usb_function *f_ecm_cfg1;
+static struct usb_function *f_ecm_cfg2;
+static struct usb_function *f_obex1_cfg1;
+static struct usb_function *f_obex2_cfg1;
+static struct usb_function *f_obex1_cfg2;
+static struct usb_function *f_obex2_cfg2;
+static struct usb_function *f_phonet_cfg1;
+static struct usb_function *f_phonet_cfg2;
+
+
+static struct usb_configuration nokia_config_500ma_driver = {
+ .label = "Bus Powered",
+ .bConfigurationValue = 1,
+ /* .iConfiguration = DYNAMIC */
+ .bmAttributes = USB_CONFIG_ATT_ONE,
+ .MaxPower = 500,
+};
+
+static struct usb_configuration nokia_config_100ma_driver = {
+ .label = "Self Powered",
+ .bConfigurationValue = 2,
+ /* .iConfiguration = DYNAMIC */
+ .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+ .MaxPower = 100,
+};
+
+static struct usb_function_instance *fi_acm;
+static struct usb_function_instance *fi_ecm;
+static struct usb_function_instance *fi_obex1;
+static struct usb_function_instance *fi_obex2;
+static struct usb_function_instance *fi_phonet;
+
+static int nokia_bind_config(struct usb_configuration *c)
+{
+ struct usb_function *f_acm;
+ struct usb_function *f_phonet = NULL;
+ struct usb_function *f_obex1 = NULL;
+ struct usb_function *f_ecm;
+ struct usb_function *f_obex2 = NULL;
+ int status = 0;
+ int obex1_stat = -1;
+ int obex2_stat = -1;
+ int phonet_stat = -1;
+
+ if (!IS_ERR(fi_phonet)) {
+ f_phonet = usb_get_function(fi_phonet);
+ if (IS_ERR(f_phonet))
+ pr_debug("could not get phonet function\n");
+ }
+
+ if (!IS_ERR(fi_obex1)) {
+ f_obex1 = usb_get_function(fi_obex1);
+ if (IS_ERR(f_obex1))
+ pr_debug("could not get obex function 0\n");
+ }
+
+ if (!IS_ERR(fi_obex2)) {
+ f_obex2 = usb_get_function(fi_obex2);
+ if (IS_ERR(f_obex2))
+ pr_debug("could not get obex function 1\n");
+ }
+
+ f_acm = usb_get_function(fi_acm);
+ if (IS_ERR(f_acm)) {
+ status = PTR_ERR(f_acm);
+ goto err_get_acm;
+ }
+
+ f_ecm = usb_get_function(fi_ecm);
+ if (IS_ERR(f_ecm)) {
+ status = PTR_ERR(f_ecm);
+ goto err_get_ecm;
+ }
+
+ if (!IS_ERR_OR_NULL(f_phonet)) {
+ phonet_stat = usb_add_function(c, f_phonet);
+ if (phonet_stat)
+ pr_debug("could not add phonet function\n");
+ }
+
+ if (!IS_ERR_OR_NULL(f_obex1)) {
+ obex1_stat = usb_add_function(c, f_obex1);
+ if (obex1_stat)
+ pr_debug("could not add obex function 0\n");
+ }
+
+ if (!IS_ERR_OR_NULL(f_obex2)) {
+ obex2_stat = usb_add_function(c, f_obex2);
+ if (obex2_stat)
+ pr_debug("could not add obex function 1\n");
+ }
+
+ status = usb_add_function(c, f_acm);
+ if (status)
+ goto err_conf;
+
+ status = usb_add_function(c, f_ecm);
+ if (status) {
+ pr_debug("could not bind ecm config %d\n", status);
+ goto err_ecm;
+ }
+ if (c == &nokia_config_500ma_driver) {
+ f_acm_cfg1 = f_acm;
+ f_ecm_cfg1 = f_ecm;
+ f_phonet_cfg1 = f_phonet;
+ f_obex1_cfg1 = f_obex1;
+ f_obex2_cfg1 = f_obex2;
+ } else {
+ f_acm_cfg2 = f_acm;
+ f_ecm_cfg2 = f_ecm;
+ f_phonet_cfg2 = f_phonet;
+ f_obex1_cfg2 = f_obex1;
+ f_obex2_cfg2 = f_obex2;
+ }
+
+ return status;
+err_ecm:
+ usb_remove_function(c, f_acm);
+err_conf:
+ if (!obex2_stat)
+ usb_remove_function(c, f_obex2);
+ if (!obex1_stat)
+ usb_remove_function(c, f_obex1);
+ if (!phonet_stat)
+ usb_remove_function(c, f_phonet);
+ usb_put_function(f_ecm);
+err_get_ecm:
+ usb_put_function(f_acm);
+err_get_acm:
+ if (!IS_ERR_OR_NULL(f_obex2))
+ usb_put_function(f_obex2);
+ if (!IS_ERR_OR_NULL(f_obex1))
+ usb_put_function(f_obex1);
+ if (!IS_ERR_OR_NULL(f_phonet))
+ usb_put_function(f_phonet);
+ return status;
+}
+
+static int nokia_bind(struct usb_composite_dev *cdev)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ int status;
+
+ status = usb_string_ids_tab(cdev, strings_dev);
+ if (status < 0)
+ goto err_usb;
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+ status = strings_dev[STRING_DESCRIPTION_IDX].id;
+ nokia_config_500ma_driver.iConfiguration = status;
+ nokia_config_100ma_driver.iConfiguration = status;
+
+ if (!gadget_supports_altsettings(gadget)) {
+ status = -ENODEV;
+ goto err_usb;
+ }
+
+ fi_phonet = usb_get_function_instance("phonet");
+ if (IS_ERR(fi_phonet))
+ pr_debug("could not find phonet function\n");
+
+ fi_obex1 = usb_get_function_instance("obex");
+ if (IS_ERR(fi_obex1))
+ pr_debug("could not find obex function 1\n");
+
+ fi_obex2 = usb_get_function_instance("obex");
+ if (IS_ERR(fi_obex2))
+ pr_debug("could not find obex function 2\n");
+
+ fi_acm = usb_get_function_instance("acm");
+ if (IS_ERR(fi_acm)) {
+ status = PTR_ERR(fi_acm);
+ goto err_obex2_inst;
+ }
+
+ fi_ecm = usb_get_function_instance("ecm");
+ if (IS_ERR(fi_ecm)) {
+ status = PTR_ERR(fi_ecm);
+ goto err_acm_inst;
+ }
+
+ /* finally register the configuration */
+ status = usb_add_config(cdev, &nokia_config_500ma_driver,
+ nokia_bind_config);
+ if (status < 0)
+ goto err_ecm_inst;
+
+ status = usb_add_config(cdev, &nokia_config_100ma_driver,
+ nokia_bind_config);
+ if (status < 0)
+ goto err_put_cfg1;
+
+ usb_composite_overwrite_options(cdev, &coverwrite);
+ dev_info(&gadget->dev, "%s\n", NOKIA_LONG_NAME);
+
+ return 0;
+
+err_put_cfg1:
+ usb_put_function(f_acm_cfg1);
+ if (!IS_ERR_OR_NULL(f_obex1_cfg1))
+ usb_put_function(f_obex1_cfg1);
+ if (!IS_ERR_OR_NULL(f_obex2_cfg1))
+ usb_put_function(f_obex2_cfg1);
+ if (!IS_ERR_OR_NULL(f_phonet_cfg1))
+ usb_put_function(f_phonet_cfg1);
+ usb_put_function(f_ecm_cfg1);
+err_ecm_inst:
+ usb_put_function_instance(fi_ecm);
+err_acm_inst:
+ usb_put_function_instance(fi_acm);
+err_obex2_inst:
+ if (!IS_ERR(fi_obex2))
+ usb_put_function_instance(fi_obex2);
+ if (!IS_ERR(fi_obex1))
+ usb_put_function_instance(fi_obex1);
+ if (!IS_ERR(fi_phonet))
+ usb_put_function_instance(fi_phonet);
+err_usb:
+ return status;
+}
+
+static int nokia_unbind(struct usb_composite_dev *cdev)
+{
+ if (!IS_ERR_OR_NULL(f_obex1_cfg2))
+ usb_put_function(f_obex1_cfg2);
+ if (!IS_ERR_OR_NULL(f_obex2_cfg2))
+ usb_put_function(f_obex2_cfg2);
+ if (!IS_ERR_OR_NULL(f_obex1_cfg1))
+ usb_put_function(f_obex1_cfg1);
+ if (!IS_ERR_OR_NULL(f_obex2_cfg1))
+ usb_put_function(f_obex2_cfg1);
+ if (!IS_ERR_OR_NULL(f_phonet_cfg1))
+ usb_put_function(f_phonet_cfg1);
+ if (!IS_ERR_OR_NULL(f_phonet_cfg2))
+ usb_put_function(f_phonet_cfg2);
+ usb_put_function(f_acm_cfg1);
+ usb_put_function(f_acm_cfg2);
+ usb_put_function(f_ecm_cfg1);
+ usb_put_function(f_ecm_cfg2);
+
+ usb_put_function_instance(fi_ecm);
+ if (!IS_ERR(fi_obex2))
+ usb_put_function_instance(fi_obex2);
+ if (!IS_ERR(fi_obex1))
+ usb_put_function_instance(fi_obex1);
+ if (!IS_ERR(fi_phonet))
+ usb_put_function_instance(fi_phonet);
+ usb_put_function_instance(fi_acm);
+
+ return 0;
+}
+
+static struct usb_composite_driver nokia_driver = {
+ .name = "g_nokia",
+ .dev = &device_desc,
+ .strings = dev_strings,
+ .max_speed = USB_SPEED_HIGH,
+ .bind = nokia_bind,
+ .unbind = nokia_unbind,
+};
+
+module_usb_composite_driver(nokia_driver);
diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c
new file mode 100644
index 000000000..1ce7df106
--- /dev/null
+++ b/drivers/usb/gadget/legacy/printer.c
@@ -0,0 +1,215 @@
+/*
+ * printer.c -- Printer gadget driver
+ *
+ * Copyright (C) 2003-2005 David Brownell
+ * Copyright (C) 2006 Craig W. Nadler
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <asm/byteorder.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/g_printer.h>
+
+#include "gadget_chips.h"
+
+USB_GADGET_COMPOSITE_OPTIONS();
+
+#define DRIVER_DESC "Printer Gadget"
+#define DRIVER_VERSION "2015 FEB 17"
+
+static const char shortname [] = "printer";
+static const char driver_desc [] = DRIVER_DESC;
+
+#include "u_printer.h"
+
+/*-------------------------------------------------------------------------*/
+
+/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
+ * Instead: allocate your own, using normal USB-IF procedures.
+ */
+
+/* Thanks to NetChip Technologies for donating this product ID.
+ */
+#define PRINTER_VENDOR_NUM 0x0525 /* NetChip */
+#define PRINTER_PRODUCT_NUM 0xa4a8 /* Linux-USB Printer Gadget */
+
+/* Some systems will want different product identifiers published in the
+ * device descriptor, either numbers or strings or both. These string
+ * parameters are in UTF-8 (superset of ASCII's 7 bit characters).
+ */
+
+module_param_named(iSerialNum, coverwrite.serial_number, charp, S_IRUGO);
+MODULE_PARM_DESC(iSerialNum, "1");
+
+static char *iPNPstring;
+module_param(iPNPstring, charp, S_IRUGO);
+MODULE_PARM_DESC(iPNPstring, "MFG:linux;MDL:g_printer;CLS:PRINTER;SN:1;");
+
+/* Number of requests to allocate per endpoint, not used for ep0. */
+static unsigned qlen = 10;
+module_param(qlen, uint, S_IRUGO|S_IWUSR);
+
+#define QLEN qlen
+
+static struct usb_function_instance *fi_printer;
+static struct usb_function *f_printer;
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * DESCRIPTORS ... most are static, but strings and (full) configuration
+ * descriptors are built on demand.
+ */
+
+static struct usb_device_descriptor device_desc = {
+ .bLength = sizeof device_desc,
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_PER_INTERFACE,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+ .idVendor = cpu_to_le16(PRINTER_VENDOR_NUM),
+ .idProduct = cpu_to_le16(PRINTER_PRODUCT_NUM),
+ .bNumConfigurations = 1
+};
+
+static struct usb_otg_descriptor otg_descriptor = {
+ .bLength = sizeof otg_descriptor,
+ .bDescriptorType = USB_DT_OTG,
+ .bmAttributes = USB_OTG_SRP,
+};
+
+static const struct usb_descriptor_header *otg_desc[] = {
+ (struct usb_descriptor_header *) &otg_descriptor,
+ NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* descriptors that are built on-demand */
+
+static char product_desc [40] = DRIVER_DESC;
+static char serial_num [40] = "1";
+static char pnp_string[PNP_STRING_LEN] =
+ "XXMFG:linux;MDL:g_printer;CLS:PRINTER;SN:1;";
+
+/* static strings, in UTF-8 */
+static struct usb_string strings [] = {
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = product_desc,
+ [USB_GADGET_SERIAL_IDX].s = serial_num,
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+ .language = 0x0409, /* en-us */
+ .strings = strings,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+ &stringtab_dev,
+ NULL,
+};
+
+static struct usb_configuration printer_cfg_driver = {
+ .label = "printer",
+ .bConfigurationValue = 1,
+ .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+};
+
+static int printer_do_config(struct usb_configuration *c)
+{
+ struct usb_gadget *gadget = c->cdev->gadget;
+ int status = 0;
+
+ usb_ep_autoconfig_reset(gadget);
+
+ usb_gadget_set_selfpowered(gadget);
+
+ if (gadget_is_otg(gadget)) {
+ otg_descriptor.bmAttributes |= USB_OTG_HNP;
+ printer_cfg_driver.descriptors = otg_desc;
+ printer_cfg_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
+ f_printer = usb_get_function(fi_printer);
+ if (IS_ERR(f_printer))
+ return PTR_ERR(f_printer);
+
+ status = usb_add_function(c, f_printer);
+ if (status < 0)
+ usb_put_function(f_printer);
+
+ return status;
+}
+
+static int printer_bind(struct usb_composite_dev *cdev)
+{
+ struct f_printer_opts *opts;
+ int ret, len;
+
+ fi_printer = usb_get_function_instance("printer");
+ if (IS_ERR(fi_printer))
+ return PTR_ERR(fi_printer);
+
+ if (iPNPstring)
+ strlcpy(&pnp_string[2], iPNPstring, PNP_STRING_LEN - 2);
+
+ len = strlen(pnp_string);
+ pnp_string[0] = (len >> 8) & 0xFF;
+ pnp_string[1] = len & 0xFF;
+
+ opts = container_of(fi_printer, struct f_printer_opts, func_inst);
+ opts->minor = 0;
+ memcpy(opts->pnp_string, pnp_string, PNP_STRING_LEN);
+ opts->q_len = QLEN;
+
+ ret = usb_string_ids_tab(cdev, strings);
+ if (ret < 0) {
+ usb_put_function_instance(fi_printer);
+ return ret;
+ }
+ device_desc.iManufacturer = strings[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings[USB_GADGET_PRODUCT_IDX].id;
+ device_desc.iSerialNumber = strings[USB_GADGET_SERIAL_IDX].id;
+
+ ret = usb_add_config(cdev, &printer_cfg_driver, printer_do_config);
+ if (ret) {
+ usb_put_function_instance(fi_printer);
+ return ret;
+ }
+ usb_composite_overwrite_options(cdev, &coverwrite);
+ return ret;
+}
+
+static int printer_unbind(struct usb_composite_dev *cdev)
+{
+ usb_put_function(f_printer);
+ usb_put_function_instance(fi_printer);
+
+ return 0;
+}
+
+static struct usb_composite_driver printer_driver = {
+ .name = shortname,
+ .dev = &device_desc,
+ .strings = dev_strings,
+ .max_speed = USB_SPEED_SUPER,
+ .bind = printer_bind,
+ .unbind = printer_unbind,
+};
+
+module_usb_composite_driver(printer_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Craig Nadler");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/legacy/serial.c b/drivers/usb/gadget/legacy/serial.c
new file mode 100644
index 000000000..8b7528f9b
--- /dev/null
+++ b/drivers/usb/gadget/legacy/serial.c
@@ -0,0 +1,276 @@
+/*
+ * serial.c -- USB gadget serial 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/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#include "u_serial.h"
+#include "gadget_chips.h"
+
+
+/* Defines */
+
+#define GS_VERSION_STR "v2.4"
+#define GS_VERSION_NUM 0x2400
+
+#define GS_LONG_NAME "Gadget Serial"
+#define GS_VERSION_NAME GS_LONG_NAME " " GS_VERSION_STR
+
+/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
+
+/* Thanks to NetChip Technologies for donating this product ID.
+*
+* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
+* Instead: allocate your own, using normal USB-IF procedures.
+*/
+#define GS_VENDOR_ID 0x0525 /* NetChip */
+#define GS_PRODUCT_ID 0xa4a6 /* Linux-USB Serial Gadget */
+#define GS_CDC_PRODUCT_ID 0xa4a7 /* ... as CDC-ACM */
+#define GS_CDC_OBEX_PRODUCT_ID 0xa4a9 /* ... as CDC-OBEX */
+
+/* string IDs are assigned dynamically */
+
+#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX
+
+static struct usb_string strings_dev[] = {
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = GS_VERSION_NAME,
+ [USB_GADGET_SERIAL_IDX].s = "",
+ [STRING_DESCRIPTION_IDX].s = NULL /* updated; f(use_acm) */,
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+ .language = 0x0409, /* en-us */
+ .strings = strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+ &stringtab_dev,
+ NULL,
+};
+
+static struct usb_device_descriptor device_desc = {
+ .bLength = USB_DT_DEVICE_SIZE,
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = cpu_to_le16(0x0200),
+ /* .bDeviceClass = f(use_acm) */
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+ /* .bMaxPacketSize0 = f(hardware) */
+ .idVendor = cpu_to_le16(GS_VENDOR_ID),
+ /* .idProduct = f(use_acm) */
+ .bcdDevice = cpu_to_le16(GS_VERSION_NUM),
+ /* .iManufacturer = DYNAMIC */
+ /* .iProduct = DYNAMIC */
+ .bNumConfigurations = 1,
+};
+
+static struct usb_otg_descriptor otg_descriptor = {
+ .bLength = sizeof otg_descriptor,
+ .bDescriptorType = USB_DT_OTG,
+
+ /* REVISIT SRP-only hardware is possible, although
+ * it would not be called "OTG" ...
+ */
+ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
+};
+
+static const struct usb_descriptor_header *otg_desc[] = {
+ (struct usb_descriptor_header *) &otg_descriptor,
+ NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* Module */
+MODULE_DESCRIPTION(GS_VERSION_NAME);
+MODULE_AUTHOR("Al Borchers");
+MODULE_AUTHOR("David Brownell");
+MODULE_LICENSE("GPL");
+
+static bool use_acm = true;
+module_param(use_acm, bool, 0);
+MODULE_PARM_DESC(use_acm, "Use CDC ACM, default=yes");
+
+static bool use_obex = false;
+module_param(use_obex, bool, 0);
+MODULE_PARM_DESC(use_obex, "Use CDC OBEX, default=no");
+
+static unsigned n_ports = 1;
+module_param(n_ports, uint, 0);
+MODULE_PARM_DESC(n_ports, "number of ports to create, default=1");
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_configuration serial_config_driver = {
+ /* .label = f(use_acm) */
+ /* .bConfigurationValue = f(use_acm) */
+ /* .iConfiguration = DYNAMIC */
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+};
+
+static struct usb_function_instance *fi_serial[MAX_U_SERIAL_PORTS];
+static struct usb_function *f_serial[MAX_U_SERIAL_PORTS];
+
+static int serial_register_ports(struct usb_composite_dev *cdev,
+ struct usb_configuration *c, const char *f_name)
+{
+ int i;
+ int ret;
+
+ ret = usb_add_config_only(cdev, c);
+ if (ret)
+ goto out;
+
+ for (i = 0; i < n_ports; i++) {
+
+ fi_serial[i] = usb_get_function_instance(f_name);
+ if (IS_ERR(fi_serial[i])) {
+ ret = PTR_ERR(fi_serial[i]);
+ goto fail;
+ }
+
+ f_serial[i] = usb_get_function(fi_serial[i]);
+ if (IS_ERR(f_serial[i])) {
+ ret = PTR_ERR(f_serial[i]);
+ goto err_get_func;
+ }
+
+ ret = usb_add_function(c, f_serial[i]);
+ if (ret)
+ goto err_add_func;
+ }
+
+ return 0;
+
+err_add_func:
+ usb_put_function(f_serial[i]);
+err_get_func:
+ usb_put_function_instance(fi_serial[i]);
+
+fail:
+ i--;
+ while (i >= 0) {
+ usb_remove_function(c, f_serial[i]);
+ usb_put_function(f_serial[i]);
+ usb_put_function_instance(fi_serial[i]);
+ i--;
+ }
+out:
+ return ret;
+}
+
+static int gs_bind(struct usb_composite_dev *cdev)
+{
+ int status;
+
+ /* Allocate string descriptor numbers ... note that string
+ * contents can be overridden by the composite_dev glue.
+ */
+
+ status = usb_string_ids_tab(cdev, strings_dev);
+ if (status < 0)
+ goto fail;
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+ status = strings_dev[STRING_DESCRIPTION_IDX].id;
+ serial_config_driver.iConfiguration = status;
+
+ if (gadget_is_otg(cdev->gadget)) {
+ serial_config_driver.descriptors = otg_desc;
+ serial_config_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
+ /* register our configuration */
+ if (use_acm) {
+ status = serial_register_ports(cdev, &serial_config_driver,
+ "acm");
+ usb_ep_autoconfig_reset(cdev->gadget);
+ } else if (use_obex)
+ status = serial_register_ports(cdev, &serial_config_driver,
+ "obex");
+ else {
+ status = serial_register_ports(cdev, &serial_config_driver,
+ "gser");
+ }
+ if (status < 0)
+ goto fail;
+
+ usb_composite_overwrite_options(cdev, &coverwrite);
+ INFO(cdev, "%s\n", GS_VERSION_NAME);
+
+ return 0;
+
+fail:
+ return status;
+}
+
+static int gs_unbind(struct usb_composite_dev *cdev)
+{
+ int i;
+
+ for (i = 0; i < n_ports; i++) {
+ usb_put_function(f_serial[i]);
+ usb_put_function_instance(fi_serial[i]);
+ }
+ return 0;
+}
+
+static struct usb_composite_driver gserial_driver = {
+ .name = "g_serial",
+ .dev = &device_desc,
+ .strings = dev_strings,
+ .max_speed = USB_SPEED_SUPER,
+ .bind = gs_bind,
+ .unbind = gs_unbind,
+};
+
+static int __init init(void)
+{
+ /* We *could* export two configs; that'd be much cleaner...
+ * but neither of these product IDs was defined that way.
+ */
+ if (use_acm) {
+ serial_config_driver.label = "CDC ACM config";
+ serial_config_driver.bConfigurationValue = 2;
+ device_desc.bDeviceClass = USB_CLASS_COMM;
+ device_desc.idProduct =
+ cpu_to_le16(GS_CDC_PRODUCT_ID);
+ } else if (use_obex) {
+ serial_config_driver.label = "CDC OBEX config";
+ serial_config_driver.bConfigurationValue = 3;
+ device_desc.bDeviceClass = USB_CLASS_COMM;
+ device_desc.idProduct =
+ cpu_to_le16(GS_CDC_OBEX_PRODUCT_ID);
+ } else {
+ serial_config_driver.label = "Generic Serial config";
+ serial_config_driver.bConfigurationValue = 1;
+ device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC;
+ device_desc.idProduct =
+ cpu_to_le16(GS_PRODUCT_ID);
+ }
+ strings_dev[STRING_DESCRIPTION_IDX].s = serial_config_driver.label;
+
+ return usb_composite_probe(&gserial_driver);
+}
+module_init(init);
+
+static void __exit cleanup(void)
+{
+ usb_composite_unregister(&gserial_driver);
+}
+module_exit(cleanup);
diff --git a/drivers/usb/gadget/legacy/tcm_usb_gadget.c b/drivers/usb/gadget/legacy/tcm_usb_gadget.c
new file mode 100644
index 000000000..f9b4882fc
--- /dev/null
+++ b/drivers/usb/gadget/legacy/tcm_usb_gadget.c
@@ -0,0 +1,2433 @@
+/* Target based USB-Gadget
+ *
+ * UAS protocol handling, target callbacks, configfs handling,
+ * BBB (USB Mass Storage Class Bulk-Only (BBB) and Transport protocol handling.
+ *
+ * Author: Sebastian Andrzej Siewior <bigeasy at linutronix dot de>
+ * License: GPLv2 as published by FSF.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/configfs.h>
+#include <linux/ctype.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/storage.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_tcq.h>
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_fabric_configfs.h>
+#include <target/target_core_configfs.h>
+#include <target/configfs_macros.h>
+#include <asm/unaligned.h>
+
+#include "tcm_usb_gadget.h"
+
+USB_GADGET_COMPOSITE_OPTIONS();
+
+static const struct target_core_fabric_ops usbg_ops;
+
+static inline struct f_uas *to_f_uas(struct usb_function *f)
+{
+ return container_of(f, struct f_uas, function);
+}
+
+static void usbg_cmd_release(struct kref *);
+
+static inline void usbg_cleanup_cmd(struct usbg_cmd *cmd)
+{
+ kref_put(&cmd->ref, usbg_cmd_release);
+}
+
+/* Start bot.c code */
+
+static int bot_enqueue_cmd_cbw(struct f_uas *fu)
+{
+ int ret;
+
+ if (fu->flags & USBG_BOT_CMD_PEND)
+ return 0;
+
+ ret = usb_ep_queue(fu->ep_out, fu->cmd.req, GFP_ATOMIC);
+ if (!ret)
+ fu->flags |= USBG_BOT_CMD_PEND;
+ return ret;
+}
+
+static void bot_status_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct usbg_cmd *cmd = req->context;
+ struct f_uas *fu = cmd->fu;
+
+ usbg_cleanup_cmd(cmd);
+ if (req->status < 0) {
+ pr_err("ERR %s(%d)\n", __func__, __LINE__);
+ return;
+ }
+
+ /* CSW completed, wait for next CBW */
+ bot_enqueue_cmd_cbw(fu);
+}
+
+static void bot_enqueue_sense_code(struct f_uas *fu, struct usbg_cmd *cmd)
+{
+ struct bulk_cs_wrap *csw = &fu->bot_status.csw;
+ int ret;
+ u8 *sense;
+ unsigned int csw_stat;
+
+ csw_stat = cmd->csw_code;
+
+ /*
+ * We can't send SENSE as a response. So we take ASC & ASCQ from our
+ * sense buffer and queue it and hope the host sends a REQUEST_SENSE
+ * command where it learns why we failed.
+ */
+ sense = cmd->sense_iu.sense;
+
+ csw->Tag = cmd->bot_tag;
+ csw->Status = csw_stat;
+ fu->bot_status.req->context = cmd;
+ ret = usb_ep_queue(fu->ep_in, fu->bot_status.req, GFP_ATOMIC);
+ if (ret)
+ pr_err("%s(%d) ERR: %d\n", __func__, __LINE__, ret);
+}
+
+static void bot_err_compl(struct usb_ep *ep, struct usb_request *req)
+{
+ struct usbg_cmd *cmd = req->context;
+ struct f_uas *fu = cmd->fu;
+
+ if (req->status < 0)
+ pr_err("ERR %s(%d)\n", __func__, __LINE__);
+
+ if (cmd->data_len) {
+ if (cmd->data_len > ep->maxpacket) {
+ req->length = ep->maxpacket;
+ cmd->data_len -= ep->maxpacket;
+ } else {
+ req->length = cmd->data_len;
+ cmd->data_len = 0;
+ }
+
+ usb_ep_queue(ep, req, GFP_ATOMIC);
+ return ;
+ }
+ bot_enqueue_sense_code(fu, cmd);
+}
+
+static void bot_send_bad_status(struct usbg_cmd *cmd)
+{
+ struct f_uas *fu = cmd->fu;
+ struct bulk_cs_wrap *csw = &fu->bot_status.csw;
+ struct usb_request *req;
+ struct usb_ep *ep;
+
+ csw->Residue = cpu_to_le32(cmd->data_len);
+
+ if (cmd->data_len) {
+ if (cmd->is_read) {
+ ep = fu->ep_in;
+ req = fu->bot_req_in;
+ } else {
+ ep = fu->ep_out;
+ req = fu->bot_req_out;
+ }
+
+ if (cmd->data_len > fu->ep_in->maxpacket) {
+ req->length = ep->maxpacket;
+ cmd->data_len -= ep->maxpacket;
+ } else {
+ req->length = cmd->data_len;
+ cmd->data_len = 0;
+ }
+ req->complete = bot_err_compl;
+ req->context = cmd;
+ req->buf = fu->cmd.buf;
+ usb_ep_queue(ep, req, GFP_KERNEL);
+ } else {
+ bot_enqueue_sense_code(fu, cmd);
+ }
+}
+
+static int bot_send_status(struct usbg_cmd *cmd, bool moved_data)
+{
+ struct f_uas *fu = cmd->fu;
+ struct bulk_cs_wrap *csw = &fu->bot_status.csw;
+ int ret;
+
+ if (cmd->se_cmd.scsi_status == SAM_STAT_GOOD) {
+ if (!moved_data && cmd->data_len) {
+ /*
+ * the host wants to move data, we don't. Fill / empty
+ * the pipe and then send the csw with reside set.
+ */
+ cmd->csw_code = US_BULK_STAT_OK;
+ bot_send_bad_status(cmd);
+ return 0;
+ }
+
+ csw->Tag = cmd->bot_tag;
+ csw->Residue = cpu_to_le32(0);
+ csw->Status = US_BULK_STAT_OK;
+ fu->bot_status.req->context = cmd;
+
+ ret = usb_ep_queue(fu->ep_in, fu->bot_status.req, GFP_KERNEL);
+ if (ret)
+ pr_err("%s(%d) ERR: %d\n", __func__, __LINE__, ret);
+ } else {
+ cmd->csw_code = US_BULK_STAT_FAIL;
+ bot_send_bad_status(cmd);
+ }
+ return 0;
+}
+
+/*
+ * Called after command (no data transfer) or after the write (to device)
+ * operation is completed
+ */
+static int bot_send_status_response(struct usbg_cmd *cmd)
+{
+ bool moved_data = false;
+
+ if (!cmd->is_read)
+ moved_data = true;
+ return bot_send_status(cmd, moved_data);
+}
+
+/* Read request completed, now we have to send the CSW */
+static void bot_read_compl(struct usb_ep *ep, struct usb_request *req)
+{
+ struct usbg_cmd *cmd = req->context;
+
+ if (req->status < 0)
+ pr_err("ERR %s(%d)\n", __func__, __LINE__);
+
+ bot_send_status(cmd, true);
+}
+
+static int bot_send_read_response(struct usbg_cmd *cmd)
+{
+ struct f_uas *fu = cmd->fu;
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+ struct usb_gadget *gadget = fuas_to_gadget(fu);
+ int ret;
+
+ if (!cmd->data_len) {
+ cmd->csw_code = US_BULK_STAT_PHASE;
+ bot_send_bad_status(cmd);
+ return 0;
+ }
+
+ if (!gadget->sg_supported) {
+ cmd->data_buf = kmalloc(se_cmd->data_length, GFP_ATOMIC);
+ if (!cmd->data_buf)
+ return -ENOMEM;
+
+ sg_copy_to_buffer(se_cmd->t_data_sg,
+ se_cmd->t_data_nents,
+ cmd->data_buf,
+ se_cmd->data_length);
+
+ fu->bot_req_in->buf = cmd->data_buf;
+ } else {
+ fu->bot_req_in->buf = NULL;
+ fu->bot_req_in->num_sgs = se_cmd->t_data_nents;
+ fu->bot_req_in->sg = se_cmd->t_data_sg;
+ }
+
+ fu->bot_req_in->complete = bot_read_compl;
+ fu->bot_req_in->length = se_cmd->data_length;
+ fu->bot_req_in->context = cmd;
+ ret = usb_ep_queue(fu->ep_in, fu->bot_req_in, GFP_ATOMIC);
+ if (ret)
+ pr_err("%s(%d)\n", __func__, __LINE__);
+ return 0;
+}
+
+static void usbg_data_write_cmpl(struct usb_ep *, struct usb_request *);
+static int usbg_prepare_w_request(struct usbg_cmd *, struct usb_request *);
+
+static int bot_send_write_request(struct usbg_cmd *cmd)
+{
+ struct f_uas *fu = cmd->fu;
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+ struct usb_gadget *gadget = fuas_to_gadget(fu);
+ int ret;
+
+ init_completion(&cmd->write_complete);
+ cmd->fu = fu;
+
+ if (!cmd->data_len) {
+ cmd->csw_code = US_BULK_STAT_PHASE;
+ return -EINVAL;
+ }
+
+ if (!gadget->sg_supported) {
+ cmd->data_buf = kmalloc(se_cmd->data_length, GFP_KERNEL);
+ if (!cmd->data_buf)
+ return -ENOMEM;
+
+ fu->bot_req_out->buf = cmd->data_buf;
+ } else {
+ fu->bot_req_out->buf = NULL;
+ fu->bot_req_out->num_sgs = se_cmd->t_data_nents;
+ fu->bot_req_out->sg = se_cmd->t_data_sg;
+ }
+
+ fu->bot_req_out->complete = usbg_data_write_cmpl;
+ fu->bot_req_out->length = se_cmd->data_length;
+ fu->bot_req_out->context = cmd;
+
+ ret = usbg_prepare_w_request(cmd, fu->bot_req_out);
+ if (ret)
+ goto cleanup;
+ ret = usb_ep_queue(fu->ep_out, fu->bot_req_out, GFP_KERNEL);
+ if (ret)
+ pr_err("%s(%d)\n", __func__, __LINE__);
+
+ wait_for_completion(&cmd->write_complete);
+ target_execute_cmd(se_cmd);
+cleanup:
+ return ret;
+}
+
+static int bot_submit_command(struct f_uas *, void *, unsigned int);
+
+static void bot_cmd_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_uas *fu = req->context;
+ int ret;
+
+ fu->flags &= ~USBG_BOT_CMD_PEND;
+
+ if (req->status < 0)
+ return;
+
+ ret = bot_submit_command(fu, req->buf, req->actual);
+ if (ret)
+ pr_err("%s(%d): %d\n", __func__, __LINE__, ret);
+}
+
+static int bot_prepare_reqs(struct f_uas *fu)
+{
+ int ret;
+
+ fu->bot_req_in = usb_ep_alloc_request(fu->ep_in, GFP_KERNEL);
+ if (!fu->bot_req_in)
+ goto err;
+
+ fu->bot_req_out = usb_ep_alloc_request(fu->ep_out, GFP_KERNEL);
+ if (!fu->bot_req_out)
+ goto err_out;
+
+ fu->cmd.req = usb_ep_alloc_request(fu->ep_out, GFP_KERNEL);
+ if (!fu->cmd.req)
+ goto err_cmd;
+
+ fu->bot_status.req = usb_ep_alloc_request(fu->ep_in, GFP_KERNEL);
+ if (!fu->bot_status.req)
+ goto err_sts;
+
+ fu->bot_status.req->buf = &fu->bot_status.csw;
+ fu->bot_status.req->length = US_BULK_CS_WRAP_LEN;
+ fu->bot_status.req->complete = bot_status_complete;
+ fu->bot_status.csw.Signature = cpu_to_le32(US_BULK_CS_SIGN);
+
+ fu->cmd.buf = kmalloc(fu->ep_out->maxpacket, GFP_KERNEL);
+ if (!fu->cmd.buf)
+ goto err_buf;
+
+ fu->cmd.req->complete = bot_cmd_complete;
+ fu->cmd.req->buf = fu->cmd.buf;
+ fu->cmd.req->length = fu->ep_out->maxpacket;
+ fu->cmd.req->context = fu;
+
+ ret = bot_enqueue_cmd_cbw(fu);
+ if (ret)
+ goto err_queue;
+ return 0;
+err_queue:
+ kfree(fu->cmd.buf);
+ fu->cmd.buf = NULL;
+err_buf:
+ usb_ep_free_request(fu->ep_in, fu->bot_status.req);
+err_sts:
+ usb_ep_free_request(fu->ep_out, fu->cmd.req);
+ fu->cmd.req = NULL;
+err_cmd:
+ usb_ep_free_request(fu->ep_out, fu->bot_req_out);
+ fu->bot_req_out = NULL;
+err_out:
+ usb_ep_free_request(fu->ep_in, fu->bot_req_in);
+ fu->bot_req_in = NULL;
+err:
+ pr_err("BOT: endpoint setup failed\n");
+ return -ENOMEM;
+}
+
+static void bot_cleanup_old_alt(struct f_uas *fu)
+{
+ if (!(fu->flags & USBG_ENABLED))
+ return;
+
+ usb_ep_disable(fu->ep_in);
+ usb_ep_disable(fu->ep_out);
+
+ if (!fu->bot_req_in)
+ return;
+
+ usb_ep_free_request(fu->ep_in, fu->bot_req_in);
+ usb_ep_free_request(fu->ep_out, fu->bot_req_out);
+ usb_ep_free_request(fu->ep_out, fu->cmd.req);
+ usb_ep_free_request(fu->ep_out, fu->bot_status.req);
+
+ kfree(fu->cmd.buf);
+
+ fu->bot_req_in = NULL;
+ fu->bot_req_out = NULL;
+ fu->cmd.req = NULL;
+ fu->bot_status.req = NULL;
+ fu->cmd.buf = NULL;
+}
+
+static void bot_set_alt(struct f_uas *fu)
+{
+ struct usb_function *f = &fu->function;
+ struct usb_gadget *gadget = f->config->cdev->gadget;
+ int ret;
+
+ fu->flags = USBG_IS_BOT;
+
+ config_ep_by_speed(gadget, f, fu->ep_in);
+ ret = usb_ep_enable(fu->ep_in);
+ if (ret)
+ goto err_b_in;
+
+ config_ep_by_speed(gadget, f, fu->ep_out);
+ ret = usb_ep_enable(fu->ep_out);
+ if (ret)
+ goto err_b_out;
+
+ ret = bot_prepare_reqs(fu);
+ if (ret)
+ goto err_wq;
+ fu->flags |= USBG_ENABLED;
+ pr_info("Using the BOT protocol\n");
+ return;
+err_wq:
+ usb_ep_disable(fu->ep_out);
+err_b_out:
+ usb_ep_disable(fu->ep_in);
+err_b_in:
+ fu->flags = USBG_IS_BOT;
+}
+
+static int usbg_bot_setup(struct usb_function *f,
+ const struct usb_ctrlrequest *ctrl)
+{
+ struct f_uas *fu = to_f_uas(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+ int luns;
+ u8 *ret_lun;
+
+ switch (ctrl->bRequest) {
+ case US_BULK_GET_MAX_LUN:
+ if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_CLASS |
+ USB_RECIP_INTERFACE))
+ return -ENOTSUPP;
+
+ if (w_length < 1)
+ return -EINVAL;
+ if (w_value != 0)
+ return -EINVAL;
+ luns = atomic_read(&fu->tpg->tpg_port_count);
+ if (!luns) {
+ pr_err("No LUNs configured?\n");
+ return -EINVAL;
+ }
+ /*
+ * If 4 LUNs are present we return 3 i.e. LUN 0..3 can be
+ * accessed. The upper limit is 0xf
+ */
+ luns--;
+ if (luns > 0xf) {
+ pr_info_once("Limiting the number of luns to 16\n");
+ luns = 0xf;
+ }
+ ret_lun = cdev->req->buf;
+ *ret_lun = luns;
+ cdev->req->length = 1;
+ return usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC);
+ break;
+
+ case US_BULK_RESET_REQUEST:
+ /* XXX maybe we should remove previous requests for IN + OUT */
+ bot_enqueue_cmd_cbw(fu);
+ return 0;
+ break;
+ }
+ return -ENOTSUPP;
+}
+
+/* Start uas.c code */
+
+static void uasp_cleanup_one_stream(struct f_uas *fu, struct uas_stream *stream)
+{
+ /* We have either all three allocated or none */
+ if (!stream->req_in)
+ return;
+
+ usb_ep_free_request(fu->ep_in, stream->req_in);
+ usb_ep_free_request(fu->ep_out, stream->req_out);
+ usb_ep_free_request(fu->ep_status, stream->req_status);
+
+ stream->req_in = NULL;
+ stream->req_out = NULL;
+ stream->req_status = NULL;
+}
+
+static void uasp_free_cmdreq(struct f_uas *fu)
+{
+ usb_ep_free_request(fu->ep_cmd, fu->cmd.req);
+ kfree(fu->cmd.buf);
+ fu->cmd.req = NULL;
+ fu->cmd.buf = NULL;
+}
+
+static void uasp_cleanup_old_alt(struct f_uas *fu)
+{
+ int i;
+
+ if (!(fu->flags & USBG_ENABLED))
+ return;
+
+ usb_ep_disable(fu->ep_in);
+ usb_ep_disable(fu->ep_out);
+ usb_ep_disable(fu->ep_status);
+ usb_ep_disable(fu->ep_cmd);
+
+ for (i = 0; i < UASP_SS_EP_COMP_NUM_STREAMS; i++)
+ uasp_cleanup_one_stream(fu, &fu->stream[i]);
+ uasp_free_cmdreq(fu);
+}
+
+static void uasp_status_data_cmpl(struct usb_ep *ep, struct usb_request *req);
+
+static int uasp_prepare_r_request(struct usbg_cmd *cmd)
+{
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+ struct f_uas *fu = cmd->fu;
+ struct usb_gadget *gadget = fuas_to_gadget(fu);
+ struct uas_stream *stream = cmd->stream;
+
+ if (!gadget->sg_supported) {
+ cmd->data_buf = kmalloc(se_cmd->data_length, GFP_ATOMIC);
+ if (!cmd->data_buf)
+ return -ENOMEM;
+
+ sg_copy_to_buffer(se_cmd->t_data_sg,
+ se_cmd->t_data_nents,
+ cmd->data_buf,
+ se_cmd->data_length);
+
+ stream->req_in->buf = cmd->data_buf;
+ } else {
+ stream->req_in->buf = NULL;
+ stream->req_in->num_sgs = se_cmd->t_data_nents;
+ stream->req_in->sg = se_cmd->t_data_sg;
+ }
+
+ stream->req_in->complete = uasp_status_data_cmpl;
+ stream->req_in->length = se_cmd->data_length;
+ stream->req_in->context = cmd;
+
+ cmd->state = UASP_SEND_STATUS;
+ return 0;
+}
+
+static void uasp_prepare_status(struct usbg_cmd *cmd)
+{
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+ struct sense_iu *iu = &cmd->sense_iu;
+ struct uas_stream *stream = cmd->stream;
+
+ cmd->state = UASP_QUEUE_COMMAND;
+ iu->iu_id = IU_ID_STATUS;
+ iu->tag = cpu_to_be16(cmd->tag);
+
+ /*
+ * iu->status_qual = cpu_to_be16(STATUS QUALIFIER SAM-4. Where R U?);
+ */
+ iu->len = cpu_to_be16(se_cmd->scsi_sense_length);
+ iu->status = se_cmd->scsi_status;
+ stream->req_status->context = cmd;
+ stream->req_status->length = se_cmd->scsi_sense_length + 16;
+ stream->req_status->buf = iu;
+ stream->req_status->complete = uasp_status_data_cmpl;
+}
+
+static void uasp_status_data_cmpl(struct usb_ep *ep, struct usb_request *req)
+{
+ struct usbg_cmd *cmd = req->context;
+ struct uas_stream *stream = cmd->stream;
+ struct f_uas *fu = cmd->fu;
+ int ret;
+
+ if (req->status < 0)
+ goto cleanup;
+
+ switch (cmd->state) {
+ case UASP_SEND_DATA:
+ ret = uasp_prepare_r_request(cmd);
+ if (ret)
+ goto cleanup;
+ ret = usb_ep_queue(fu->ep_in, stream->req_in, GFP_ATOMIC);
+ if (ret)
+ pr_err("%s(%d) => %d\n", __func__, __LINE__, ret);
+ break;
+
+ case UASP_RECEIVE_DATA:
+ ret = usbg_prepare_w_request(cmd, stream->req_out);
+ if (ret)
+ goto cleanup;
+ ret = usb_ep_queue(fu->ep_out, stream->req_out, GFP_ATOMIC);
+ if (ret)
+ pr_err("%s(%d) => %d\n", __func__, __LINE__, ret);
+ break;
+
+ case UASP_SEND_STATUS:
+ uasp_prepare_status(cmd);
+ ret = usb_ep_queue(fu->ep_status, stream->req_status,
+ GFP_ATOMIC);
+ if (ret)
+ pr_err("%s(%d) => %d\n", __func__, __LINE__, ret);
+ break;
+
+ case UASP_QUEUE_COMMAND:
+ usbg_cleanup_cmd(cmd);
+ usb_ep_queue(fu->ep_cmd, fu->cmd.req, GFP_ATOMIC);
+ break;
+
+ default:
+ BUG();
+ }
+ return;
+
+cleanup:
+ usbg_cleanup_cmd(cmd);
+}
+
+static int uasp_send_status_response(struct usbg_cmd *cmd)
+{
+ struct f_uas *fu = cmd->fu;
+ struct uas_stream *stream = cmd->stream;
+ struct sense_iu *iu = &cmd->sense_iu;
+
+ iu->tag = cpu_to_be16(cmd->tag);
+ stream->req_status->complete = uasp_status_data_cmpl;
+ stream->req_status->context = cmd;
+ cmd->fu = fu;
+ uasp_prepare_status(cmd);
+ return usb_ep_queue(fu->ep_status, stream->req_status, GFP_ATOMIC);
+}
+
+static int uasp_send_read_response(struct usbg_cmd *cmd)
+{
+ struct f_uas *fu = cmd->fu;
+ struct uas_stream *stream = cmd->stream;
+ struct sense_iu *iu = &cmd->sense_iu;
+ int ret;
+
+ cmd->fu = fu;
+
+ iu->tag = cpu_to_be16(cmd->tag);
+ if (fu->flags & USBG_USE_STREAMS) {
+
+ ret = uasp_prepare_r_request(cmd);
+ if (ret)
+ goto out;
+ ret = usb_ep_queue(fu->ep_in, stream->req_in, GFP_ATOMIC);
+ if (ret) {
+ pr_err("%s(%d) => %d\n", __func__, __LINE__, ret);
+ kfree(cmd->data_buf);
+ cmd->data_buf = NULL;
+ }
+
+ } else {
+
+ iu->iu_id = IU_ID_READ_READY;
+ iu->tag = cpu_to_be16(cmd->tag);
+
+ stream->req_status->complete = uasp_status_data_cmpl;
+ stream->req_status->context = cmd;
+
+ cmd->state = UASP_SEND_DATA;
+ stream->req_status->buf = iu;
+ stream->req_status->length = sizeof(struct iu);
+
+ ret = usb_ep_queue(fu->ep_status, stream->req_status,
+ GFP_ATOMIC);
+ if (ret)
+ pr_err("%s(%d) => %d\n", __func__, __LINE__, ret);
+ }
+out:
+ return ret;
+}
+
+static int uasp_send_write_request(struct usbg_cmd *cmd)
+{
+ struct f_uas *fu = cmd->fu;
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+ struct uas_stream *stream = cmd->stream;
+ struct sense_iu *iu = &cmd->sense_iu;
+ int ret;
+
+ init_completion(&cmd->write_complete);
+ cmd->fu = fu;
+
+ iu->tag = cpu_to_be16(cmd->tag);
+
+ if (fu->flags & USBG_USE_STREAMS) {
+
+ ret = usbg_prepare_w_request(cmd, stream->req_out);
+ if (ret)
+ goto cleanup;
+ ret = usb_ep_queue(fu->ep_out, stream->req_out, GFP_ATOMIC);
+ if (ret)
+ pr_err("%s(%d)\n", __func__, __LINE__);
+
+ } else {
+
+ iu->iu_id = IU_ID_WRITE_READY;
+ iu->tag = cpu_to_be16(cmd->tag);
+
+ stream->req_status->complete = uasp_status_data_cmpl;
+ stream->req_status->context = cmd;
+
+ cmd->state = UASP_RECEIVE_DATA;
+ stream->req_status->buf = iu;
+ stream->req_status->length = sizeof(struct iu);
+
+ ret = usb_ep_queue(fu->ep_status, stream->req_status,
+ GFP_ATOMIC);
+ if (ret)
+ pr_err("%s(%d)\n", __func__, __LINE__);
+ }
+
+ wait_for_completion(&cmd->write_complete);
+ target_execute_cmd(se_cmd);
+cleanup:
+ return ret;
+}
+
+static int usbg_submit_command(struct f_uas *, void *, unsigned int);
+
+static void uasp_cmd_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_uas *fu = req->context;
+ int ret;
+
+ if (req->status < 0)
+ return;
+
+ ret = usbg_submit_command(fu, req->buf, req->actual);
+ /*
+ * Once we tune for performance enqueue the command req here again so
+ * we can receive a second command while we processing this one. Pay
+ * attention to properly sync STAUS endpoint with DATA IN + OUT so you
+ * don't break HS.
+ */
+ if (!ret)
+ return;
+ usb_ep_queue(fu->ep_cmd, fu->cmd.req, GFP_ATOMIC);
+}
+
+static int uasp_alloc_stream_res(struct f_uas *fu, struct uas_stream *stream)
+{
+ stream->req_in = usb_ep_alloc_request(fu->ep_in, GFP_KERNEL);
+ if (!stream->req_in)
+ goto out;
+
+ stream->req_out = usb_ep_alloc_request(fu->ep_out, GFP_KERNEL);
+ if (!stream->req_out)
+ goto err_out;
+
+ stream->req_status = usb_ep_alloc_request(fu->ep_status, GFP_KERNEL);
+ if (!stream->req_status)
+ goto err_sts;
+
+ return 0;
+err_sts:
+ usb_ep_free_request(fu->ep_status, stream->req_status);
+ stream->req_status = NULL;
+err_out:
+ usb_ep_free_request(fu->ep_out, stream->req_out);
+ stream->req_out = NULL;
+out:
+ return -ENOMEM;
+}
+
+static int uasp_alloc_cmd(struct f_uas *fu)
+{
+ fu->cmd.req = usb_ep_alloc_request(fu->ep_cmd, GFP_KERNEL);
+ if (!fu->cmd.req)
+ goto err;
+
+ fu->cmd.buf = kmalloc(fu->ep_cmd->maxpacket, GFP_KERNEL);
+ if (!fu->cmd.buf)
+ goto err_buf;
+
+ fu->cmd.req->complete = uasp_cmd_complete;
+ fu->cmd.req->buf = fu->cmd.buf;
+ fu->cmd.req->length = fu->ep_cmd->maxpacket;
+ fu->cmd.req->context = fu;
+ return 0;
+
+err_buf:
+ usb_ep_free_request(fu->ep_cmd, fu->cmd.req);
+err:
+ return -ENOMEM;
+}
+
+static void uasp_setup_stream_res(struct f_uas *fu, int max_streams)
+{
+ int i;
+
+ for (i = 0; i < max_streams; i++) {
+ struct uas_stream *s = &fu->stream[i];
+
+ s->req_in->stream_id = i + 1;
+ s->req_out->stream_id = i + 1;
+ s->req_status->stream_id = i + 1;
+ }
+}
+
+static int uasp_prepare_reqs(struct f_uas *fu)
+{
+ int ret;
+ int i;
+ int max_streams;
+
+ if (fu->flags & USBG_USE_STREAMS)
+ max_streams = UASP_SS_EP_COMP_NUM_STREAMS;
+ else
+ max_streams = 1;
+
+ for (i = 0; i < max_streams; i++) {
+ ret = uasp_alloc_stream_res(fu, &fu->stream[i]);
+ if (ret)
+ goto err_cleanup;
+ }
+
+ ret = uasp_alloc_cmd(fu);
+ if (ret)
+ goto err_free_stream;
+ uasp_setup_stream_res(fu, max_streams);
+
+ ret = usb_ep_queue(fu->ep_cmd, fu->cmd.req, GFP_ATOMIC);
+ if (ret)
+ goto err_free_stream;
+
+ return 0;
+
+err_free_stream:
+ uasp_free_cmdreq(fu);
+
+err_cleanup:
+ if (i) {
+ do {
+ uasp_cleanup_one_stream(fu, &fu->stream[i - 1]);
+ i--;
+ } while (i);
+ }
+ pr_err("UASP: endpoint setup failed\n");
+ return ret;
+}
+
+static void uasp_set_alt(struct f_uas *fu)
+{
+ struct usb_function *f = &fu->function;
+ struct usb_gadget *gadget = f->config->cdev->gadget;
+ int ret;
+
+ fu->flags = USBG_IS_UAS;
+
+ if (gadget->speed == USB_SPEED_SUPER)
+ fu->flags |= USBG_USE_STREAMS;
+
+ config_ep_by_speed(gadget, f, fu->ep_in);
+ ret = usb_ep_enable(fu->ep_in);
+ if (ret)
+ goto err_b_in;
+
+ config_ep_by_speed(gadget, f, fu->ep_out);
+ ret = usb_ep_enable(fu->ep_out);
+ if (ret)
+ goto err_b_out;
+
+ config_ep_by_speed(gadget, f, fu->ep_cmd);
+ ret = usb_ep_enable(fu->ep_cmd);
+ if (ret)
+ goto err_cmd;
+ config_ep_by_speed(gadget, f, fu->ep_status);
+ ret = usb_ep_enable(fu->ep_status);
+ if (ret)
+ goto err_status;
+
+ ret = uasp_prepare_reqs(fu);
+ if (ret)
+ goto err_wq;
+ fu->flags |= USBG_ENABLED;
+
+ pr_info("Using the UAS protocol\n");
+ return;
+err_wq:
+ usb_ep_disable(fu->ep_status);
+err_status:
+ usb_ep_disable(fu->ep_cmd);
+err_cmd:
+ usb_ep_disable(fu->ep_out);
+err_b_out:
+ usb_ep_disable(fu->ep_in);
+err_b_in:
+ fu->flags = 0;
+}
+
+static int get_cmd_dir(const unsigned char *cdb)
+{
+ int ret;
+
+ switch (cdb[0]) {
+ case READ_6:
+ case READ_10:
+ case READ_12:
+ case READ_16:
+ case INQUIRY:
+ case MODE_SENSE:
+ case MODE_SENSE_10:
+ case SERVICE_ACTION_IN_16:
+ case MAINTENANCE_IN:
+ case PERSISTENT_RESERVE_IN:
+ case SECURITY_PROTOCOL_IN:
+ case ACCESS_CONTROL_IN:
+ case REPORT_LUNS:
+ case READ_BLOCK_LIMITS:
+ case READ_POSITION:
+ case READ_CAPACITY:
+ case READ_TOC:
+ case READ_FORMAT_CAPACITIES:
+ case REQUEST_SENSE:
+ ret = DMA_FROM_DEVICE;
+ break;
+
+ case WRITE_6:
+ case WRITE_10:
+ case WRITE_12:
+ case WRITE_16:
+ case MODE_SELECT:
+ case MODE_SELECT_10:
+ case WRITE_VERIFY:
+ case WRITE_VERIFY_12:
+ case PERSISTENT_RESERVE_OUT:
+ case MAINTENANCE_OUT:
+ case SECURITY_PROTOCOL_OUT:
+ case ACCESS_CONTROL_OUT:
+ ret = DMA_TO_DEVICE;
+ break;
+ case ALLOW_MEDIUM_REMOVAL:
+ case TEST_UNIT_READY:
+ case SYNCHRONIZE_CACHE:
+ case START_STOP:
+ case ERASE:
+ case REZERO_UNIT:
+ case SEEK_10:
+ case SPACE:
+ case VERIFY:
+ case WRITE_FILEMARKS:
+ ret = DMA_NONE;
+ break;
+ default:
+ pr_warn("target: Unknown data direction for SCSI Opcode "
+ "0x%02x\n", cdb[0]);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static void usbg_data_write_cmpl(struct usb_ep *ep, struct usb_request *req)
+{
+ struct usbg_cmd *cmd = req->context;
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+
+ if (req->status < 0) {
+ pr_err("%s() state %d transfer failed\n", __func__, cmd->state);
+ goto cleanup;
+ }
+
+ if (req->num_sgs == 0) {
+ sg_copy_from_buffer(se_cmd->t_data_sg,
+ se_cmd->t_data_nents,
+ cmd->data_buf,
+ se_cmd->data_length);
+ }
+
+ complete(&cmd->write_complete);
+ return;
+
+cleanup:
+ usbg_cleanup_cmd(cmd);
+}
+
+static int usbg_prepare_w_request(struct usbg_cmd *cmd, struct usb_request *req)
+{
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+ struct f_uas *fu = cmd->fu;
+ struct usb_gadget *gadget = fuas_to_gadget(fu);
+
+ if (!gadget->sg_supported) {
+ cmd->data_buf = kmalloc(se_cmd->data_length, GFP_ATOMIC);
+ if (!cmd->data_buf)
+ return -ENOMEM;
+
+ req->buf = cmd->data_buf;
+ } else {
+ req->buf = NULL;
+ req->num_sgs = se_cmd->t_data_nents;
+ req->sg = se_cmd->t_data_sg;
+ }
+
+ req->complete = usbg_data_write_cmpl;
+ req->length = se_cmd->data_length;
+ req->context = cmd;
+ return 0;
+}
+
+static int usbg_send_status_response(struct se_cmd *se_cmd)
+{
+ struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd,
+ se_cmd);
+ struct f_uas *fu = cmd->fu;
+
+ if (fu->flags & USBG_IS_BOT)
+ return bot_send_status_response(cmd);
+ else
+ return uasp_send_status_response(cmd);
+}
+
+static int usbg_send_write_request(struct se_cmd *se_cmd)
+{
+ struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd,
+ se_cmd);
+ struct f_uas *fu = cmd->fu;
+
+ if (fu->flags & USBG_IS_BOT)
+ return bot_send_write_request(cmd);
+ else
+ return uasp_send_write_request(cmd);
+}
+
+static int usbg_send_read_response(struct se_cmd *se_cmd)
+{
+ struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd,
+ se_cmd);
+ struct f_uas *fu = cmd->fu;
+
+ if (fu->flags & USBG_IS_BOT)
+ return bot_send_read_response(cmd);
+ else
+ return uasp_send_read_response(cmd);
+}
+
+static void usbg_cmd_work(struct work_struct *work)
+{
+ struct usbg_cmd *cmd = container_of(work, struct usbg_cmd, work);
+ struct se_cmd *se_cmd;
+ struct tcm_usbg_nexus *tv_nexus;
+ struct usbg_tpg *tpg;
+ int dir;
+
+ se_cmd = &cmd->se_cmd;
+ tpg = cmd->fu->tpg;
+ tv_nexus = tpg->tpg_nexus;
+ dir = get_cmd_dir(cmd->cmd_buf);
+ if (dir < 0) {
+ transport_init_se_cmd(se_cmd,
+ tv_nexus->tvn_se_sess->se_tpg->se_tpg_tfo,
+ tv_nexus->tvn_se_sess, cmd->data_len, DMA_NONE,
+ cmd->prio_attr, cmd->sense_iu.sense);
+ goto out;
+ }
+
+ if (target_submit_cmd(se_cmd, tv_nexus->tvn_se_sess,
+ cmd->cmd_buf, cmd->sense_iu.sense, cmd->unpacked_lun,
+ 0, cmd->prio_attr, dir, TARGET_SCF_UNKNOWN_SIZE) < 0)
+ goto out;
+
+ return;
+
+out:
+ transport_send_check_condition_and_sense(se_cmd,
+ TCM_UNSUPPORTED_SCSI_OPCODE, 1);
+ usbg_cleanup_cmd(cmd);
+}
+
+static int usbg_submit_command(struct f_uas *fu,
+ void *cmdbuf, unsigned int len)
+{
+ struct command_iu *cmd_iu = cmdbuf;
+ struct usbg_cmd *cmd;
+ struct usbg_tpg *tpg;
+ struct se_cmd *se_cmd;
+ struct tcm_usbg_nexus *tv_nexus;
+ u32 cmd_len;
+ int ret;
+
+ if (cmd_iu->iu_id != IU_ID_COMMAND) {
+ pr_err("Unsupported type %d\n", cmd_iu->iu_id);
+ return -EINVAL;
+ }
+
+ cmd = kzalloc(sizeof *cmd, GFP_ATOMIC);
+ if (!cmd)
+ return -ENOMEM;
+
+ cmd->fu = fu;
+
+ /* XXX until I figure out why I can't free in on complete */
+ kref_init(&cmd->ref);
+ kref_get(&cmd->ref);
+
+ tpg = fu->tpg;
+ cmd_len = (cmd_iu->len & ~0x3) + 16;
+ if (cmd_len > USBG_MAX_CMD)
+ goto err;
+
+ memcpy(cmd->cmd_buf, cmd_iu->cdb, cmd_len);
+
+ cmd->tag = be16_to_cpup(&cmd_iu->tag);
+ if (fu->flags & USBG_USE_STREAMS) {
+ if (cmd->tag > UASP_SS_EP_COMP_NUM_STREAMS)
+ goto err;
+ if (!cmd->tag)
+ cmd->stream = &fu->stream[0];
+ else
+ cmd->stream = &fu->stream[cmd->tag - 1];
+ } else {
+ cmd->stream = &fu->stream[0];
+ }
+
+ tv_nexus = tpg->tpg_nexus;
+ if (!tv_nexus) {
+ pr_err("Missing nexus, ignoring command\n");
+ goto err;
+ }
+
+ switch (cmd_iu->prio_attr & 0x7) {
+ case UAS_HEAD_TAG:
+ cmd->prio_attr = TCM_HEAD_TAG;
+ break;
+ case UAS_ORDERED_TAG:
+ cmd->prio_attr = TCM_ORDERED_TAG;
+ break;
+ case UAS_ACA:
+ cmd->prio_attr = TCM_ACA_TAG;
+ break;
+ default:
+ pr_debug_once("Unsupported prio_attr: %02x.\n",
+ cmd_iu->prio_attr);
+ case UAS_SIMPLE_TAG:
+ cmd->prio_attr = TCM_SIMPLE_TAG;
+ break;
+ }
+
+ se_cmd = &cmd->se_cmd;
+ cmd->unpacked_lun = scsilun_to_int(&cmd_iu->lun);
+
+ INIT_WORK(&cmd->work, usbg_cmd_work);
+ ret = queue_work(tpg->workqueue, &cmd->work);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+err:
+ kfree(cmd);
+ return -EINVAL;
+}
+
+static void bot_cmd_work(struct work_struct *work)
+{
+ struct usbg_cmd *cmd = container_of(work, struct usbg_cmd, work);
+ struct se_cmd *se_cmd;
+ struct tcm_usbg_nexus *tv_nexus;
+ struct usbg_tpg *tpg;
+ int dir;
+
+ se_cmd = &cmd->se_cmd;
+ tpg = cmd->fu->tpg;
+ tv_nexus = tpg->tpg_nexus;
+ dir = get_cmd_dir(cmd->cmd_buf);
+ if (dir < 0) {
+ transport_init_se_cmd(se_cmd,
+ tv_nexus->tvn_se_sess->se_tpg->se_tpg_tfo,
+ tv_nexus->tvn_se_sess, cmd->data_len, DMA_NONE,
+ cmd->prio_attr, cmd->sense_iu.sense);
+ goto out;
+ }
+
+ if (target_submit_cmd(se_cmd, tv_nexus->tvn_se_sess,
+ cmd->cmd_buf, cmd->sense_iu.sense, cmd->unpacked_lun,
+ cmd->data_len, cmd->prio_attr, dir, 0) < 0)
+ goto out;
+
+ return;
+
+out:
+ transport_send_check_condition_and_sense(se_cmd,
+ TCM_UNSUPPORTED_SCSI_OPCODE, 1);
+ usbg_cleanup_cmd(cmd);
+}
+
+static int bot_submit_command(struct f_uas *fu,
+ void *cmdbuf, unsigned int len)
+{
+ struct bulk_cb_wrap *cbw = cmdbuf;
+ struct usbg_cmd *cmd;
+ struct usbg_tpg *tpg;
+ struct se_cmd *se_cmd;
+ struct tcm_usbg_nexus *tv_nexus;
+ u32 cmd_len;
+ int ret;
+
+ if (cbw->Signature != cpu_to_le32(US_BULK_CB_SIGN)) {
+ pr_err("Wrong signature on CBW\n");
+ return -EINVAL;
+ }
+ if (len != 31) {
+ pr_err("Wrong length for CBW\n");
+ return -EINVAL;
+ }
+
+ cmd_len = cbw->Length;
+ if (cmd_len < 1 || cmd_len > 16)
+ return -EINVAL;
+
+ cmd = kzalloc(sizeof *cmd, GFP_ATOMIC);
+ if (!cmd)
+ return -ENOMEM;
+
+ cmd->fu = fu;
+
+ /* XXX until I figure out why I can't free in on complete */
+ kref_init(&cmd->ref);
+ kref_get(&cmd->ref);
+
+ tpg = fu->tpg;
+
+ memcpy(cmd->cmd_buf, cbw->CDB, cmd_len);
+
+ cmd->bot_tag = cbw->Tag;
+
+ tv_nexus = tpg->tpg_nexus;
+ if (!tv_nexus) {
+ pr_err("Missing nexus, ignoring command\n");
+ goto err;
+ }
+
+ cmd->prio_attr = TCM_SIMPLE_TAG;
+ se_cmd = &cmd->se_cmd;
+ cmd->unpacked_lun = cbw->Lun;
+ cmd->is_read = cbw->Flags & US_BULK_FLAG_IN ? 1 : 0;
+ cmd->data_len = le32_to_cpu(cbw->DataTransferLength);
+
+ INIT_WORK(&cmd->work, bot_cmd_work);
+ ret = queue_work(tpg->workqueue, &cmd->work);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+err:
+ kfree(cmd);
+ return -EINVAL;
+}
+
+/* Start fabric.c code */
+
+static int usbg_check_true(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+static int usbg_check_false(struct se_portal_group *se_tpg)
+{
+ return 0;
+}
+
+static char *usbg_get_fabric_name(void)
+{
+ return "usb_gadget";
+}
+
+static u8 usbg_get_fabric_proto_ident(struct se_portal_group *se_tpg)
+{
+ struct usbg_tpg *tpg = container_of(se_tpg,
+ struct usbg_tpg, se_tpg);
+ struct usbg_tport *tport = tpg->tport;
+ u8 proto_id;
+
+ switch (tport->tport_proto_id) {
+ case SCSI_PROTOCOL_SAS:
+ default:
+ proto_id = sas_get_fabric_proto_ident(se_tpg);
+ break;
+ }
+
+ return proto_id;
+}
+
+static char *usbg_get_fabric_wwn(struct se_portal_group *se_tpg)
+{
+ struct usbg_tpg *tpg = container_of(se_tpg,
+ struct usbg_tpg, se_tpg);
+ struct usbg_tport *tport = tpg->tport;
+
+ return &tport->tport_name[0];
+}
+
+static u16 usbg_get_tag(struct se_portal_group *se_tpg)
+{
+ struct usbg_tpg *tpg = container_of(se_tpg,
+ struct usbg_tpg, se_tpg);
+ return tpg->tport_tpgt;
+}
+
+static u32 usbg_get_default_depth(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+static u32 usbg_get_pr_transport_id(
+ struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl,
+ struct t10_pr_registration *pr_reg,
+ int *format_code,
+ unsigned char *buf)
+{
+ struct usbg_tpg *tpg = container_of(se_tpg,
+ struct usbg_tpg, se_tpg);
+ struct usbg_tport *tport = tpg->tport;
+ int ret = 0;
+
+ switch (tport->tport_proto_id) {
+ case SCSI_PROTOCOL_SAS:
+ default:
+ ret = sas_get_pr_transport_id(se_tpg, se_nacl, pr_reg,
+ format_code, buf);
+ break;
+ }
+
+ return ret;
+}
+
+static u32 usbg_get_pr_transport_id_len(
+ struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl,
+ struct t10_pr_registration *pr_reg,
+ int *format_code)
+{
+ struct usbg_tpg *tpg = container_of(se_tpg,
+ struct usbg_tpg, se_tpg);
+ struct usbg_tport *tport = tpg->tport;
+ int ret = 0;
+
+ switch (tport->tport_proto_id) {
+ case SCSI_PROTOCOL_SAS:
+ default:
+ ret = sas_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg,
+ format_code);
+ break;
+ }
+
+ return ret;
+}
+
+static char *usbg_parse_pr_out_transport_id(
+ struct se_portal_group *se_tpg,
+ const char *buf,
+ u32 *out_tid_len,
+ char **port_nexus_ptr)
+{
+ struct usbg_tpg *tpg = container_of(se_tpg,
+ struct usbg_tpg, se_tpg);
+ struct usbg_tport *tport = tpg->tport;
+ char *tid = NULL;
+
+ switch (tport->tport_proto_id) {
+ case SCSI_PROTOCOL_SAS:
+ default:
+ tid = sas_parse_pr_out_transport_id(se_tpg, buf, out_tid_len,
+ port_nexus_ptr);
+ }
+
+ return tid;
+}
+
+static struct se_node_acl *usbg_alloc_fabric_acl(struct se_portal_group *se_tpg)
+{
+ struct usbg_nacl *nacl;
+
+ nacl = kzalloc(sizeof(struct usbg_nacl), GFP_KERNEL);
+ if (!nacl)
+ return NULL;
+
+ return &nacl->se_node_acl;
+}
+
+static void usbg_release_fabric_acl(
+ struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl)
+{
+ struct usbg_nacl *nacl = container_of(se_nacl,
+ struct usbg_nacl, se_node_acl);
+ kfree(nacl);
+}
+
+static u32 usbg_tpg_get_inst_index(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+static void usbg_cmd_release(struct kref *ref)
+{
+ struct usbg_cmd *cmd = container_of(ref, struct usbg_cmd,
+ ref);
+
+ transport_generic_free_cmd(&cmd->se_cmd, 0);
+}
+
+static void usbg_release_cmd(struct se_cmd *se_cmd)
+{
+ struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd,
+ se_cmd);
+ kfree(cmd->data_buf);
+ kfree(cmd);
+ return;
+}
+
+static int usbg_shutdown_session(struct se_session *se_sess)
+{
+ return 0;
+}
+
+static void usbg_close_session(struct se_session *se_sess)
+{
+ return;
+}
+
+static u32 usbg_sess_get_index(struct se_session *se_sess)
+{
+ return 0;
+}
+
+/*
+ * XXX Error recovery: return != 0 if we expect writes. Dunno when that could be
+ */
+static int usbg_write_pending_status(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+static void usbg_set_default_node_attrs(struct se_node_acl *nacl)
+{
+ return;
+}
+
+static u32 usbg_get_task_tag(struct se_cmd *se_cmd)
+{
+ struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd,
+ se_cmd);
+ struct f_uas *fu = cmd->fu;
+
+ if (fu->flags & USBG_IS_BOT)
+ return le32_to_cpu(cmd->bot_tag);
+ else
+ return cmd->tag;
+}
+
+static int usbg_get_cmd_state(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+static void usbg_queue_tm_rsp(struct se_cmd *se_cmd)
+{
+}
+
+static void usbg_aborted_task(struct se_cmd *se_cmd)
+{
+ return;
+}
+
+static const char *usbg_check_wwn(const char *name)
+{
+ const char *n;
+ unsigned int len;
+
+ n = strstr(name, "naa.");
+ if (!n)
+ return NULL;
+ n += 4;
+ len = strlen(n);
+ if (len == 0 || len > USBG_NAMELEN - 1)
+ return NULL;
+ return n;
+}
+
+static struct se_node_acl *usbg_make_nodeacl(
+ struct se_portal_group *se_tpg,
+ struct config_group *group,
+ const char *name)
+{
+ struct se_node_acl *se_nacl, *se_nacl_new;
+ struct usbg_nacl *nacl;
+ u64 wwpn = 0;
+ u32 nexus_depth;
+ const char *wnn_name;
+
+ wnn_name = usbg_check_wwn(name);
+ if (!wnn_name)
+ return ERR_PTR(-EINVAL);
+ se_nacl_new = usbg_alloc_fabric_acl(se_tpg);
+ if (!(se_nacl_new))
+ return ERR_PTR(-ENOMEM);
+
+ nexus_depth = 1;
+ /*
+ * se_nacl_new may be released by core_tpg_add_initiator_node_acl()
+ * when converting a NodeACL from demo mode -> explict
+ */
+ se_nacl = core_tpg_add_initiator_node_acl(se_tpg, se_nacl_new,
+ name, nexus_depth);
+ if (IS_ERR(se_nacl)) {
+ usbg_release_fabric_acl(se_tpg, se_nacl_new);
+ return se_nacl;
+ }
+ /*
+ * Locate our struct usbg_nacl and set the FC Nport WWPN
+ */
+ nacl = container_of(se_nacl, struct usbg_nacl, se_node_acl);
+ nacl->iport_wwpn = wwpn;
+ snprintf(nacl->iport_name, sizeof(nacl->iport_name), "%s", name);
+ return se_nacl;
+}
+
+static void usbg_drop_nodeacl(struct se_node_acl *se_acl)
+{
+ struct usbg_nacl *nacl = container_of(se_acl,
+ struct usbg_nacl, se_node_acl);
+ core_tpg_del_initiator_node_acl(se_acl->se_tpg, se_acl, 1);
+ kfree(nacl);
+}
+
+struct usbg_tpg *the_only_tpg_I_currently_have;
+
+static struct se_portal_group *usbg_make_tpg(
+ struct se_wwn *wwn,
+ struct config_group *group,
+ const char *name)
+{
+ struct usbg_tport *tport = container_of(wwn, struct usbg_tport,
+ tport_wwn);
+ struct usbg_tpg *tpg;
+ unsigned long tpgt;
+ int ret;
+
+ if (strstr(name, "tpgt_") != name)
+ return ERR_PTR(-EINVAL);
+ if (kstrtoul(name + 5, 0, &tpgt) || tpgt > UINT_MAX)
+ return ERR_PTR(-EINVAL);
+ if (the_only_tpg_I_currently_have) {
+ pr_err("Until the gadget framework can't handle multiple\n");
+ pr_err("gadgets, you can't do this here.\n");
+ return ERR_PTR(-EBUSY);
+ }
+
+ tpg = kzalloc(sizeof(struct usbg_tpg), GFP_KERNEL);
+ if (!tpg)
+ return ERR_PTR(-ENOMEM);
+ mutex_init(&tpg->tpg_mutex);
+ atomic_set(&tpg->tpg_port_count, 0);
+ tpg->workqueue = alloc_workqueue("tcm_usb_gadget", 0, 1);
+ if (!tpg->workqueue) {
+ kfree(tpg);
+ return NULL;
+ }
+
+ tpg->tport = tport;
+ tpg->tport_tpgt = tpgt;
+
+ ret = core_tpg_register(&usbg_ops, wwn, &tpg->se_tpg, tpg,
+ TRANSPORT_TPG_TYPE_NORMAL);
+ if (ret < 0) {
+ destroy_workqueue(tpg->workqueue);
+ kfree(tpg);
+ return NULL;
+ }
+ the_only_tpg_I_currently_have = tpg;
+ return &tpg->se_tpg;
+}
+
+static void usbg_drop_tpg(struct se_portal_group *se_tpg)
+{
+ struct usbg_tpg *tpg = container_of(se_tpg,
+ struct usbg_tpg, se_tpg);
+
+ core_tpg_deregister(se_tpg);
+ destroy_workqueue(tpg->workqueue);
+ kfree(tpg);
+ the_only_tpg_I_currently_have = NULL;
+}
+
+static struct se_wwn *usbg_make_tport(
+ struct target_fabric_configfs *tf,
+ struct config_group *group,
+ const char *name)
+{
+ struct usbg_tport *tport;
+ const char *wnn_name;
+ u64 wwpn = 0;
+
+ wnn_name = usbg_check_wwn(name);
+ if (!wnn_name)
+ return ERR_PTR(-EINVAL);
+
+ tport = kzalloc(sizeof(struct usbg_tport), GFP_KERNEL);
+ if (!(tport))
+ return ERR_PTR(-ENOMEM);
+ tport->tport_wwpn = wwpn;
+ snprintf(tport->tport_name, sizeof(tport->tport_name), "%s", wnn_name);
+ return &tport->tport_wwn;
+}
+
+static void usbg_drop_tport(struct se_wwn *wwn)
+{
+ struct usbg_tport *tport = container_of(wwn,
+ struct usbg_tport, tport_wwn);
+ kfree(tport);
+}
+
+/*
+ * If somebody feels like dropping the version property, go ahead.
+ */
+static ssize_t usbg_wwn_show_attr_version(
+ struct target_fabric_configfs *tf,
+ char *page)
+{
+ return sprintf(page, "usb-gadget fabric module\n");
+}
+TF_WWN_ATTR_RO(usbg, version);
+
+static struct configfs_attribute *usbg_wwn_attrs[] = {
+ &usbg_wwn_version.attr,
+ NULL,
+};
+
+static ssize_t tcm_usbg_tpg_show_enable(
+ struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg);
+
+ return snprintf(page, PAGE_SIZE, "%u\n", tpg->gadget_connect);
+}
+
+static int usbg_attach(struct usbg_tpg *);
+static void usbg_detach(struct usbg_tpg *);
+
+static ssize_t tcm_usbg_tpg_store_enable(
+ struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg);
+ unsigned long op;
+ ssize_t ret;
+
+ ret = kstrtoul(page, 0, &op);
+ if (ret < 0)
+ return -EINVAL;
+ if (op > 1)
+ return -EINVAL;
+
+ if (op && tpg->gadget_connect)
+ goto out;
+ if (!op && !tpg->gadget_connect)
+ goto out;
+
+ if (op) {
+ ret = usbg_attach(tpg);
+ if (ret)
+ goto out;
+ } else {
+ usbg_detach(tpg);
+ }
+ tpg->gadget_connect = op;
+out:
+ return count;
+}
+TF_TPG_BASE_ATTR(tcm_usbg, enable, S_IRUGO | S_IWUSR);
+
+static ssize_t tcm_usbg_tpg_show_nexus(
+ struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg);
+ struct tcm_usbg_nexus *tv_nexus;
+ ssize_t ret;
+
+ mutex_lock(&tpg->tpg_mutex);
+ tv_nexus = tpg->tpg_nexus;
+ if (!tv_nexus) {
+ ret = -ENODEV;
+ goto out;
+ }
+ ret = snprintf(page, PAGE_SIZE, "%s\n",
+ tv_nexus->tvn_se_sess->se_node_acl->initiatorname);
+out:
+ mutex_unlock(&tpg->tpg_mutex);
+ return ret;
+}
+
+static int tcm_usbg_make_nexus(struct usbg_tpg *tpg, char *name)
+{
+ struct se_portal_group *se_tpg;
+ struct tcm_usbg_nexus *tv_nexus;
+ int ret;
+
+ mutex_lock(&tpg->tpg_mutex);
+ if (tpg->tpg_nexus) {
+ ret = -EEXIST;
+ pr_debug("tpg->tpg_nexus already exists\n");
+ goto err_unlock;
+ }
+ se_tpg = &tpg->se_tpg;
+
+ ret = -ENOMEM;
+ tv_nexus = kzalloc(sizeof(*tv_nexus), GFP_KERNEL);
+ if (!tv_nexus)
+ goto err_unlock;
+ tv_nexus->tvn_se_sess = transport_init_session(TARGET_PROT_NORMAL);
+ if (IS_ERR(tv_nexus->tvn_se_sess))
+ goto err_free;
+
+ /*
+ * Since we are running in 'demo mode' this call with generate a
+ * struct se_node_acl for the tcm_vhost struct se_portal_group with
+ * the SCSI Initiator port name of the passed configfs group 'name'.
+ */
+ tv_nexus->tvn_se_sess->se_node_acl = core_tpg_check_initiator_node_acl(
+ se_tpg, name);
+ if (!tv_nexus->tvn_se_sess->se_node_acl) {
+ pr_debug("core_tpg_check_initiator_node_acl() failed"
+ " for %s\n", name);
+ goto err_session;
+ }
+ /*
+ * Now register the TCM vHost virtual I_T Nexus as active.
+ */
+ transport_register_session(se_tpg, tv_nexus->tvn_se_sess->se_node_acl,
+ tv_nexus->tvn_se_sess, tv_nexus);
+ tpg->tpg_nexus = tv_nexus;
+ mutex_unlock(&tpg->tpg_mutex);
+ return 0;
+
+err_session:
+ transport_free_session(tv_nexus->tvn_se_sess);
+err_free:
+ kfree(tv_nexus);
+err_unlock:
+ mutex_unlock(&tpg->tpg_mutex);
+ return ret;
+}
+
+static int tcm_usbg_drop_nexus(struct usbg_tpg *tpg)
+{
+ struct se_session *se_sess;
+ struct tcm_usbg_nexus *tv_nexus;
+ int ret = -ENODEV;
+
+ mutex_lock(&tpg->tpg_mutex);
+ tv_nexus = tpg->tpg_nexus;
+ if (!tv_nexus)
+ goto out;
+
+ se_sess = tv_nexus->tvn_se_sess;
+ if (!se_sess)
+ goto out;
+
+ if (atomic_read(&tpg->tpg_port_count)) {
+ ret = -EPERM;
+ pr_err("Unable to remove Host I_T Nexus with"
+ " active TPG port count: %d\n",
+ atomic_read(&tpg->tpg_port_count));
+ goto out;
+ }
+
+ pr_debug("Removing I_T Nexus to Initiator Port: %s\n",
+ tv_nexus->tvn_se_sess->se_node_acl->initiatorname);
+ /*
+ * Release the SCSI I_T Nexus to the emulated vHost Target Port
+ */
+ transport_deregister_session(tv_nexus->tvn_se_sess);
+ tpg->tpg_nexus = NULL;
+
+ kfree(tv_nexus);
+ ret = 0;
+out:
+ mutex_unlock(&tpg->tpg_mutex);
+ return ret;
+}
+
+static ssize_t tcm_usbg_tpg_store_nexus(
+ struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg);
+ unsigned char i_port[USBG_NAMELEN], *ptr;
+ int ret;
+
+ if (!strncmp(page, "NULL", 4)) {
+ ret = tcm_usbg_drop_nexus(tpg);
+ return (!ret) ? count : ret;
+ }
+ if (strlen(page) >= USBG_NAMELEN) {
+ pr_err("Emulated NAA Sas Address: %s, exceeds"
+ " max: %d\n", page, USBG_NAMELEN);
+ return -EINVAL;
+ }
+ snprintf(i_port, USBG_NAMELEN, "%s", page);
+
+ ptr = strstr(i_port, "naa.");
+ if (!ptr) {
+ pr_err("Missing 'naa.' prefix\n");
+ return -EINVAL;
+ }
+
+ if (i_port[strlen(i_port) - 1] == '\n')
+ i_port[strlen(i_port) - 1] = '\0';
+
+ ret = tcm_usbg_make_nexus(tpg, &i_port[4]);
+ if (ret < 0)
+ return ret;
+ return count;
+}
+TF_TPG_BASE_ATTR(tcm_usbg, nexus, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *usbg_base_attrs[] = {
+ &tcm_usbg_tpg_enable.attr,
+ &tcm_usbg_tpg_nexus.attr,
+ NULL,
+};
+
+static int usbg_port_link(struct se_portal_group *se_tpg, struct se_lun *lun)
+{
+ struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg);
+
+ atomic_inc(&tpg->tpg_port_count);
+ smp_mb__after_atomic();
+ return 0;
+}
+
+static void usbg_port_unlink(struct se_portal_group *se_tpg,
+ struct se_lun *se_lun)
+{
+ struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg);
+
+ atomic_dec(&tpg->tpg_port_count);
+ smp_mb__after_atomic();
+}
+
+static int usbg_check_stop_free(struct se_cmd *se_cmd)
+{
+ struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd,
+ se_cmd);
+
+ kref_put(&cmd->ref, usbg_cmd_release);
+ return 1;
+}
+
+static const struct target_core_fabric_ops usbg_ops = {
+ .module = THIS_MODULE,
+ .name = "usb_gadget",
+ .get_fabric_name = usbg_get_fabric_name,
+ .get_fabric_proto_ident = usbg_get_fabric_proto_ident,
+ .tpg_get_wwn = usbg_get_fabric_wwn,
+ .tpg_get_tag = usbg_get_tag,
+ .tpg_get_default_depth = usbg_get_default_depth,
+ .tpg_get_pr_transport_id = usbg_get_pr_transport_id,
+ .tpg_get_pr_transport_id_len = usbg_get_pr_transport_id_len,
+ .tpg_parse_pr_out_transport_id = usbg_parse_pr_out_transport_id,
+ .tpg_check_demo_mode = usbg_check_true,
+ .tpg_check_demo_mode_cache = usbg_check_false,
+ .tpg_check_demo_mode_write_protect = usbg_check_false,
+ .tpg_check_prod_mode_write_protect = usbg_check_false,
+ .tpg_alloc_fabric_acl = usbg_alloc_fabric_acl,
+ .tpg_release_fabric_acl = usbg_release_fabric_acl,
+ .tpg_get_inst_index = usbg_tpg_get_inst_index,
+ .release_cmd = usbg_release_cmd,
+ .shutdown_session = usbg_shutdown_session,
+ .close_session = usbg_close_session,
+ .sess_get_index = usbg_sess_get_index,
+ .sess_get_initiator_sid = NULL,
+ .write_pending = usbg_send_write_request,
+ .write_pending_status = usbg_write_pending_status,
+ .set_default_node_attributes = usbg_set_default_node_attrs,
+ .get_task_tag = usbg_get_task_tag,
+ .get_cmd_state = usbg_get_cmd_state,
+ .queue_data_in = usbg_send_read_response,
+ .queue_status = usbg_send_status_response,
+ .queue_tm_rsp = usbg_queue_tm_rsp,
+ .aborted_task = usbg_aborted_task,
+ .check_stop_free = usbg_check_stop_free,
+
+ .fabric_make_wwn = usbg_make_tport,
+ .fabric_drop_wwn = usbg_drop_tport,
+ .fabric_make_tpg = usbg_make_tpg,
+ .fabric_drop_tpg = usbg_drop_tpg,
+ .fabric_post_link = usbg_port_link,
+ .fabric_pre_unlink = usbg_port_unlink,
+ .fabric_make_np = NULL,
+ .fabric_drop_np = NULL,
+ .fabric_make_nodeacl = usbg_make_nodeacl,
+ .fabric_drop_nodeacl = usbg_drop_nodeacl,
+
+ .tfc_wwn_attrs = usbg_wwn_attrs,
+ .tfc_tpg_base_attrs = usbg_base_attrs,
+};
+
+/* Start gadget.c code */
+
+static struct usb_interface_descriptor bot_intf_desc = {
+ .bLength = sizeof(bot_intf_desc),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bNumEndpoints = 2,
+ .bAlternateSetting = USB_G_ALT_INT_BBB,
+ .bInterfaceClass = USB_CLASS_MASS_STORAGE,
+ .bInterfaceSubClass = USB_SC_SCSI,
+ .bInterfaceProtocol = USB_PR_BULK,
+};
+
+static struct usb_interface_descriptor uasp_intf_desc = {
+ .bLength = sizeof(uasp_intf_desc),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bNumEndpoints = 4,
+ .bAlternateSetting = USB_G_ALT_INT_UAS,
+ .bInterfaceClass = USB_CLASS_MASS_STORAGE,
+ .bInterfaceSubClass = USB_SC_SCSI,
+ .bInterfaceProtocol = USB_PR_UAS,
+};
+
+static struct usb_endpoint_descriptor uasp_bi_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 uasp_fs_bi_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_pipe_usage_descriptor uasp_bi_pipe_desc = {
+ .bLength = sizeof(uasp_bi_pipe_desc),
+ .bDescriptorType = USB_DT_PIPE_USAGE,
+ .bPipeID = DATA_IN_PIPE_ID,
+};
+
+static struct usb_endpoint_descriptor uasp_ss_bi_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_ss_ep_comp_descriptor uasp_bi_ep_comp_desc = {
+ .bLength = sizeof(uasp_bi_ep_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ .bMaxBurst = 0,
+ .bmAttributes = UASP_SS_EP_COMP_LOG_STREAMS,
+ .wBytesPerInterval = 0,
+};
+
+static struct usb_ss_ep_comp_descriptor bot_bi_ep_comp_desc = {
+ .bLength = sizeof(bot_bi_ep_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ .bMaxBurst = 0,
+};
+
+static struct usb_endpoint_descriptor uasp_bo_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 uasp_fs_bo_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_pipe_usage_descriptor uasp_bo_pipe_desc = {
+ .bLength = sizeof(uasp_bo_pipe_desc),
+ .bDescriptorType = USB_DT_PIPE_USAGE,
+ .bPipeID = DATA_OUT_PIPE_ID,
+};
+
+static struct usb_endpoint_descriptor uasp_ss_bo_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(0x400),
+};
+
+static struct usb_ss_ep_comp_descriptor uasp_bo_ep_comp_desc = {
+ .bLength = sizeof(uasp_bo_ep_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ .bmAttributes = UASP_SS_EP_COMP_LOG_STREAMS,
+};
+
+static struct usb_ss_ep_comp_descriptor bot_bo_ep_comp_desc = {
+ .bLength = sizeof(bot_bo_ep_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_endpoint_descriptor uasp_status_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 uasp_fs_status_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_pipe_usage_descriptor uasp_status_pipe_desc = {
+ .bLength = sizeof(uasp_status_pipe_desc),
+ .bDescriptorType = USB_DT_PIPE_USAGE,
+ .bPipeID = STATUS_PIPE_ID,
+};
+
+static struct usb_endpoint_descriptor uasp_ss_status_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_ss_ep_comp_descriptor uasp_status_in_ep_comp_desc = {
+ .bLength = sizeof(uasp_status_in_ep_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ .bmAttributes = UASP_SS_EP_COMP_LOG_STREAMS,
+};
+
+static struct usb_endpoint_descriptor uasp_cmd_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 uasp_fs_cmd_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_pipe_usage_descriptor uasp_cmd_pipe_desc = {
+ .bLength = sizeof(uasp_cmd_pipe_desc),
+ .bDescriptorType = USB_DT_PIPE_USAGE,
+ .bPipeID = CMD_PIPE_ID,
+};
+
+static struct usb_endpoint_descriptor uasp_ss_cmd_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 uasp_cmd_comp_desc = {
+ .bLength = sizeof(uasp_cmd_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_descriptor_header *uasp_fs_function_desc[] = {
+ (struct usb_descriptor_header *) &bot_intf_desc,
+ (struct usb_descriptor_header *) &uasp_fs_bi_desc,
+ (struct usb_descriptor_header *) &uasp_fs_bo_desc,
+
+ (struct usb_descriptor_header *) &uasp_intf_desc,
+ (struct usb_descriptor_header *) &uasp_fs_bi_desc,
+ (struct usb_descriptor_header *) &uasp_bi_pipe_desc,
+ (struct usb_descriptor_header *) &uasp_fs_bo_desc,
+ (struct usb_descriptor_header *) &uasp_bo_pipe_desc,
+ (struct usb_descriptor_header *) &uasp_fs_status_desc,
+ (struct usb_descriptor_header *) &uasp_status_pipe_desc,
+ (struct usb_descriptor_header *) &uasp_fs_cmd_desc,
+ (struct usb_descriptor_header *) &uasp_cmd_pipe_desc,
+ NULL,
+};
+
+static struct usb_descriptor_header *uasp_hs_function_desc[] = {
+ (struct usb_descriptor_header *) &bot_intf_desc,
+ (struct usb_descriptor_header *) &uasp_bi_desc,
+ (struct usb_descriptor_header *) &uasp_bo_desc,
+
+ (struct usb_descriptor_header *) &uasp_intf_desc,
+ (struct usb_descriptor_header *) &uasp_bi_desc,
+ (struct usb_descriptor_header *) &uasp_bi_pipe_desc,
+ (struct usb_descriptor_header *) &uasp_bo_desc,
+ (struct usb_descriptor_header *) &uasp_bo_pipe_desc,
+ (struct usb_descriptor_header *) &uasp_status_desc,
+ (struct usb_descriptor_header *) &uasp_status_pipe_desc,
+ (struct usb_descriptor_header *) &uasp_cmd_desc,
+ (struct usb_descriptor_header *) &uasp_cmd_pipe_desc,
+ NULL,
+};
+
+static struct usb_descriptor_header *uasp_ss_function_desc[] = {
+ (struct usb_descriptor_header *) &bot_intf_desc,
+ (struct usb_descriptor_header *) &uasp_ss_bi_desc,
+ (struct usb_descriptor_header *) &bot_bi_ep_comp_desc,
+ (struct usb_descriptor_header *) &uasp_ss_bo_desc,
+ (struct usb_descriptor_header *) &bot_bo_ep_comp_desc,
+
+ (struct usb_descriptor_header *) &uasp_intf_desc,
+ (struct usb_descriptor_header *) &uasp_ss_bi_desc,
+ (struct usb_descriptor_header *) &uasp_bi_ep_comp_desc,
+ (struct usb_descriptor_header *) &uasp_bi_pipe_desc,
+ (struct usb_descriptor_header *) &uasp_ss_bo_desc,
+ (struct usb_descriptor_header *) &uasp_bo_ep_comp_desc,
+ (struct usb_descriptor_header *) &uasp_bo_pipe_desc,
+ (struct usb_descriptor_header *) &uasp_ss_status_desc,
+ (struct usb_descriptor_header *) &uasp_status_in_ep_comp_desc,
+ (struct usb_descriptor_header *) &uasp_status_pipe_desc,
+ (struct usb_descriptor_header *) &uasp_ss_cmd_desc,
+ (struct usb_descriptor_header *) &uasp_cmd_comp_desc,
+ (struct usb_descriptor_header *) &uasp_cmd_pipe_desc,
+ NULL,
+};
+
+#define UAS_VENDOR_ID 0x0525 /* NetChip */
+#define UAS_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */
+
+static struct usb_device_descriptor usbg_device_desc = {
+ .bLength = sizeof(usbg_device_desc),
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_PER_INTERFACE,
+ .idVendor = cpu_to_le16(UAS_VENDOR_ID),
+ .idProduct = cpu_to_le16(UAS_PRODUCT_ID),
+ .bNumConfigurations = 1,
+};
+
+static struct usb_string usbg_us_strings[] = {
+ [USB_GADGET_MANUFACTURER_IDX].s = "Target Manufactor",
+ [USB_GADGET_PRODUCT_IDX].s = "Target Product",
+ [USB_GADGET_SERIAL_IDX].s = "000000000001",
+ [USB_G_STR_CONFIG].s = "default config",
+ [USB_G_STR_INT_UAS].s = "USB Attached SCSI",
+ [USB_G_STR_INT_BBB].s = "Bulk Only Transport",
+ { },
+};
+
+static struct usb_gadget_strings usbg_stringtab = {
+ .language = 0x0409,
+ .strings = usbg_us_strings,
+};
+
+static struct usb_gadget_strings *usbg_strings[] = {
+ &usbg_stringtab,
+ NULL,
+};
+
+static int guas_unbind(struct usb_composite_dev *cdev)
+{
+ return 0;
+}
+
+static struct usb_configuration usbg_config_driver = {
+ .label = "Linux Target",
+ .bConfigurationValue = 1,
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+};
+
+static void give_back_ep(struct usb_ep **pep)
+{
+ struct usb_ep *ep = *pep;
+ if (!ep)
+ return;
+ ep->driver_data = NULL;
+}
+
+static int usbg_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct f_uas *fu = to_f_uas(f);
+ struct usb_gadget *gadget = c->cdev->gadget;
+ struct usb_ep *ep;
+ int iface;
+ int ret;
+
+ iface = usb_interface_id(c, f);
+ if (iface < 0)
+ return iface;
+
+ bot_intf_desc.bInterfaceNumber = iface;
+ uasp_intf_desc.bInterfaceNumber = iface;
+ fu->iface = iface;
+ ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_bi_desc,
+ &uasp_bi_ep_comp_desc);
+ if (!ep)
+ goto ep_fail;
+
+ ep->driver_data = fu;
+ fu->ep_in = ep;
+
+ ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_bo_desc,
+ &uasp_bo_ep_comp_desc);
+ if (!ep)
+ goto ep_fail;
+ ep->driver_data = fu;
+ fu->ep_out = ep;
+
+ ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_status_desc,
+ &uasp_status_in_ep_comp_desc);
+ if (!ep)
+ goto ep_fail;
+ ep->driver_data = fu;
+ fu->ep_status = ep;
+
+ ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_cmd_desc,
+ &uasp_cmd_comp_desc);
+ if (!ep)
+ goto ep_fail;
+ ep->driver_data = fu;
+ fu->ep_cmd = ep;
+
+ /* Assume endpoint addresses are the same for both speeds */
+ uasp_bi_desc.bEndpointAddress = uasp_ss_bi_desc.bEndpointAddress;
+ uasp_bo_desc.bEndpointAddress = uasp_ss_bo_desc.bEndpointAddress;
+ uasp_status_desc.bEndpointAddress =
+ uasp_ss_status_desc.bEndpointAddress;
+ uasp_cmd_desc.bEndpointAddress = uasp_ss_cmd_desc.bEndpointAddress;
+
+ uasp_fs_bi_desc.bEndpointAddress = uasp_ss_bi_desc.bEndpointAddress;
+ uasp_fs_bo_desc.bEndpointAddress = uasp_ss_bo_desc.bEndpointAddress;
+ uasp_fs_status_desc.bEndpointAddress =
+ uasp_ss_status_desc.bEndpointAddress;
+ uasp_fs_cmd_desc.bEndpointAddress = uasp_ss_cmd_desc.bEndpointAddress;
+
+ ret = usb_assign_descriptors(f, uasp_fs_function_desc,
+ uasp_hs_function_desc, uasp_ss_function_desc);
+ if (ret)
+ goto ep_fail;
+
+ return 0;
+ep_fail:
+ pr_err("Can't claim all required eps\n");
+
+ give_back_ep(&fu->ep_in);
+ give_back_ep(&fu->ep_out);
+ give_back_ep(&fu->ep_status);
+ give_back_ep(&fu->ep_cmd);
+ return -ENOTSUPP;
+}
+
+static void usbg_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct f_uas *fu = to_f_uas(f);
+
+ usb_free_all_descriptors(f);
+ kfree(fu);
+}
+
+struct guas_setup_wq {
+ struct work_struct work;
+ struct f_uas *fu;
+ unsigned int alt;
+};
+
+static void usbg_delayed_set_alt(struct work_struct *wq)
+{
+ struct guas_setup_wq *work = container_of(wq, struct guas_setup_wq,
+ work);
+ struct f_uas *fu = work->fu;
+ int alt = work->alt;
+
+ kfree(work);
+
+ if (fu->flags & USBG_IS_BOT)
+ bot_cleanup_old_alt(fu);
+ if (fu->flags & USBG_IS_UAS)
+ uasp_cleanup_old_alt(fu);
+
+ if (alt == USB_G_ALT_INT_BBB)
+ bot_set_alt(fu);
+ else if (alt == USB_G_ALT_INT_UAS)
+ uasp_set_alt(fu);
+ usb_composite_setup_continue(fu->function.config->cdev);
+}
+
+static int usbg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+ struct f_uas *fu = to_f_uas(f);
+
+ if ((alt == USB_G_ALT_INT_BBB) || (alt == USB_G_ALT_INT_UAS)) {
+ struct guas_setup_wq *work;
+
+ work = kmalloc(sizeof(*work), GFP_ATOMIC);
+ if (!work)
+ return -ENOMEM;
+ INIT_WORK(&work->work, usbg_delayed_set_alt);
+ work->fu = fu;
+ work->alt = alt;
+ schedule_work(&work->work);
+ return USB_GADGET_DELAYED_STATUS;
+ }
+ return -EOPNOTSUPP;
+}
+
+static void usbg_disable(struct usb_function *f)
+{
+ struct f_uas *fu = to_f_uas(f);
+
+ if (fu->flags & USBG_IS_UAS)
+ uasp_cleanup_old_alt(fu);
+ else if (fu->flags & USBG_IS_BOT)
+ bot_cleanup_old_alt(fu);
+ fu->flags = 0;
+}
+
+static int usbg_setup(struct usb_function *f,
+ const struct usb_ctrlrequest *ctrl)
+{
+ struct f_uas *fu = to_f_uas(f);
+
+ if (!(fu->flags & USBG_IS_BOT))
+ return -EOPNOTSUPP;
+
+ return usbg_bot_setup(f, ctrl);
+}
+
+static int usbg_cfg_bind(struct usb_configuration *c)
+{
+ struct f_uas *fu;
+ int ret;
+
+ fu = kzalloc(sizeof(*fu), GFP_KERNEL);
+ if (!fu)
+ return -ENOMEM;
+ fu->function.name = "Target Function";
+ fu->function.bind = usbg_bind;
+ fu->function.unbind = usbg_unbind;
+ fu->function.set_alt = usbg_set_alt;
+ fu->function.setup = usbg_setup;
+ fu->function.disable = usbg_disable;
+ fu->tpg = the_only_tpg_I_currently_have;
+
+ bot_intf_desc.iInterface = usbg_us_strings[USB_G_STR_INT_BBB].id;
+ uasp_intf_desc.iInterface = usbg_us_strings[USB_G_STR_INT_UAS].id;
+
+ ret = usb_add_function(c, &fu->function);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ kfree(fu);
+ return ret;
+}
+
+static int usb_target_bind(struct usb_composite_dev *cdev)
+{
+ int ret;
+
+ ret = usb_string_ids_tab(cdev, usbg_us_strings);
+ if (ret)
+ return ret;
+
+ usbg_device_desc.iManufacturer =
+ usbg_us_strings[USB_GADGET_MANUFACTURER_IDX].id;
+ usbg_device_desc.iProduct = usbg_us_strings[USB_GADGET_PRODUCT_IDX].id;
+ usbg_device_desc.iSerialNumber =
+ usbg_us_strings[USB_GADGET_SERIAL_IDX].id;
+ usbg_config_driver.iConfiguration =
+ usbg_us_strings[USB_G_STR_CONFIG].id;
+
+ ret = usb_add_config(cdev, &usbg_config_driver,
+ usbg_cfg_bind);
+ if (ret)
+ return ret;
+ usb_composite_overwrite_options(cdev, &coverwrite);
+ return 0;
+}
+
+static struct usb_composite_driver usbg_driver = {
+ .name = "g_target",
+ .dev = &usbg_device_desc,
+ .strings = usbg_strings,
+ .max_speed = USB_SPEED_SUPER,
+ .bind = usb_target_bind,
+ .unbind = guas_unbind,
+};
+
+static int usbg_attach(struct usbg_tpg *tpg)
+{
+ return usb_composite_probe(&usbg_driver);
+}
+
+static void usbg_detach(struct usbg_tpg *tpg)
+{
+ usb_composite_unregister(&usbg_driver);
+}
+
+static int __init usb_target_gadget_init(void)
+{
+ return target_register_template(&usbg_ops);
+}
+module_init(usb_target_gadget_init);
+
+static void __exit usb_target_gadget_exit(void)
+{
+ target_unregister_template(&usbg_ops);
+}
+module_exit(usb_target_gadget_exit);
+
+MODULE_AUTHOR("Sebastian Andrzej Siewior <bigeasy@linutronix.de>");
+MODULE_DESCRIPTION("usb-gadget fabric");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/legacy/tcm_usb_gadget.h b/drivers/usb/gadget/legacy/tcm_usb_gadget.h
new file mode 100644
index 000000000..828921992
--- /dev/null
+++ b/drivers/usb/gadget/legacy/tcm_usb_gadget.h
@@ -0,0 +1,145 @@
+#ifndef __TARGET_USB_GADGET_H__
+#define __TARGET_USB_GADGET_H__
+
+#include <linux/kref.h>
+/* #include <linux/usb/uas.h> */
+#include <linux/usb/composite.h>
+#include <linux/usb/uas.h>
+#include <linux/usb/storage.h>
+#include <scsi/scsi.h>
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+
+#define USBG_NAMELEN 32
+
+#define fuas_to_gadget(f) (f->function.config->cdev->gadget)
+#define UASP_SS_EP_COMP_LOG_STREAMS 4
+#define UASP_SS_EP_COMP_NUM_STREAMS (1 << UASP_SS_EP_COMP_LOG_STREAMS)
+
+enum {
+ USB_G_STR_CONFIG = USB_GADGET_FIRST_AVAIL_IDX,
+ USB_G_STR_INT_UAS,
+ USB_G_STR_INT_BBB,
+};
+
+#define USB_G_ALT_INT_BBB 0
+#define USB_G_ALT_INT_UAS 1
+
+struct usbg_nacl {
+ /* Binary World Wide unique Port Name for SAS Initiator port */
+ u64 iport_wwpn;
+ /* ASCII formatted WWPN for Sas Initiator port */
+ char iport_name[USBG_NAMELEN];
+ /* Returned by usbg_make_nodeacl() */
+ struct se_node_acl se_node_acl;
+};
+
+struct tcm_usbg_nexus {
+ struct se_session *tvn_se_sess;
+};
+
+struct usbg_tpg {
+ struct mutex tpg_mutex;
+ /* SAS port target portal group tag for TCM */
+ u16 tport_tpgt;
+ /* Pointer back to usbg_tport */
+ struct usbg_tport *tport;
+ struct workqueue_struct *workqueue;
+ /* Returned by usbg_make_tpg() */
+ struct se_portal_group se_tpg;
+ u32 gadget_connect;
+ struct tcm_usbg_nexus *tpg_nexus;
+ atomic_t tpg_port_count;
+};
+
+struct usbg_tport {
+ /* SCSI protocol the tport is providing */
+ u8 tport_proto_id;
+ /* Binary World Wide unique Port Name for SAS Target port */
+ u64 tport_wwpn;
+ /* ASCII formatted WWPN for SAS Target port */
+ char tport_name[USBG_NAMELEN];
+ /* Returned by usbg_make_tport() */
+ struct se_wwn tport_wwn;
+};
+
+enum uas_state {
+ UASP_SEND_DATA,
+ UASP_RECEIVE_DATA,
+ UASP_SEND_STATUS,
+ UASP_QUEUE_COMMAND,
+};
+
+#define USBG_MAX_CMD 64
+struct usbg_cmd {
+ /* common */
+ u8 cmd_buf[USBG_MAX_CMD];
+ u32 data_len;
+ struct work_struct work;
+ int unpacked_lun;
+ struct se_cmd se_cmd;
+ void *data_buf; /* used if no sg support available */
+ struct f_uas *fu;
+ struct completion write_complete;
+ struct kref ref;
+
+ /* UAS only */
+ u16 tag;
+ u16 prio_attr;
+ struct sense_iu sense_iu;
+ enum uas_state state;
+ struct uas_stream *stream;
+
+ /* BOT only */
+ __le32 bot_tag;
+ unsigned int csw_code;
+ unsigned is_read:1;
+
+};
+
+struct uas_stream {
+ struct usb_request *req_in;
+ struct usb_request *req_out;
+ struct usb_request *req_status;
+};
+
+struct usbg_cdb {
+ struct usb_request *req;
+ void *buf;
+};
+
+struct bot_status {
+ struct usb_request *req;
+ struct bulk_cs_wrap csw;
+};
+
+struct f_uas {
+ struct usbg_tpg *tpg;
+ struct usb_function function;
+ u16 iface;
+
+ u32 flags;
+#define USBG_ENABLED (1 << 0)
+#define USBG_IS_UAS (1 << 1)
+#define USBG_USE_STREAMS (1 << 2)
+#define USBG_IS_BOT (1 << 3)
+#define USBG_BOT_CMD_PEND (1 << 4)
+
+ struct usbg_cdb cmd;
+ struct usb_ep *ep_in;
+ struct usb_ep *ep_out;
+
+ /* UAS */
+ struct usb_ep *ep_status;
+ struct usb_ep *ep_cmd;
+ struct uas_stream stream[UASP_SS_EP_COMP_NUM_STREAMS];
+
+ /* BOT */
+ struct bot_status bot_status;
+ struct usb_request *bot_req_in;
+ struct usb_request *bot_req_out;
+};
+
+extern struct usbg_tpg *the_only_tpg_I_currently_have;
+
+#endif
diff --git a/drivers/usb/gadget/legacy/webcam.c b/drivers/usb/gadget/legacy/webcam.c
new file mode 100644
index 000000000..72c976bf3
--- /dev/null
+++ b/drivers/usb/gadget/legacy/webcam.c
@@ -0,0 +1,440 @@
+/*
+ * webcam.c -- USB webcam 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/module.h>
+#include <linux/usb/video.h>
+
+#include "u_uvc.h"
+
+USB_GADGET_COMPOSITE_OPTIONS();
+
+/*-------------------------------------------------------------------------*/
+
+/* 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)");
+
+static unsigned int trace;
+module_param(trace, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(trace, "Trace level bitmask");
+/* --------------------------------------------------------------------------
+ * Device descriptor
+ */
+
+#define WEBCAM_VENDOR_ID 0x1d6b /* Linux Foundation */
+#define WEBCAM_PRODUCT_ID 0x0102 /* Webcam A/V gadget */
+#define WEBCAM_DEVICE_BCD 0x0010 /* 0.10 */
+
+static char webcam_vendor_label[] = "Linux Foundation";
+static char webcam_product_label[] = "Webcam gadget";
+static char webcam_config_label[] = "Video";
+
+/* string IDs are assigned dynamically */
+
+#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX
+
+static struct usb_string webcam_strings[] = {
+ [USB_GADGET_MANUFACTURER_IDX].s = webcam_vendor_label,
+ [USB_GADGET_PRODUCT_IDX].s = webcam_product_label,
+ [USB_GADGET_SERIAL_IDX].s = "",
+ [STRING_DESCRIPTION_IDX].s = webcam_config_label,
+ { }
+};
+
+static struct usb_gadget_strings webcam_stringtab = {
+ .language = 0x0409, /* en-us */
+ .strings = webcam_strings,
+};
+
+static struct usb_gadget_strings *webcam_device_strings[] = {
+ &webcam_stringtab,
+ NULL,
+};
+
+static struct usb_function_instance *fi_uvc;
+static struct usb_function *f_uvc;
+
+static struct usb_device_descriptor webcam_device_descriptor = {
+ .bLength = USB_DT_DEVICE_SIZE,
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_MISC,
+ .bDeviceSubClass = 0x02,
+ .bDeviceProtocol = 0x01,
+ .bMaxPacketSize0 = 0, /* dynamic */
+ .idVendor = cpu_to_le16(WEBCAM_VENDOR_ID),
+ .idProduct = cpu_to_le16(WEBCAM_PRODUCT_ID),
+ .bcdDevice = cpu_to_le16(WEBCAM_DEVICE_BCD),
+ .iManufacturer = 0, /* dynamic */
+ .iProduct = 0, /* dynamic */
+ .iSerialNumber = 0, /* dynamic */
+ .bNumConfigurations = 0, /* dynamic */
+};
+
+DECLARE_UVC_HEADER_DESCRIPTOR(1);
+
+static const struct UVC_HEADER_DESCRIPTOR(1) uvc_control_header = {
+ .bLength = UVC_DT_HEADER_SIZE(1),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = UVC_VC_HEADER,
+ .bcdUVC = cpu_to_le16(0x0100),
+ .wTotalLength = 0, /* dynamic */
+ .dwClockFrequency = cpu_to_le32(48000000),
+ .bInCollection = 0, /* dynamic */
+ .baInterfaceNr[0] = 0, /* dynamic */
+};
+
+static const struct uvc_camera_terminal_descriptor uvc_camera_terminal = {
+ .bLength = UVC_DT_CAMERA_TERMINAL_SIZE(3),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = UVC_VC_INPUT_TERMINAL,
+ .bTerminalID = 1,
+ .wTerminalType = cpu_to_le16(0x0201),
+ .bAssocTerminal = 0,
+ .iTerminal = 0,
+ .wObjectiveFocalLengthMin = cpu_to_le16(0),
+ .wObjectiveFocalLengthMax = cpu_to_le16(0),
+ .wOcularFocalLength = cpu_to_le16(0),
+ .bControlSize = 3,
+ .bmControls[0] = 2,
+ .bmControls[1] = 0,
+ .bmControls[2] = 0,
+};
+
+static const struct uvc_processing_unit_descriptor uvc_processing = {
+ .bLength = UVC_DT_PROCESSING_UNIT_SIZE(2),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = UVC_VC_PROCESSING_UNIT,
+ .bUnitID = 2,
+ .bSourceID = 1,
+ .wMaxMultiplier = cpu_to_le16(16*1024),
+ .bControlSize = 2,
+ .bmControls[0] = 1,
+ .bmControls[1] = 0,
+ .iProcessing = 0,
+};
+
+static const struct uvc_output_terminal_descriptor uvc_output_terminal = {
+ .bLength = UVC_DT_OUTPUT_TERMINAL_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = UVC_VC_OUTPUT_TERMINAL,
+ .bTerminalID = 3,
+ .wTerminalType = cpu_to_le16(0x0101),
+ .bAssocTerminal = 0,
+ .bSourceID = 2,
+ .iTerminal = 0,
+};
+
+DECLARE_UVC_INPUT_HEADER_DESCRIPTOR(1, 2);
+
+static const struct UVC_INPUT_HEADER_DESCRIPTOR(1, 2) uvc_input_header = {
+ .bLength = UVC_DT_INPUT_HEADER_SIZE(1, 2),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = UVC_VS_INPUT_HEADER,
+ .bNumFormats = 2,
+ .wTotalLength = 0, /* dynamic */
+ .bEndpointAddress = 0, /* dynamic */
+ .bmInfo = 0,
+ .bTerminalLink = 3,
+ .bStillCaptureMethod = 0,
+ .bTriggerSupport = 0,
+ .bTriggerUsage = 0,
+ .bControlSize = 1,
+ .bmaControls[0][0] = 0,
+ .bmaControls[1][0] = 4,
+};
+
+static const struct uvc_format_uncompressed uvc_format_yuv = {
+ .bLength = UVC_DT_FORMAT_UNCOMPRESSED_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = UVC_VS_FORMAT_UNCOMPRESSED,
+ .bFormatIndex = 1,
+ .bNumFrameDescriptors = 2,
+ .guidFormat =
+ { 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71},
+ .bBitsPerPixel = 16,
+ .bDefaultFrameIndex = 1,
+ .bAspectRatioX = 0,
+ .bAspectRatioY = 0,
+ .bmInterfaceFlags = 0,
+ .bCopyProtect = 0,
+};
+
+DECLARE_UVC_FRAME_UNCOMPRESSED(1);
+DECLARE_UVC_FRAME_UNCOMPRESSED(3);
+
+static const struct UVC_FRAME_UNCOMPRESSED(3) uvc_frame_yuv_360p = {
+ .bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(3),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED,
+ .bFrameIndex = 1,
+ .bmCapabilities = 0,
+ .wWidth = cpu_to_le16(640),
+ .wHeight = cpu_to_le16(360),
+ .dwMinBitRate = cpu_to_le32(18432000),
+ .dwMaxBitRate = cpu_to_le32(55296000),
+ .dwMaxVideoFrameBufferSize = cpu_to_le32(460800),
+ .dwDefaultFrameInterval = cpu_to_le32(666666),
+ .bFrameIntervalType = 3,
+ .dwFrameInterval[0] = cpu_to_le32(666666),
+ .dwFrameInterval[1] = cpu_to_le32(1000000),
+ .dwFrameInterval[2] = cpu_to_le32(5000000),
+};
+
+static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_720p = {
+ .bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED,
+ .bFrameIndex = 2,
+ .bmCapabilities = 0,
+ .wWidth = cpu_to_le16(1280),
+ .wHeight = cpu_to_le16(720),
+ .dwMinBitRate = cpu_to_le32(29491200),
+ .dwMaxBitRate = cpu_to_le32(29491200),
+ .dwMaxVideoFrameBufferSize = cpu_to_le32(1843200),
+ .dwDefaultFrameInterval = cpu_to_le32(5000000),
+ .bFrameIntervalType = 1,
+ .dwFrameInterval[0] = cpu_to_le32(5000000),
+};
+
+static const struct uvc_format_mjpeg uvc_format_mjpg = {
+ .bLength = UVC_DT_FORMAT_MJPEG_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = UVC_VS_FORMAT_MJPEG,
+ .bFormatIndex = 2,
+ .bNumFrameDescriptors = 2,
+ .bmFlags = 0,
+ .bDefaultFrameIndex = 1,
+ .bAspectRatioX = 0,
+ .bAspectRatioY = 0,
+ .bmInterfaceFlags = 0,
+ .bCopyProtect = 0,
+};
+
+DECLARE_UVC_FRAME_MJPEG(1);
+DECLARE_UVC_FRAME_MJPEG(3);
+
+static const struct UVC_FRAME_MJPEG(3) uvc_frame_mjpg_360p = {
+ .bLength = UVC_DT_FRAME_MJPEG_SIZE(3),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = UVC_VS_FRAME_MJPEG,
+ .bFrameIndex = 1,
+ .bmCapabilities = 0,
+ .wWidth = cpu_to_le16(640),
+ .wHeight = cpu_to_le16(360),
+ .dwMinBitRate = cpu_to_le32(18432000),
+ .dwMaxBitRate = cpu_to_le32(55296000),
+ .dwMaxVideoFrameBufferSize = cpu_to_le32(460800),
+ .dwDefaultFrameInterval = cpu_to_le32(666666),
+ .bFrameIntervalType = 3,
+ .dwFrameInterval[0] = cpu_to_le32(666666),
+ .dwFrameInterval[1] = cpu_to_le32(1000000),
+ .dwFrameInterval[2] = cpu_to_le32(5000000),
+};
+
+static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_720p = {
+ .bLength = UVC_DT_FRAME_MJPEG_SIZE(1),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = UVC_VS_FRAME_MJPEG,
+ .bFrameIndex = 2,
+ .bmCapabilities = 0,
+ .wWidth = cpu_to_le16(1280),
+ .wHeight = cpu_to_le16(720),
+ .dwMinBitRate = cpu_to_le32(29491200),
+ .dwMaxBitRate = cpu_to_le32(29491200),
+ .dwMaxVideoFrameBufferSize = cpu_to_le32(1843200),
+ .dwDefaultFrameInterval = cpu_to_le32(5000000),
+ .bFrameIntervalType = 1,
+ .dwFrameInterval[0] = cpu_to_le32(5000000),
+};
+
+static const struct uvc_color_matching_descriptor uvc_color_matching = {
+ .bLength = UVC_DT_COLOR_MATCHING_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = UVC_VS_COLORFORMAT,
+ .bColorPrimaries = 1,
+ .bTransferCharacteristics = 1,
+ .bMatrixCoefficients = 4,
+};
+
+static const struct uvc_descriptor_header * const uvc_fs_control_cls[] = {
+ (const struct uvc_descriptor_header *) &uvc_control_header,
+ (const struct uvc_descriptor_header *) &uvc_camera_terminal,
+ (const struct uvc_descriptor_header *) &uvc_processing,
+ (const struct uvc_descriptor_header *) &uvc_output_terminal,
+ NULL,
+};
+
+static const struct uvc_descriptor_header * const uvc_ss_control_cls[] = {
+ (const struct uvc_descriptor_header *) &uvc_control_header,
+ (const struct uvc_descriptor_header *) &uvc_camera_terminal,
+ (const struct uvc_descriptor_header *) &uvc_processing,
+ (const struct uvc_descriptor_header *) &uvc_output_terminal,
+ NULL,
+};
+
+static const struct uvc_descriptor_header * const uvc_fs_streaming_cls[] = {
+ (const struct uvc_descriptor_header *) &uvc_input_header,
+ (const struct uvc_descriptor_header *) &uvc_format_yuv,
+ (const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
+ (const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
+ (const struct uvc_descriptor_header *) &uvc_format_mjpg,
+ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
+ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
+ (const struct uvc_descriptor_header *) &uvc_color_matching,
+ NULL,
+};
+
+static const struct uvc_descriptor_header * const uvc_hs_streaming_cls[] = {
+ (const struct uvc_descriptor_header *) &uvc_input_header,
+ (const struct uvc_descriptor_header *) &uvc_format_yuv,
+ (const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
+ (const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
+ (const struct uvc_descriptor_header *) &uvc_format_mjpg,
+ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
+ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
+ (const struct uvc_descriptor_header *) &uvc_color_matching,
+ NULL,
+};
+
+static const struct uvc_descriptor_header * const uvc_ss_streaming_cls[] = {
+ (const struct uvc_descriptor_header *) &uvc_input_header,
+ (const struct uvc_descriptor_header *) &uvc_format_yuv,
+ (const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
+ (const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
+ (const struct uvc_descriptor_header *) &uvc_format_mjpg,
+ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
+ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
+ (const struct uvc_descriptor_header *) &uvc_color_matching,
+ NULL,
+};
+
+/* --------------------------------------------------------------------------
+ * USB configuration
+ */
+
+static int
+webcam_config_bind(struct usb_configuration *c)
+{
+ int status = 0;
+
+ f_uvc = usb_get_function(fi_uvc);
+ if (IS_ERR(f_uvc))
+ return PTR_ERR(f_uvc);
+
+ status = usb_add_function(c, f_uvc);
+ if (status < 0)
+ usb_put_function(f_uvc);
+
+ return status;
+}
+
+static struct usb_configuration webcam_config_driver = {
+ .label = webcam_config_label,
+ .bConfigurationValue = 1,
+ .iConfiguration = 0, /* dynamic */
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+ .MaxPower = CONFIG_USB_GADGET_VBUS_DRAW,
+};
+
+static int
+webcam_unbind(struct usb_composite_dev *cdev)
+{
+ if (!IS_ERR_OR_NULL(f_uvc))
+ usb_put_function(f_uvc);
+ if (!IS_ERR_OR_NULL(fi_uvc))
+ usb_put_function_instance(fi_uvc);
+ return 0;
+}
+
+static int
+webcam_bind(struct usb_composite_dev *cdev)
+{
+ struct f_uvc_opts *uvc_opts;
+ int ret;
+
+ fi_uvc = usb_get_function_instance("uvc");
+ if (IS_ERR(fi_uvc))
+ return PTR_ERR(fi_uvc);
+
+ uvc_opts = container_of(fi_uvc, struct f_uvc_opts, func_inst);
+
+ uvc_opts->streaming_interval = streaming_interval;
+ uvc_opts->streaming_maxpacket = streaming_maxpacket;
+ uvc_opts->streaming_maxburst = streaming_maxburst;
+ uvc_set_trace_param(trace);
+
+ uvc_opts->fs_control = uvc_fs_control_cls;
+ uvc_opts->ss_control = uvc_ss_control_cls;
+ uvc_opts->fs_streaming = uvc_fs_streaming_cls;
+ uvc_opts->hs_streaming = uvc_hs_streaming_cls;
+ uvc_opts->ss_streaming = uvc_ss_streaming_cls;
+
+ /* Allocate string descriptor numbers ... note that string contents
+ * can be overridden by the composite_dev glue.
+ */
+ ret = usb_string_ids_tab(cdev, webcam_strings);
+ if (ret < 0)
+ goto error;
+ webcam_device_descriptor.iManufacturer =
+ webcam_strings[USB_GADGET_MANUFACTURER_IDX].id;
+ webcam_device_descriptor.iProduct =
+ webcam_strings[USB_GADGET_PRODUCT_IDX].id;
+ webcam_config_driver.iConfiguration =
+ webcam_strings[STRING_DESCRIPTION_IDX].id;
+
+ /* Register our configuration. */
+ if ((ret = usb_add_config(cdev, &webcam_config_driver,
+ webcam_config_bind)) < 0)
+ goto error;
+
+ usb_composite_overwrite_options(cdev, &coverwrite);
+ INFO(cdev, "Webcam Video Gadget\n");
+ return 0;
+
+error:
+ usb_put_function_instance(fi_uvc);
+ return ret;
+}
+
+/* --------------------------------------------------------------------------
+ * Driver
+ */
+
+static struct usb_composite_driver webcam_driver = {
+ .name = "g_webcam",
+ .dev = &webcam_device_descriptor,
+ .strings = webcam_device_strings,
+ .max_speed = USB_SPEED_SUPER,
+ .bind = webcam_bind,
+ .unbind = webcam_unbind,
+};
+
+module_usb_composite_driver(webcam_driver);
+
+MODULE_AUTHOR("Laurent Pinchart");
+MODULE_DESCRIPTION("Webcam Video Gadget");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.1.0");
+
diff --git a/drivers/usb/gadget/legacy/zero.c b/drivers/usb/gadget/legacy/zero.c
new file mode 100644
index 000000000..c986e8add
--- /dev/null
+++ b/drivers/usb/gadget/legacy/zero.c
@@ -0,0 +1,417 @@
+/*
+ * zero.c -- Gadget Zero, for USB development
+ *
+ * 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.
+ */
+
+/*
+ * Gadget Zero only needs two bulk endpoints, and is an example of how you
+ * can write a hardware-agnostic gadget driver running inside a USB device.
+ * Some hardware details are visible, but don't affect most of the driver.
+ *
+ * Use it with the Linux host/master side "usbtest" driver to get a basic
+ * functional test of your device-side usb stack, or with "usb-skeleton".
+ *
+ * It supports two similar configurations. One sinks whatever the usb host
+ * writes, and in return sources zeroes. The other loops whatever the host
+ * writes back, so the host can read it.
+ *
+ * Many drivers will only have one configuration, letting them be much
+ * simpler if they also don't support high speed operation (like this
+ * driver does).
+ *
+ * Why is *this* driver using two configurations, rather than setting up
+ * two interfaces with different functions? To help verify that multiple
+ * configuration infrastructure is working correctly; also, so that it can
+ * work with low capability USB controllers without four bulk endpoints.
+ */
+
+/*
+ * driver assumes self-powered hardware, and
+ * has no way for users to trigger remote wakeup.
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/usb/composite.h>
+
+#include "g_zero.h"
+/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
+
+#define DRIVER_VERSION "Cinco de Mayo 2008"
+
+static const char longname[] = "Gadget Zero";
+
+/*
+ * Normally the "loopback" configuration is second (index 1) so
+ * it's not the default. Here's where to change that order, to
+ * work better with hosts where config changes are problematic or
+ * controllers (like original superh) that only support one config.
+ */
+static bool loopdefault = 0;
+module_param(loopdefault, bool, S_IRUGO|S_IWUSR);
+
+static struct usb_zero_options gzero_options = {
+ .isoc_interval = GZERO_ISOC_INTERVAL,
+ .isoc_maxpacket = GZERO_ISOC_MAXPACKET,
+ .bulk_buflen = GZERO_BULK_BUFLEN,
+ .qlen = GZERO_QLEN,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* Thanks to NetChip Technologies for donating this product ID.
+ *
+ * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
+ * Instead: allocate your own, using normal USB-IF procedures.
+ */
+#ifndef CONFIG_USB_ZERO_HNPTEST
+#define DRIVER_VENDOR_NUM 0x0525 /* NetChip */
+#define DRIVER_PRODUCT_NUM 0xa4a0 /* Linux-USB "Gadget Zero" */
+#define DEFAULT_AUTORESUME 0
+#else
+#define DRIVER_VENDOR_NUM 0x1a0a /* OTG test device IDs */
+#define DRIVER_PRODUCT_NUM 0xbadd
+#define DEFAULT_AUTORESUME 5
+#endif
+
+/* If the optional "autoresume" mode is enabled, it provides good
+ * functional coverage for the "USBCV" test harness from USB-IF.
+ * It's always set if OTG mode is enabled.
+ */
+static unsigned autoresume = DEFAULT_AUTORESUME;
+module_param(autoresume, uint, S_IRUGO);
+MODULE_PARM_DESC(autoresume, "zero, or seconds before remote wakeup");
+
+/* Maximum Autoresume time */
+static unsigned max_autoresume;
+module_param(max_autoresume, uint, S_IRUGO);
+MODULE_PARM_DESC(max_autoresume, "maximum seconds before remote wakeup");
+
+/* Interval between two remote wakeups */
+static unsigned autoresume_interval_ms;
+module_param(autoresume_interval_ms, uint, S_IRUGO);
+MODULE_PARM_DESC(autoresume_interval_ms,
+ "milliseconds to increase successive wakeup delays");
+
+static unsigned autoresume_step_ms;
+/*-------------------------------------------------------------------------*/
+
+static struct usb_device_descriptor device_desc = {
+ .bLength = sizeof device_desc,
+ .bDescriptorType = USB_DT_DEVICE,
+
+ .bcdUSB = cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_VENDOR_SPEC,
+
+ .idVendor = cpu_to_le16(DRIVER_VENDOR_NUM),
+ .idProduct = cpu_to_le16(DRIVER_PRODUCT_NUM),
+ .bNumConfigurations = 2,
+};
+
+#ifdef CONFIG_USB_OTG
+static struct usb_otg_descriptor otg_descriptor = {
+ .bLength = sizeof otg_descriptor,
+ .bDescriptorType = USB_DT_OTG,
+
+ /* REVISIT SRP-only hardware is possible, although
+ * it would not be called "OTG" ...
+ */
+ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
+};
+
+static const struct usb_descriptor_header *otg_desc[] = {
+ (struct usb_descriptor_header *) &otg_descriptor,
+ NULL,
+};
+#else
+#define otg_desc NULL
+#endif
+
+/* string IDs are assigned dynamically */
+/* default serial number takes at least two packets */
+static char serial[] = "0123456789.0123456789.0123456789";
+
+#define USB_GZERO_SS_DESC (USB_GADGET_FIRST_AVAIL_IDX + 0)
+#define USB_GZERO_LB_DESC (USB_GADGET_FIRST_AVAIL_IDX + 1)
+
+static struct usb_string strings_dev[] = {
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = longname,
+ [USB_GADGET_SERIAL_IDX].s = serial,
+ [USB_GZERO_SS_DESC].s = "source and sink data",
+ [USB_GZERO_LB_DESC].s = "loop input to output",
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+ .language = 0x0409, /* en-us */
+ .strings = strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+ &stringtab_dev,
+ NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static struct timer_list autoresume_timer;
+
+static void zero_autoresume(unsigned long _c)
+{
+ struct usb_composite_dev *cdev = (void *)_c;
+ struct usb_gadget *g = cdev->gadget;
+
+ /* unconfigured devices can't issue wakeups */
+ if (!cdev->config)
+ return;
+
+ /* Normally the host would be woken up for something
+ * more significant than just a timer firing; likely
+ * because of some direct user request.
+ */
+ if (g->speed != USB_SPEED_UNKNOWN) {
+ int status = usb_gadget_wakeup(g);
+ INFO(cdev, "%s --> %d\n", __func__, status);
+ }
+}
+
+static void zero_suspend(struct usb_composite_dev *cdev)
+{
+ if (cdev->gadget->speed == USB_SPEED_UNKNOWN)
+ return;
+
+ if (autoresume) {
+ if (max_autoresume &&
+ (autoresume_step_ms > max_autoresume * 1000))
+ autoresume_step_ms = autoresume * 1000;
+
+ mod_timer(&autoresume_timer, jiffies +
+ msecs_to_jiffies(autoresume_step_ms));
+ DBG(cdev, "suspend, wakeup in %d milliseconds\n",
+ autoresume_step_ms);
+
+ autoresume_step_ms += autoresume_interval_ms;
+ } else
+ DBG(cdev, "%s\n", __func__);
+}
+
+static void zero_resume(struct usb_composite_dev *cdev)
+{
+ DBG(cdev, "%s\n", __func__);
+ del_timer(&autoresume_timer);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_configuration loopback_driver = {
+ .label = "loopback",
+ .bConfigurationValue = 2,
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+ /* .iConfiguration = DYNAMIC */
+};
+
+static struct usb_function *func_ss;
+static struct usb_function_instance *func_inst_ss;
+
+static int ss_config_setup(struct usb_configuration *c,
+ const struct usb_ctrlrequest *ctrl)
+{
+ switch (ctrl->bRequest) {
+ case 0x5b:
+ case 0x5c:
+ return func_ss->setup(func_ss, ctrl);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static struct usb_configuration sourcesink_driver = {
+ .label = "source/sink",
+ .setup = ss_config_setup,
+ .bConfigurationValue = 3,
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+ /* .iConfiguration = DYNAMIC */
+};
+
+module_param_named(buflen, gzero_options.bulk_buflen, uint, 0);
+module_param_named(pattern, gzero_options.pattern, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(pattern, "0 = all zeroes, 1 = mod63, 2 = none");
+
+module_param_named(isoc_interval, gzero_options.isoc_interval, uint,
+ S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(isoc_interval, "1 - 16");
+
+module_param_named(isoc_maxpacket, gzero_options.isoc_maxpacket, uint,
+ S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(isoc_maxpacket, "0 - 1023 (fs), 0 - 1024 (hs/ss)");
+
+module_param_named(isoc_mult, gzero_options.isoc_mult, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(isoc_mult, "0 - 2 (hs/ss only)");
+
+module_param_named(isoc_maxburst, gzero_options.isoc_maxburst, uint,
+ S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(isoc_maxburst, "0 - 15 (ss only)");
+
+static struct usb_function *func_lb;
+static struct usb_function_instance *func_inst_lb;
+
+module_param_named(qlen, gzero_options.qlen, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(qlen, "depth of loopback queue");
+
+static int zero_bind(struct usb_composite_dev *cdev)
+{
+ struct f_ss_opts *ss_opts;
+ struct f_lb_opts *lb_opts;
+ int status;
+
+ /* Allocate string descriptor numbers ... note that string
+ * contents can be overridden by the composite_dev glue.
+ */
+ status = usb_string_ids_tab(cdev, strings_dev);
+ if (status < 0)
+ return status;
+
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+ device_desc.iSerialNumber = strings_dev[USB_GADGET_SERIAL_IDX].id;
+
+ setup_timer(&autoresume_timer, zero_autoresume, (unsigned long) cdev);
+
+ func_inst_ss = usb_get_function_instance("SourceSink");
+ if (IS_ERR(func_inst_ss))
+ return PTR_ERR(func_inst_ss);
+
+ ss_opts = container_of(func_inst_ss, struct f_ss_opts, func_inst);
+ ss_opts->pattern = gzero_options.pattern;
+ ss_opts->isoc_interval = gzero_options.isoc_interval;
+ ss_opts->isoc_maxpacket = gzero_options.isoc_maxpacket;
+ ss_opts->isoc_mult = gzero_options.isoc_mult;
+ ss_opts->isoc_maxburst = gzero_options.isoc_maxburst;
+ ss_opts->bulk_buflen = gzero_options.bulk_buflen;
+
+ func_ss = usb_get_function(func_inst_ss);
+ if (IS_ERR(func_ss)) {
+ status = PTR_ERR(func_ss);
+ goto err_put_func_inst_ss;
+ }
+
+ func_inst_lb = usb_get_function_instance("Loopback");
+ if (IS_ERR(func_inst_lb)) {
+ status = PTR_ERR(func_inst_lb);
+ goto err_put_func_ss;
+ }
+
+ lb_opts = container_of(func_inst_lb, struct f_lb_opts, func_inst);
+ lb_opts->bulk_buflen = gzero_options.bulk_buflen;
+ lb_opts->qlen = gzero_options.qlen;
+
+ func_lb = usb_get_function(func_inst_lb);
+ if (IS_ERR(func_lb)) {
+ status = PTR_ERR(func_lb);
+ goto err_put_func_inst_lb;
+ }
+
+ sourcesink_driver.iConfiguration = strings_dev[USB_GZERO_SS_DESC].id;
+ loopback_driver.iConfiguration = strings_dev[USB_GZERO_LB_DESC].id;
+
+ /* support autoresume for remote wakeup testing */
+ sourcesink_driver.bmAttributes &= ~USB_CONFIG_ATT_WAKEUP;
+ loopback_driver.bmAttributes &= ~USB_CONFIG_ATT_WAKEUP;
+ sourcesink_driver.descriptors = NULL;
+ loopback_driver.descriptors = NULL;
+ if (autoresume) {
+ sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ autoresume_step_ms = autoresume * 1000;
+ }
+
+ /* support OTG systems */
+ if (gadget_is_otg(cdev->gadget)) {
+ sourcesink_driver.descriptors = otg_desc;
+ sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ loopback_driver.descriptors = otg_desc;
+ loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
+ /* Register primary, then secondary configuration. Note that
+ * SH3 only allows one config...
+ */
+ if (loopdefault) {
+ usb_add_config_only(cdev, &loopback_driver);
+ usb_add_config_only(cdev, &sourcesink_driver);
+ } else {
+ usb_add_config_only(cdev, &sourcesink_driver);
+ usb_add_config_only(cdev, &loopback_driver);
+ }
+ status = usb_add_function(&sourcesink_driver, func_ss);
+ if (status)
+ goto err_conf_flb;
+
+ usb_ep_autoconfig_reset(cdev->gadget);
+ status = usb_add_function(&loopback_driver, func_lb);
+ if (status)
+ goto err_conf_flb;
+
+ usb_ep_autoconfig_reset(cdev->gadget);
+ usb_composite_overwrite_options(cdev, &coverwrite);
+
+ INFO(cdev, "%s, version: " DRIVER_VERSION "\n", longname);
+
+ return 0;
+
+err_conf_flb:
+ usb_put_function(func_lb);
+ func_lb = NULL;
+err_put_func_inst_lb:
+ usb_put_function_instance(func_inst_lb);
+ func_inst_lb = NULL;
+err_put_func_ss:
+ usb_put_function(func_ss);
+ func_ss = NULL;
+err_put_func_inst_ss:
+ usb_put_function_instance(func_inst_ss);
+ func_inst_ss = NULL;
+ return status;
+}
+
+static int zero_unbind(struct usb_composite_dev *cdev)
+{
+ del_timer_sync(&autoresume_timer);
+ if (!IS_ERR_OR_NULL(func_ss))
+ usb_put_function(func_ss);
+ usb_put_function_instance(func_inst_ss);
+ if (!IS_ERR_OR_NULL(func_lb))
+ usb_put_function(func_lb);
+ usb_put_function_instance(func_inst_lb);
+ return 0;
+}
+
+static struct usb_composite_driver zero_driver = {
+ .name = "zero",
+ .dev = &device_desc,
+ .strings = dev_strings,
+ .max_speed = USB_SPEED_SUPER,
+ .bind = zero_bind,
+ .unbind = zero_unbind,
+ .suspend = zero_suspend,
+ .resume = zero_resume,
+};
+
+module_usb_composite_driver(zero_driver);
+
+MODULE_AUTHOR("David Brownell");
+MODULE_LICENSE("GPL");