From 8443f2d2b7782fef35fe579bf1eb612c24951486 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 15 Jul 2014 13:09:44 +0200 Subject: usb: gadget: Gadget directory cleanup - group legacy gadgets The drivers/usb/gadget directory contains many files. Files which are related can be distributed into separate directories. This patch moves the legacy gadgets (i.e. those not using configfs) into a separate directory. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 49e434e..2986a43 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -722,466 +722,7 @@ config USB_CONFIGFS_F_FS implemented in kernel space (for instance Ethernet, serial or mass storage) and other are implemented in user space. -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 - boolean "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 - 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 - 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 - 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 - 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 - 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". +source "drivers/usb/gadget/legacy/Kconfig" endchoice diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 49514ea..61d2503 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -1,8 +1,8 @@ # # USB peripheral controller drivers # -ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG -ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG +subdir-ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG +subdir-ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG obj-$(CONFIG_USB_GADGET) += udc-core.o obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o @@ -64,42 +64,4 @@ obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o usb_f_fs-y := f_fs.o obj-$(CONFIG_USB_F_FS) += usb_f_fs.o -# -# USB gadget drivers -# -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 +obj-$(CONFIG_USB_GADGET) += legacy/ diff --git a/drivers/usb/gadget/acm_ms.c b/drivers/usb/gadget/acm_ms.c deleted file mode 100644 index c30b7b5..0000000 --- a/drivers/usb/gadget/acm_ms.c +++ /dev/null @@ -1,274 +0,0 @@ -/* - * 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 - * - * 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 -#include - -#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 __init 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 __init 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 __exit 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 __refdata 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 = __exit_p(acm_ms_unbind), -}; - -module_usb_composite_driver(acm_ms_driver); - -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_AUTHOR("Klaus Schwarzkopf "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/audio.c deleted file mode 100644 index 6eb695e..0000000 --- a/drivers/usb/gadget/audio.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - * audio.c -- Audio gadget driver - * - * Copyright (C) 2008 Bryan Wu - * Copyright (C) 2008 Analog Devices, Inc - * - * Enter bugs at http://blackfin.uclinux.org/ - * - * Licensed under the GPL-2 or later. - */ - -/* #define VERBOSE_DEBUG */ - -#include -#include -#include - -#include "gadget_chips.h" -#define DRIVER_DESC "Linux USB Audio Gadget" -#define DRIVER_VERSION "Feb 2, 2012" - -USB_GADGET_COMPOSITE_OPTIONS(); - -/* 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, -}; - -#ifdef CONFIG_GADGET_UAC1 -#include "u_uac1.h" -#include "u_uac1.c" -#include "f_uac1.c" -#else -#include "f_uac2.c" -#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 __init audio_do_config(struct usb_configuration *c) -{ - /* 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; - } - - audio_bind_config(c); - - return 0; -} - -static struct usb_configuration audio_config_driver = { - .label = DRIVER_DESC, - .bConfigurationValue = 1, - /* .iConfiguration = DYNAMIC */ - .bmAttributes = USB_CONFIG_ATT_SELFPOWER, -#ifndef CONFIG_GADGET_UAC1 - .unbind = uac2_unbind_config, -#endif -}; - -/*-------------------------------------------------------------------------*/ - -static int __init audio_bind(struct usb_composite_dev *cdev) -{ - int status; - - 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: - return status; -} - -static int __exit audio_unbind(struct usb_composite_dev *cdev) -{ -#ifdef CONFIG_GADGET_UAC1 - gaudio_cleanup(); -#endif - return 0; -} - -static __refdata struct usb_composite_driver audio_driver = { - .name = "g_audio", - .dev = &device_desc, - .strings = audio_strings, - .max_speed = USB_SPEED_HIGH, - .bind = audio_bind, - .unbind = __exit_p(audio_unbind), -}; - -module_usb_composite_driver(audio_driver); - -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_AUTHOR("Bryan Wu "); -MODULE_LICENSE("GPL"); - diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c deleted file mode 100644 index 2e85d94..0000000 --- a/drivers/usb/gadget/cdc2.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - * 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 -#include - -#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 __init 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 __init 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 __exit 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 __refdata struct usb_composite_driver cdc_driver = { - .name = "g_cdc", - .dev = &device_desc, - .strings = dev_strings, - .max_speed = USB_SPEED_HIGH, - .bind = cdc_bind, - .unbind = __exit_p(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/dbgp.c b/drivers/usb/gadget/dbgp.c deleted file mode 100644 index 986fc51..0000000 --- a/drivers/usb/gadget/dbgp.c +++ /dev/null @@ -1,434 +0,0 @@ -/* - * dbgp.c -- EHCI Debug Port device gadget - * - * Copyright (C) 2010 Stephane Duverger - * - * Released under the GPLv2. - */ - -/* verbose messages */ -#include -#include -#include -#include -#include - -#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); -#endif - if (dbgp.req) { - kfree(dbgp.req->buf); - usb_ep_free_request(gadget->ep0, dbgp.req); - } - - gadget->ep0->driver_data = NULL; -} - -#ifdef CONFIG_USB_G_DBGP_SERIAL -static unsigned char tty_line; -#endif - -static int __init 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; - - if (gserial_alloc_line(&tty_line)) { - stp = 3; - goto fail_3; - } - - return 0; - -fail_3: - dbgp.o_ep->driver_data = NULL; -#else - return 0; -#endif -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 __init 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; - } -#endif - err = dbgp_configure_endpoints(gadget); - if (err < 0) { - stp = 4; - 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 = 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 __refdata struct usb_gadget_driver dbgp_driver = { - .function = "dbgp", - .max_speed = USB_SPEED_HIGH, - .bind = dbgp_bind, - .unbind = dbgp_unbind, - .setup = dbgp_setup, - .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/ether.c b/drivers/usb/gadget/ether.c deleted file mode 100644 index c5fdc61..0000000 --- a/drivers/usb/gadget/ether.c +++ /dev/null @@ -1,482 +0,0 @@ -/* - * 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 -#include - -#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 - -#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 __init 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 __init 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 __init 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, ð_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 __exit 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 __refdata struct usb_composite_driver eth_driver = { - .name = "g_ether", - .dev = &device_desc, - .strings = dev_strings, - .max_speed = USB_SPEED_SUPER, - .bind = eth_bind, - .unbind = __exit_p(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/g_ffs.c b/drivers/usb/gadget/g_ffs.c deleted file mode 100644 index 06acfa5..0000000 --- a/drivers/usb/gadget/g_ffs.c +++ /dev/null @@ -1,582 +0,0 @@ -/* - * g_ffs.c -- user mode file system API for USB composite function controllers - * - * Copyright (C) 2010 Samsung Electronics - * Author: Michal Nazarewicz - * - * 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 - -#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS -#include - -# 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; -} 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 __refdata 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 0; -} - -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)) - 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/gmidi.c b/drivers/usb/gadget/gmidi.c deleted file mode 100644 index 3d696b8..0000000 --- a/drivers/usb/gadget/gmidi.c +++ /dev/null @@ -1,166 +0,0 @@ -/* - * gmidi.c -- USB MIDI Gadget Driver - * - * Copyright (C) 2006 Thumtronics Pty Ltd. - * Developed for Thumtronics by Grey Innovation - * Ben Williamson - * - * 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 -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "gadget_chips.h" - -#include "f_midi.c" - -/*-------------------------------------------------------------------------*/ - -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 int __exit midi_unbind(struct usb_composite_dev *dev) -{ - 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 __init midi_bind_config(struct usb_configuration *c) -{ - return f_midi_bind_config(c, index, id, - in_ports, out_ports, - buflen, qlen); -} - -static int __init midi_bind(struct usb_composite_dev *cdev) -{ - int status; - - 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; - midi_config.iConfiguration = strings_dev[STRING_DESCRIPTION_IDX].id; - - status = usb_add_config(cdev, &midi_config, midi_bind_config); - if (status < 0) - return status; - usb_composite_overwrite_options(cdev, &coverwrite); - pr_info("%s\n", longname); - return 0; -} - -static __refdata struct usb_composite_driver midi_driver = { - .name = (char *) longname, - .dev = &device_desc, - .strings = dev_strings, - .max_speed = USB_SPEED_HIGH, - .bind = midi_bind, - .unbind = __exit_p(midi_unbind), -}; - -module_usb_composite_driver(midi_driver); diff --git a/drivers/usb/gadget/hid.c b/drivers/usb/gadget/hid.c deleted file mode 100644 index 778613eb..0000000 --- a/drivers/usb/gadget/hid.c +++ /dev/null @@ -1,266 +0,0 @@ -/* - * hid.c -- HID Composite driver - * - * Based on multi.c - * - * Copyright (C) 2010 Fabien Chouteau - * - * 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 -#include -#include -#include -#include - -#include "gadget_chips.h" -#define DRIVER_DESC "HID Gadget" -#define DRIVER_VERSION "2010/03/16" - -/*-------------------------------------------------------------------------*/ - -#define HIDG_VENDOR_NUM 0x0525 /* XXX NetChip */ -#define HIDG_PRODUCT_NUM 0xa4ac /* Linux-USB HID gadget */ - -/*-------------------------------------------------------------------------*/ - -/* - * kbuild is not very cooperative with respect to linking separately - * compiled library objects into one module. So for now we won't use - * separate compilation ... ensuring init/exit sections work to shrink - * the runtime footprint, and giving us at least some parts of what - * a "gcc --combine ... part1.c part2.c part3.c ... " build would. - */ -#include "f_hid.c" - - -struct hidg_func_node { - 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 __init do_config(struct usb_configuration *c) -{ - struct hidg_func_node *e; - int func = 0, 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) { - status = hidg_bind_config(c, e->func, func++); - if (status) - break; - } - - return status; -} - -static struct usb_configuration config_driver = { - .label = "HID Gadget", - .bConfigurationValue = 1, - /* .iConfiguration = DYNAMIC */ - .bmAttributes = USB_CONFIG_ATT_SELFPOWER, -}; - -/****************************** Gadget Bind ******************************/ - -static int __init hid_bind(struct usb_composite_dev *cdev) -{ - struct usb_gadget *gadget = cdev->gadget; - struct list_head *tmp; - int status, funcs = 0; - - list_for_each(tmp, &hidg_func_list) - funcs++; - - if (!funcs) - return -ENODEV; - - /* set up HID */ - status = ghid_setup(cdev->gadget, funcs); - if (status < 0) - return 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; - - /* register our configuration */ - status = usb_add_config(cdev, &config_driver, do_config); - if (status < 0) - return status; - - usb_composite_overwrite_options(cdev, &coverwrite); - dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); - - return 0; -} - -static int __exit hid_unbind(struct usb_composite_dev *cdev) -{ - ghid_cleanup(); - return 0; -} - -static int __init 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 __refdata struct usb_composite_driver hidg_driver = { - .name = "g_hid", - .dev = &device_desc, - .strings = dev_strings, - .max_speed = USB_SPEED_HIGH, - .bind = hid_bind, - .unbind = __exit_p(hid_unbind), -}; - -static struct platform_driver hidg_plat_driver = { - .remove = hidg_plat_driver_remove, - .driver = { - .owner = THIS_MODULE, - .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) -{ - platform_driver_unregister(&hidg_plat_driver); - usb_composite_unregister(&hidg_driver); -} -module_exit(hidg_cleanup); diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c deleted file mode 100644 index ee6c164..0000000 --- a/drivers/usb/gadget/inode.c +++ /dev/null @@ -1,2142 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - - -/* - * 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"); - - -/*----------------------------------------------------------------------*/ - -#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; - struct inode *inode; -}; - -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) -{ - int val; - - if (f_flags & O_NONBLOCK) { - if (!mutex_trylock(&epdata->lock)) - goto nonblock; - if (epdata->state != STATE_EP_ENABLED) { - 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: - break; - // case STATE_EP_DISABLED: /* "can't happen" */ - // case STATE_EP_READY: /* "can't happen" */ - default: /* error! */ - pr_debug ("%s: ep %p not available, state %d\n", - shortname, epdata, epdata->state); - // FALLTHROUGH - case STATE_EP_UNBOUND: /* clean disconnect */ - val = -ENODEV; - mutex_unlock(&epdata->lock); - } - return val; -} - -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; -} - - -/* handle a synchronous OUT bulk/intr/iso transfer */ -static ssize_t -ep_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr) -{ - struct ep_data *data = fd->private_data; - void *kbuf; - ssize_t value; - - if ((value = get_ready_ep (fd->f_flags, data)) < 0) - return value; - - /* halt any endpoint by doing a "wrong direction" i/o call */ - if (usb_endpoint_dir_in(&data->desc)) { - if (usb_endpoint_xfer_isoc(&data->desc)) { - mutex_unlock(&data->lock); - return -EINVAL; - } - DBG (data->dev, "%s halt\n", data->name); - spin_lock_irq (&data->dev->lock); - if (likely (data->ep != NULL)) - usb_ep_set_halt (data->ep); - spin_unlock_irq (&data->dev->lock); - mutex_unlock(&data->lock); - return -EBADMSG; - } - - /* FIXME readahead for O_NONBLOCK and poll(); careful with ZLPs */ - - value = -ENOMEM; - kbuf = kmalloc (len, GFP_KERNEL); - if (unlikely (!kbuf)) - goto free1; - - value = ep_io (data, kbuf, len); - VDEBUG (data->dev, "%s read %zu OUT, status %d\n", - data->name, len, (int) value); - if (value >= 0 && copy_to_user (buf, kbuf, value)) - value = -EFAULT; - -free1: - mutex_unlock(&data->lock); - kfree (kbuf); - return value; -} - -/* handle a synchronous IN bulk/intr/iso transfer */ -static ssize_t -ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) -{ - struct ep_data *data = fd->private_data; - void *kbuf; - ssize_t value; - - if ((value = get_ready_ep (fd->f_flags, data)) < 0) - return value; - - /* halt any endpoint by doing a "wrong direction" i/o call */ - if (!usb_endpoint_dir_in(&data->desc)) { - if (usb_endpoint_xfer_isoc(&data->desc)) { - mutex_unlock(&data->lock); - return -EINVAL; - } - DBG (data->dev, "%s halt\n", data->name); - spin_lock_irq (&data->dev->lock); - if (likely (data->ep != NULL)) - usb_ep_set_halt (data->ep); - spin_unlock_irq (&data->dev->lock); - mutex_unlock(&data->lock); - return -EBADMSG; - } - - /* FIXME writebehind for O_NONBLOCK and poll(), qlen = 1 */ - - value = -ENOMEM; - kbuf = memdup_user(buf, len); - if (!kbuf) { - value = PTR_ERR(kbuf); - goto free1; - } - - value = ep_io (data, kbuf, len); - VDEBUG (data->dev, "%s write %zu IN, status %d\n", - data->name, len, (int) value); -free1: - mutex_unlock(&data->lock); - 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)) < 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; - const struct iovec *iv; - unsigned long nr_segs; - 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 ssize_t ep_copy_to_user(struct kiocb_priv *priv) -{ - ssize_t len, total; - void *to_copy; - int i; - - /* copy stuff into user buffers */ - total = priv->actual; - len = 0; - to_copy = priv->buf; - for (i=0; i < priv->nr_segs; i++) { - ssize_t this = min((ssize_t)(priv->iv[i].iov_len), total); - - if (copy_to_user(priv->iv[i].iov_base, to_copy, this)) { - if (len == 0) - len = -EFAULT; - break; - } - - total -= this; - len += this; - to_copy += this; - if (total == 0) - break; - } - - return len; -} - -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 = ep_copy_to_user(priv); - unuse_mm(mm); - - /* completing the iocb can drop the ctx and mm, don't touch mm after */ - aio_complete(iocb, ret, ret); - - kfree(priv->buf); - 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->iv == NULL || unlikely(req->actual == 0)) { - kfree(req->buf); - kfree(priv); - iocb->private = NULL; - /* aio_complete() reports bytes-transferred _and_ faults */ - aio_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; - schedule_work(&priv->work); - } - spin_unlock(&epdata->dev->lock); - - usb_ep_free_request(ep, req); - put_ep(epdata); -} - -static ssize_t -ep_aio_rwtail( - struct kiocb *iocb, - char *buf, - size_t len, - struct ep_data *epdata, - const struct iovec *iv, - unsigned long nr_segs -) -{ - struct kiocb_priv *priv; - struct usb_request *req; - ssize_t value; - - priv = kmalloc(sizeof *priv, GFP_KERNEL); - if (!priv) { - value = -ENOMEM; -fail: - kfree(buf); - return value; - } - iocb->private = priv; - priv->iocb = iocb; - priv->iv = iv; - priv->nr_segs = nr_segs; - INIT_WORK(&priv->work, ep_user_copy_worker); - - value = get_ready_ep(iocb->ki_filp->f_flags, epdata); - if (unlikely(value < 0)) { - kfree(priv); - goto fail; - } - - 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); - if (likely(epdata->ep)) { - req = usb_ep_alloc_request(epdata->ep, GFP_ATOMIC); - if (likely(req)) { - 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); - } else - value = -EAGAIN; - } else - value = -ENODEV; - spin_unlock_irq(&epdata->dev->lock); - - mutex_unlock(&epdata->lock); - - if (unlikely(value)) { - kfree(priv); - put_ep(epdata); - } else - value = -EIOCBQUEUED; - return value; -} - -static ssize_t -ep_aio_read(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t o) -{ - struct ep_data *epdata = iocb->ki_filp->private_data; - char *buf; - - if (unlikely(usb_endpoint_dir_in(&epdata->desc))) - return -EINVAL; - - buf = kmalloc(iocb->ki_nbytes, GFP_KERNEL); - if (unlikely(!buf)) - return -ENOMEM; - - return ep_aio_rwtail(iocb, buf, iocb->ki_nbytes, epdata, iov, nr_segs); -} - -static ssize_t -ep_aio_write(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t o) -{ - struct ep_data *epdata = iocb->ki_filp->private_data; - char *buf; - size_t len = 0; - int i = 0; - - if (unlikely(!usb_endpoint_dir_in(&epdata->desc))) - return -EINVAL; - - buf = kmalloc(iocb->ki_nbytes, GFP_KERNEL); - if (unlikely(!buf)) - return -ENOMEM; - - for (i=0; i < nr_segs; i++) { - if (unlikely(copy_from_user(&buf[len], iov[i].iov_base, - iov[i].iov_len) != 0)) { - kfree(buf); - return -EFAULT; - } - len += iov[i].iov_len; - } - return ep_aio_rwtail(iocb, buf, len, epdata, NULL, 0); -} - -/*----------------------------------------------------------------------*/ - -/* used after endpoint configuration */ -static const struct file_operations ep_io_operations = { - .owner = THIS_MODULE, - .llseek = no_llseek, - - .read = ep_read, - .write = ep_write, - .unlocked_ioctl = ep_ioctl, - .release = ep_release, - - .aio_read = ep_aio_read, - .aio_write = ep_aio_write, -}; - -/* 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 file *fd, const char __user *buf, size_t len, loff_t *ptr) -{ - struct ep_data *data = fd->private_data; - struct usb_ep *ep; - u32 tag; - int value, length = len; - - value = mutex_lock_interruptible(&data->lock); - if (value < 0) - return value; - - 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 */ - if (copy_from_user (&tag, buf, 4)) { - goto fail1; - } - 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 */ - if (copy_from_user (&data->desc, buf, USB_DT_ENDPOINT_SIZE)) { - goto fail1; - } - 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; - if (copy_from_user (&data->hs_desc, buf + USB_DT_ENDPOINT_SIZE, - USB_DT_ENDPOINT_SIZE)) { - goto fail1; - } - 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; - value = usb_ep_enable(ep); - if (value == 0) - data->state = STATE_EP_ENABLED; - break; - case USB_SPEED_HIGH: - /* fails if caller didn't provide that descriptor... */ - ep->desc = &data->hs_desc; - value = usb_ep_enable(ep); - if (value == 0) - data->state = STATE_EP_ENABLED; - break; - default: - DBG(data->dev, "unconnected, %s init abandoned\n", - data->name); - value = -EINVAL; - } - if (value == 0) { - fd->f_op = &ep_io_operations; - value = length; - } -gone: - spin_unlock_irq (&data->dev->lock); - if (value < 0) { -fail: - data->desc.bDescriptorType = 0; - data->hs_desc.bDescriptorType = 0; - } - mutex_unlock(&data->lock); - return value; -fail0: - value = -EINVAL; - goto fail; -fail1: - value = -EFAULT; - 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; -} - -/* used before endpoint configuration */ -static const struct file_operations ep_config_operations = { - .llseek = no_llseek, - - .open = ep_open, - .write = ep_config, - .release = ep_release, -}; - -/*----------------------------------------------------------------------*/ - -/* 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); - - /* 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; - - spin_lock_irq (&dev->lock); - - /* 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); - - spin_unlock_irq (&dev->lock); - 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; - 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; - - 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; -} - -/* used after device configuration */ -static const struct file_operations ep0_io_operations = { - .owner = THIS_MODULE, - .llseek = no_llseek, - - .read = ep0_read, - .write = ep0_write, - .fasync = ep0_fasync, - .poll = ep0_poll, - .unlocked_ioctl = dev_ioctl, - .release = dev_release, -}; - -/*----------------------------------------------------------------------*/ - -/* 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 = dentry->d_parent->d_inode; - - /* 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 inode * -gadgetfs_create_file (struct super_block *sb, char const *name, - void *data, const struct file_operations *fops, - struct dentry **dentry_p); - -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->inode = gadgetfs_create_file (dev->sb, data->name, - data, &ep_config_operations, - &data->dentry); - if (!data->inode) - 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, - .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; - - 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. - */ - fd->f_op = &ep0_io_operations; - 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 dev_init_operations = { - .llseek = no_llseek, - - .open = dev_open, - .write = dev_config, - .fasync = ep0_fasync, - .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 inode * -gadgetfs_create_file (struct super_block *sb, char const *name, - void *data, const struct file_operations *fops, - struct dentry **dentry_p) -{ - 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); - *dentry_p = dentry; - return inode; -} - -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; - if (!gadgetfs_create_file (sb, CHIP, - dev, &dev_init_operations, - &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/Kconfig b/drivers/usb/gadget/legacy/Kconfig new file mode 100644 index 0000000..aa376f0 --- /dev/null +++ b/drivers/usb/gadget/legacy/Kconfig @@ -0,0 +1,475 @@ +# +# 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 + boolean "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 + 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 + 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 + 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 + 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 + 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 0000000..fbb32aa --- /dev/null +++ b/drivers/usb/gadget/legacy/Makefile @@ -0,0 +1,42 @@ +# +# USB gadget drivers +# + +ccflags-y := -I$(PWD)/drivers/usb/gadget/ + +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 0000000..c30b7b5 --- /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 + * + * 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 +#include + +#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 __init 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 __init 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 __exit 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 __refdata 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 = __exit_p(acm_ms_unbind), +}; + +module_usb_composite_driver(acm_ms_driver); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Klaus Schwarzkopf "); +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 0000000..6eb695e --- /dev/null +++ b/drivers/usb/gadget/legacy/audio.c @@ -0,0 +1,180 @@ +/* + * audio.c -- Audio gadget driver + * + * Copyright (C) 2008 Bryan Wu + * Copyright (C) 2008 Analog Devices, Inc + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + */ + +/* #define VERBOSE_DEBUG */ + +#include +#include +#include + +#include "gadget_chips.h" +#define DRIVER_DESC "Linux USB Audio Gadget" +#define DRIVER_VERSION "Feb 2, 2012" + +USB_GADGET_COMPOSITE_OPTIONS(); + +/* 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, +}; + +#ifdef CONFIG_GADGET_UAC1 +#include "u_uac1.h" +#include "u_uac1.c" +#include "f_uac1.c" +#else +#include "f_uac2.c" +#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 __init audio_do_config(struct usb_configuration *c) +{ + /* 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; + } + + audio_bind_config(c); + + return 0; +} + +static struct usb_configuration audio_config_driver = { + .label = DRIVER_DESC, + .bConfigurationValue = 1, + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, +#ifndef CONFIG_GADGET_UAC1 + .unbind = uac2_unbind_config, +#endif +}; + +/*-------------------------------------------------------------------------*/ + +static int __init audio_bind(struct usb_composite_dev *cdev) +{ + int status; + + 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: + return status; +} + +static int __exit audio_unbind(struct usb_composite_dev *cdev) +{ +#ifdef CONFIG_GADGET_UAC1 + gaudio_cleanup(); +#endif + return 0; +} + +static __refdata struct usb_composite_driver audio_driver = { + .name = "g_audio", + .dev = &device_desc, + .strings = audio_strings, + .max_speed = USB_SPEED_HIGH, + .bind = audio_bind, + .unbind = __exit_p(audio_unbind), +}; + +module_usb_composite_driver(audio_driver); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Bryan Wu "); +MODULE_LICENSE("GPL"); + diff --git a/drivers/usb/gadget/legacy/cdc2.c b/drivers/usb/gadget/legacy/cdc2.c new file mode 100644 index 0000000..2e85d94 --- /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 +#include + +#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 __init 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 __init 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 __exit 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 __refdata struct usb_composite_driver cdc_driver = { + .name = "g_cdc", + .dev = &device_desc, + .strings = dev_strings, + .max_speed = USB_SPEED_HIGH, + .bind = cdc_bind, + .unbind = __exit_p(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 0000000..986fc51 --- /dev/null +++ b/drivers/usb/gadget/legacy/dbgp.c @@ -0,0 +1,434 @@ +/* + * dbgp.c -- EHCI Debug Port device gadget + * + * Copyright (C) 2010 Stephane Duverger + * + * Released under the GPLv2. + */ + +/* verbose messages */ +#include +#include +#include +#include +#include + +#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); +#endif + if (dbgp.req) { + kfree(dbgp.req->buf); + usb_ep_free_request(gadget->ep0, dbgp.req); + } + + gadget->ep0->driver_data = NULL; +} + +#ifdef CONFIG_USB_G_DBGP_SERIAL +static unsigned char tty_line; +#endif + +static int __init 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; + + if (gserial_alloc_line(&tty_line)) { + stp = 3; + goto fail_3; + } + + return 0; + +fail_3: + dbgp.o_ep->driver_data = NULL; +#else + return 0; +#endif +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 __init 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; + } +#endif + err = dbgp_configure_endpoints(gadget); + if (err < 0) { + stp = 4; + 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 = 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 __refdata struct usb_gadget_driver dbgp_driver = { + .function = "dbgp", + .max_speed = USB_SPEED_HIGH, + .bind = dbgp_bind, + .unbind = dbgp_unbind, + .setup = dbgp_setup, + .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 0000000..c5fdc61 --- /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 +#include + +#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 + +#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 __init 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 __init 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 __init 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, ð_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 __exit 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 __refdata struct usb_composite_driver eth_driver = { + .name = "g_ether", + .dev = &device_desc, + .strings = dev_strings, + .max_speed = USB_SPEED_SUPER, + .bind = eth_bind, + .unbind = __exit_p(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 0000000..06acfa5 --- /dev/null +++ b/drivers/usb/gadget/legacy/g_ffs.c @@ -0,0 +1,582 @@ +/* + * g_ffs.c -- user mode file system API for USB composite function controllers + * + * Copyright (C) 2010 Samsung Electronics + * Author: Michal Nazarewicz + * + * 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 + +#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS +#include + +# 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; +} 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 __refdata 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 0; +} + +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)) + 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 0000000..3d696b8 --- /dev/null +++ b/drivers/usb/gadget/legacy/gmidi.c @@ -0,0 +1,166 @@ +/* + * gmidi.c -- USB MIDI Gadget Driver + * + * Copyright (C) 2006 Thumtronics Pty Ltd. + * Developed for Thumtronics by Grey Innovation + * Ben Williamson + * + * 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 +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "gadget_chips.h" + +#include "f_midi.c" + +/*-------------------------------------------------------------------------*/ + +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 int __exit midi_unbind(struct usb_composite_dev *dev) +{ + 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 __init midi_bind_config(struct usb_configuration *c) +{ + return f_midi_bind_config(c, index, id, + in_ports, out_ports, + buflen, qlen); +} + +static int __init midi_bind(struct usb_composite_dev *cdev) +{ + int status; + + 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; + midi_config.iConfiguration = strings_dev[STRING_DESCRIPTION_IDX].id; + + status = usb_add_config(cdev, &midi_config, midi_bind_config); + if (status < 0) + return status; + usb_composite_overwrite_options(cdev, &coverwrite); + pr_info("%s\n", longname); + return 0; +} + +static __refdata struct usb_composite_driver midi_driver = { + .name = (char *) longname, + .dev = &device_desc, + .strings = dev_strings, + .max_speed = USB_SPEED_HIGH, + .bind = midi_bind, + .unbind = __exit_p(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 0000000..778613eb --- /dev/null +++ b/drivers/usb/gadget/legacy/hid.c @@ -0,0 +1,266 @@ +/* + * hid.c -- HID Composite driver + * + * Based on multi.c + * + * Copyright (C) 2010 Fabien Chouteau + * + * 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 +#include +#include +#include +#include + +#include "gadget_chips.h" +#define DRIVER_DESC "HID Gadget" +#define DRIVER_VERSION "2010/03/16" + +/*-------------------------------------------------------------------------*/ + +#define HIDG_VENDOR_NUM 0x0525 /* XXX NetChip */ +#define HIDG_PRODUCT_NUM 0xa4ac /* Linux-USB HID gadget */ + +/*-------------------------------------------------------------------------*/ + +/* + * kbuild is not very cooperative with respect to linking separately + * compiled library objects into one module. So for now we won't use + * separate compilation ... ensuring init/exit sections work to shrink + * the runtime footprint, and giving us at least some parts of what + * a "gcc --combine ... part1.c part2.c part3.c ... " build would. + */ +#include "f_hid.c" + + +struct hidg_func_node { + 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 __init do_config(struct usb_configuration *c) +{ + struct hidg_func_node *e; + int func = 0, 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) { + status = hidg_bind_config(c, e->func, func++); + if (status) + break; + } + + return status; +} + +static struct usb_configuration config_driver = { + .label = "HID Gadget", + .bConfigurationValue = 1, + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, +}; + +/****************************** Gadget Bind ******************************/ + +static int __init hid_bind(struct usb_composite_dev *cdev) +{ + struct usb_gadget *gadget = cdev->gadget; + struct list_head *tmp; + int status, funcs = 0; + + list_for_each(tmp, &hidg_func_list) + funcs++; + + if (!funcs) + return -ENODEV; + + /* set up HID */ + status = ghid_setup(cdev->gadget, funcs); + if (status < 0) + return 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; + + /* register our configuration */ + status = usb_add_config(cdev, &config_driver, do_config); + if (status < 0) + return status; + + usb_composite_overwrite_options(cdev, &coverwrite); + dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); + + return 0; +} + +static int __exit hid_unbind(struct usb_composite_dev *cdev) +{ + ghid_cleanup(); + return 0; +} + +static int __init 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 __refdata struct usb_composite_driver hidg_driver = { + .name = "g_hid", + .dev = &device_desc, + .strings = dev_strings, + .max_speed = USB_SPEED_HIGH, + .bind = hid_bind, + .unbind = __exit_p(hid_unbind), +}; + +static struct platform_driver hidg_plat_driver = { + .remove = hidg_plat_driver_remove, + .driver = { + .owner = THIS_MODULE, + .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) +{ + platform_driver_unregister(&hidg_plat_driver); + usb_composite_unregister(&hidg_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 0000000..ee6c164 --- /dev/null +++ b/drivers/usb/gadget/legacy/inode.c @@ -0,0 +1,2142 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + + +/* + * 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"); + + +/*----------------------------------------------------------------------*/ + +#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; + struct inode *inode; +}; + +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) +{ + int val; + + if (f_flags & O_NONBLOCK) { + if (!mutex_trylock(&epdata->lock)) + goto nonblock; + if (epdata->state != STATE_EP_ENABLED) { + 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: + break; + // case STATE_EP_DISABLED: /* "can't happen" */ + // case STATE_EP_READY: /* "can't happen" */ + default: /* error! */ + pr_debug ("%s: ep %p not available, state %d\n", + shortname, epdata, epdata->state); + // FALLTHROUGH + case STATE_EP_UNBOUND: /* clean disconnect */ + val = -ENODEV; + mutex_unlock(&epdata->lock); + } + return val; +} + +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; +} + + +/* handle a synchronous OUT bulk/intr/iso transfer */ +static ssize_t +ep_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr) +{ + struct ep_data *data = fd->private_data; + void *kbuf; + ssize_t value; + + if ((value = get_ready_ep (fd->f_flags, data)) < 0) + return value; + + /* halt any endpoint by doing a "wrong direction" i/o call */ + if (usb_endpoint_dir_in(&data->desc)) { + if (usb_endpoint_xfer_isoc(&data->desc)) { + mutex_unlock(&data->lock); + return -EINVAL; + } + DBG (data->dev, "%s halt\n", data->name); + spin_lock_irq (&data->dev->lock); + if (likely (data->ep != NULL)) + usb_ep_set_halt (data->ep); + spin_unlock_irq (&data->dev->lock); + mutex_unlock(&data->lock); + return -EBADMSG; + } + + /* FIXME readahead for O_NONBLOCK and poll(); careful with ZLPs */ + + value = -ENOMEM; + kbuf = kmalloc (len, GFP_KERNEL); + if (unlikely (!kbuf)) + goto free1; + + value = ep_io (data, kbuf, len); + VDEBUG (data->dev, "%s read %zu OUT, status %d\n", + data->name, len, (int) value); + if (value >= 0 && copy_to_user (buf, kbuf, value)) + value = -EFAULT; + +free1: + mutex_unlock(&data->lock); + kfree (kbuf); + return value; +} + +/* handle a synchronous IN bulk/intr/iso transfer */ +static ssize_t +ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) +{ + struct ep_data *data = fd->private_data; + void *kbuf; + ssize_t value; + + if ((value = get_ready_ep (fd->f_flags, data)) < 0) + return value; + + /* halt any endpoint by doing a "wrong direction" i/o call */ + if (!usb_endpoint_dir_in(&data->desc)) { + if (usb_endpoint_xfer_isoc(&data->desc)) { + mutex_unlock(&data->lock); + return -EINVAL; + } + DBG (data->dev, "%s halt\n", data->name); + spin_lock_irq (&data->dev->lock); + if (likely (data->ep != NULL)) + usb_ep_set_halt (data->ep); + spin_unlock_irq (&data->dev->lock); + mutex_unlock(&data->lock); + return -EBADMSG; + } + + /* FIXME writebehind for O_NONBLOCK and poll(), qlen = 1 */ + + value = -ENOMEM; + kbuf = memdup_user(buf, len); + if (!kbuf) { + value = PTR_ERR(kbuf); + goto free1; + } + + value = ep_io (data, kbuf, len); + VDEBUG (data->dev, "%s write %zu IN, status %d\n", + data->name, len, (int) value); +free1: + mutex_unlock(&data->lock); + 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)) < 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; + const struct iovec *iv; + unsigned long nr_segs; + 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 ssize_t ep_copy_to_user(struct kiocb_priv *priv) +{ + ssize_t len, total; + void *to_copy; + int i; + + /* copy stuff into user buffers */ + total = priv->actual; + len = 0; + to_copy = priv->buf; + for (i=0; i < priv->nr_segs; i++) { + ssize_t this = min((ssize_t)(priv->iv[i].iov_len), total); + + if (copy_to_user(priv->iv[i].iov_base, to_copy, this)) { + if (len == 0) + len = -EFAULT; + break; + } + + total -= this; + len += this; + to_copy += this; + if (total == 0) + break; + } + + return len; +} + +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 = ep_copy_to_user(priv); + unuse_mm(mm); + + /* completing the iocb can drop the ctx and mm, don't touch mm after */ + aio_complete(iocb, ret, ret); + + kfree(priv->buf); + 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->iv == NULL || unlikely(req->actual == 0)) { + kfree(req->buf); + kfree(priv); + iocb->private = NULL; + /* aio_complete() reports bytes-transferred _and_ faults */ + aio_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; + schedule_work(&priv->work); + } + spin_unlock(&epdata->dev->lock); + + usb_ep_free_request(ep, req); + put_ep(epdata); +} + +static ssize_t +ep_aio_rwtail( + struct kiocb *iocb, + char *buf, + size_t len, + struct ep_data *epdata, + const struct iovec *iv, + unsigned long nr_segs +) +{ + struct kiocb_priv *priv; + struct usb_request *req; + ssize_t value; + + priv = kmalloc(sizeof *priv, GFP_KERNEL); + if (!priv) { + value = -ENOMEM; +fail: + kfree(buf); + return value; + } + iocb->private = priv; + priv->iocb = iocb; + priv->iv = iv; + priv->nr_segs = nr_segs; + INIT_WORK(&priv->work, ep_user_copy_worker); + + value = get_ready_ep(iocb->ki_filp->f_flags, epdata); + if (unlikely(value < 0)) { + kfree(priv); + goto fail; + } + + 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); + if (likely(epdata->ep)) { + req = usb_ep_alloc_request(epdata->ep, GFP_ATOMIC); + if (likely(req)) { + 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); + } else + value = -EAGAIN; + } else + value = -ENODEV; + spin_unlock_irq(&epdata->dev->lock); + + mutex_unlock(&epdata->lock); + + if (unlikely(value)) { + kfree(priv); + put_ep(epdata); + } else + value = -EIOCBQUEUED; + return value; +} + +static ssize_t +ep_aio_read(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t o) +{ + struct ep_data *epdata = iocb->ki_filp->private_data; + char *buf; + + if (unlikely(usb_endpoint_dir_in(&epdata->desc))) + return -EINVAL; + + buf = kmalloc(iocb->ki_nbytes, GFP_KERNEL); + if (unlikely(!buf)) + return -ENOMEM; + + return ep_aio_rwtail(iocb, buf, iocb->ki_nbytes, epdata, iov, nr_segs); +} + +static ssize_t +ep_aio_write(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t o) +{ + struct ep_data *epdata = iocb->ki_filp->private_data; + char *buf; + size_t len = 0; + int i = 0; + + if (unlikely(!usb_endpoint_dir_in(&epdata->desc))) + return -EINVAL; + + buf = kmalloc(iocb->ki_nbytes, GFP_KERNEL); + if (unlikely(!buf)) + return -ENOMEM; + + for (i=0; i < nr_segs; i++) { + if (unlikely(copy_from_user(&buf[len], iov[i].iov_base, + iov[i].iov_len) != 0)) { + kfree(buf); + return -EFAULT; + } + len += iov[i].iov_len; + } + return ep_aio_rwtail(iocb, buf, len, epdata, NULL, 0); +} + +/*----------------------------------------------------------------------*/ + +/* used after endpoint configuration */ +static const struct file_operations ep_io_operations = { + .owner = THIS_MODULE, + .llseek = no_llseek, + + .read = ep_read, + .write = ep_write, + .unlocked_ioctl = ep_ioctl, + .release = ep_release, + + .aio_read = ep_aio_read, + .aio_write = ep_aio_write, +}; + +/* 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 file *fd, const char __user *buf, size_t len, loff_t *ptr) +{ + struct ep_data *data = fd->private_data; + struct usb_ep *ep; + u32 tag; + int value, length = len; + + value = mutex_lock_interruptible(&data->lock); + if (value < 0) + return value; + + 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 */ + if (copy_from_user (&tag, buf, 4)) { + goto fail1; + } + 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 */ + if (copy_from_user (&data->desc, buf, USB_DT_ENDPOINT_SIZE)) { + goto fail1; + } + 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; + if (copy_from_user (&data->hs_desc, buf + USB_DT_ENDPOINT_SIZE, + USB_DT_ENDPOINT_SIZE)) { + goto fail1; + } + 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; + value = usb_ep_enable(ep); + if (value == 0) + data->state = STATE_EP_ENABLED; + break; + case USB_SPEED_HIGH: + /* fails if caller didn't provide that descriptor... */ + ep->desc = &data->hs_desc; + value = usb_ep_enable(ep); + if (value == 0) + data->state = STATE_EP_ENABLED; + break; + default: + DBG(data->dev, "unconnected, %s init abandoned\n", + data->name); + value = -EINVAL; + } + if (value == 0) { + fd->f_op = &ep_io_operations; + value = length; + } +gone: + spin_unlock_irq (&data->dev->lock); + if (value < 0) { +fail: + data->desc.bDescriptorType = 0; + data->hs_desc.bDescriptorType = 0; + } + mutex_unlock(&data->lock); + return value; +fail0: + value = -EINVAL; + goto fail; +fail1: + value = -EFAULT; + 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; +} + +/* used before endpoint configuration */ +static const struct file_operations ep_config_operations = { + .llseek = no_llseek, + + .open = ep_open, + .write = ep_config, + .release = ep_release, +}; + +/*----------------------------------------------------------------------*/ + +/* 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); + + /* 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; + + spin_lock_irq (&dev->lock); + + /* 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); + + spin_unlock_irq (&dev->lock); + 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; + 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; + + 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; +} + +/* used after device configuration */ +static const struct file_operations ep0_io_operations = { + .owner = THIS_MODULE, + .llseek = no_llseek, + + .read = ep0_read, + .write = ep0_write, + .fasync = ep0_fasync, + .poll = ep0_poll, + .unlocked_ioctl = dev_ioctl, + .release = dev_release, +}; + +/*----------------------------------------------------------------------*/ + +/* 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 = dentry->d_parent->d_inode; + + /* 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 inode * +gadgetfs_create_file (struct super_block *sb, char const *name, + void *data, const struct file_operations *fops, + struct dentry **dentry_p); + +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->inode = gadgetfs_create_file (dev->sb, data->name, + data, &ep_config_operations, + &data->dentry); + if (!data->inode) + 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, + .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; + + 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. + */ + fd->f_op = &ep0_io_operations; + 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 dev_init_operations = { + .llseek = no_llseek, + + .open = dev_open, + .write = dev_config, + .fasync = ep0_fasync, + .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 inode * +gadgetfs_create_file (struct super_block *sb, char const *name, + void *data, const struct file_operations *fops, + struct dentry **dentry_p) +{ + 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); + *dentry_p = dentry; + return inode; +} + +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; + if (!gadgetfs_create_file (sb, CHIP, + dev, &dev_init_operations, + &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 0000000..8e27a8c --- /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 + * 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 +#include +#include + +/*-------------------------------------------------------------------------*/ + +#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 __init 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 __init 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 __refdata 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 0000000..39d27bb --- /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 +#include +#include + +#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 __init 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 __init 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 __exit 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 __refdata struct usb_composite_driver multi_driver = { + .name = "g_multi", + .dev = &device_desc, + .strings = dev_strings, + .max_speed = USB_SPEED_HIGH, + .bind = multi_bind, + .unbind = __exit_p(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 0000000..e90e23d --- /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 + * + * 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 +#include +#include + +#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 __init 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 __init 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 __exit 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 __refdata struct usb_composite_driver ncm_driver = { + .name = "g_ncm", + .dev = &device_desc, + .strings = dev_strings, + .max_speed = USB_SPEED_HIGH, + .bind = gncm_bind, + .unbind = __exit_p(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 0000000..9b8fd70 --- /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 + * + * 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 +#include +#include + +#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 __init 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 __init 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 __exit 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 __refdata struct usb_composite_driver nokia_driver = { + .name = "g_nokia", + .dev = &device_desc, + .strings = dev_strings, + .max_speed = USB_SPEED_HIGH, + .bind = nokia_bind, + .unbind = __exit_p(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 0000000..6474081 --- /dev/null +++ b/drivers/usb/gadget/legacy/printer.c @@ -0,0 +1,1305 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "gadget_chips.h" + +USB_GADGET_COMPOSITE_OPTIONS(); + +#define DRIVER_DESC "Printer Gadget" +#define DRIVER_VERSION "2007 OCT 06" + +static DEFINE_MUTEX(printer_mutex); +static const char shortname [] = "printer"; +static const char driver_desc [] = DRIVER_DESC; + +static dev_t g_printer_devno; + +static struct class *usb_gadget_class; + +/*-------------------------------------------------------------------------*/ + +struct printer_dev { + spinlock_t lock; /* lock this structure */ + /* lock buffer lists during read/write calls */ + struct mutex lock_printer_io; + struct usb_gadget *gadget; + s8 interface; + struct usb_ep *in_ep, *out_ep; + + struct list_head rx_reqs; /* List of free RX structs */ + struct list_head rx_reqs_active; /* List of Active RX xfers */ + struct list_head rx_buffers; /* List of completed xfers */ + /* wait until there is data to be read. */ + wait_queue_head_t rx_wait; + struct list_head tx_reqs; /* List of free TX structs */ + struct list_head tx_reqs_active; /* List of Active TX xfers */ + /* Wait until there are write buffers available to use. */ + wait_queue_head_t tx_wait; + /* Wait until all write buffers have been sent. */ + wait_queue_head_t tx_flush_wait; + struct usb_request *current_rx_req; + size_t current_rx_bytes; + u8 *current_rx_buf; + u8 printer_status; + u8 reset_printer; + struct cdev printer_cdev; + struct device *pdev; + u8 printer_cdev_open; + wait_queue_head_t wait; + struct usb_function function; +}; + +static struct printer_dev usb_printer_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. + */ +#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 + +/*-------------------------------------------------------------------------*/ + +/* + * DESCRIPTORS ... most are static, but strings and (full) configuration + * descriptors are built on demand. + */ + +/* holds our biggest descriptor */ +#define USB_DESC_BUFSIZE 256 +#define USB_BUFSIZE 8192 + +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_interface_descriptor intf_desc = { + .bLength = sizeof intf_desc, + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_PRINTER, + .bInterfaceSubClass = 1, /* Printer Sub-Class */ + .bInterfaceProtocol = 2, /* Bi-Directional */ + .iInterface = 0 +}; + +static struct usb_endpoint_descriptor fs_ep_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK +}; + +static struct usb_endpoint_descriptor fs_ep_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK +}; + +static struct usb_descriptor_header *fs_printer_function[] = { + (struct usb_descriptor_header *) &intf_desc, + (struct usb_descriptor_header *) &fs_ep_in_desc, + (struct usb_descriptor_header *) &fs_ep_out_desc, + NULL +}; + +/* + * usb 2.0 devices need to expose both high speed and full speed + * descriptors, unless they only run at full speed. + */ + +static struct usb_endpoint_descriptor hs_ep_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512) +}; + +static struct usb_endpoint_descriptor hs_ep_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512) +}; + +static struct usb_qualifier_descriptor dev_qualifier = { + .bLength = sizeof dev_qualifier, + .bDescriptorType = USB_DT_DEVICE_QUALIFIER, + .bcdUSB = cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_PRINTER, + .bNumConfigurations = 1 +}; + +static struct usb_descriptor_header *hs_printer_function[] = { + (struct usb_descriptor_header *) &intf_desc, + (struct usb_descriptor_header *) &hs_ep_in_desc, + (struct usb_descriptor_header *) &hs_ep_out_desc, + NULL +}; + +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, +}; + +/* maxpacket and other transfer characteristics vary by speed. */ +#define ep_desc(g, hs, fs) (((g)->speed == USB_SPEED_HIGH)?(hs):(fs)) + +/*-------------------------------------------------------------------------*/ + +/* descriptors that are built on-demand */ + +static char product_desc [40] = DRIVER_DESC; +static char serial_num [40] = "1"; +static char pnp_string [1024] = + "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_request * +printer_req_alloc(struct usb_ep *ep, unsigned len, gfp_t gfp_flags) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, gfp_flags); + + if (req != NULL) { + req->length = len; + req->buf = kmalloc(len, gfp_flags); + if (req->buf == NULL) { + usb_ep_free_request(ep, req); + return NULL; + } + } + + return req; +} + +static void +printer_req_free(struct usb_ep *ep, struct usb_request *req) +{ + if (ep != NULL && req != NULL) { + kfree(req->buf); + usb_ep_free_request(ep, req); + } +} + +/*-------------------------------------------------------------------------*/ + +static void rx_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct printer_dev *dev = ep->driver_data; + int status = req->status; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + + list_del_init(&req->list); /* Remode from Active List */ + + switch (status) { + + /* normal completion */ + case 0: + if (req->actual > 0) { + list_add_tail(&req->list, &dev->rx_buffers); + DBG(dev, "G_Printer : rx length %d\n", req->actual); + } else { + list_add(&req->list, &dev->rx_reqs); + } + break; + + /* software-driven interface shutdown */ + case -ECONNRESET: /* unlink */ + case -ESHUTDOWN: /* disconnect etc */ + VDBG(dev, "rx shutdown, code %d\n", status); + list_add(&req->list, &dev->rx_reqs); + break; + + /* for hardware automagic (such as pxa) */ + case -ECONNABORTED: /* endpoint reset */ + DBG(dev, "rx %s reset\n", ep->name); + list_add(&req->list, &dev->rx_reqs); + break; + + /* data overrun */ + case -EOVERFLOW: + /* FALLTHROUGH */ + + default: + DBG(dev, "rx status %d\n", status); + list_add(&req->list, &dev->rx_reqs); + break; + } + + wake_up_interruptible(&dev->rx_wait); + spin_unlock_irqrestore(&dev->lock, flags); +} + +static void tx_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct printer_dev *dev = ep->driver_data; + + switch (req->status) { + default: + VDBG(dev, "tx err %d\n", req->status); + /* FALLTHROUGH */ + case -ECONNRESET: /* unlink */ + case -ESHUTDOWN: /* disconnect etc */ + break; + case 0: + break; + } + + spin_lock(&dev->lock); + /* Take the request struct off the active list and put it on the + * free list. + */ + list_del_init(&req->list); + list_add(&req->list, &dev->tx_reqs); + wake_up_interruptible(&dev->tx_wait); + if (likely(list_empty(&dev->tx_reqs_active))) + wake_up_interruptible(&dev->tx_flush_wait); + + spin_unlock(&dev->lock); +} + +/*-------------------------------------------------------------------------*/ + +static int +printer_open(struct inode *inode, struct file *fd) +{ + struct printer_dev *dev; + unsigned long flags; + int ret = -EBUSY; + + mutex_lock(&printer_mutex); + dev = container_of(inode->i_cdev, struct printer_dev, printer_cdev); + + spin_lock_irqsave(&dev->lock, flags); + + if (!dev->printer_cdev_open) { + dev->printer_cdev_open = 1; + fd->private_data = dev; + ret = 0; + /* Change the printer status to show that it's on-line. */ + dev->printer_status |= PRINTER_SELECTED; + } + + spin_unlock_irqrestore(&dev->lock, flags); + + DBG(dev, "printer_open returned %x\n", ret); + mutex_unlock(&printer_mutex); + return ret; +} + +static int +printer_close(struct inode *inode, struct file *fd) +{ + struct printer_dev *dev = fd->private_data; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + dev->printer_cdev_open = 0; + fd->private_data = NULL; + /* Change printer status to show that the printer is off-line. */ + dev->printer_status &= ~PRINTER_SELECTED; + spin_unlock_irqrestore(&dev->lock, flags); + + DBG(dev, "printer_close\n"); + + return 0; +} + +/* This function must be called with interrupts turned off. */ +static void +setup_rx_reqs(struct printer_dev *dev) +{ + struct usb_request *req; + + while (likely(!list_empty(&dev->rx_reqs))) { + int error; + + req = container_of(dev->rx_reqs.next, + struct usb_request, list); + list_del_init(&req->list); + + /* The USB Host sends us whatever amount of data it wants to + * so we always set the length field to the full USB_BUFSIZE. + * If the amount of data is more than the read() caller asked + * for it will be stored in the request buffer until it is + * asked for by read(). + */ + req->length = USB_BUFSIZE; + req->complete = rx_complete; + + /* here, we unlock, and only unlock, to avoid deadlock. */ + spin_unlock(&dev->lock); + error = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC); + spin_lock(&dev->lock); + if (error) { + DBG(dev, "rx submit --> %d\n", error); + list_add(&req->list, &dev->rx_reqs); + break; + } + /* if the req is empty, then add it into dev->rx_reqs_active. */ + else if (list_empty(&req->list)) { + list_add(&req->list, &dev->rx_reqs_active); + } + } +} + +static ssize_t +printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr) +{ + struct printer_dev *dev = fd->private_data; + unsigned long flags; + size_t size; + size_t bytes_copied; + struct usb_request *req; + /* This is a pointer to the current USB rx request. */ + struct usb_request *current_rx_req; + /* This is the number of bytes in the current rx buffer. */ + size_t current_rx_bytes; + /* This is a pointer to the current rx buffer. */ + u8 *current_rx_buf; + + if (len == 0) + return -EINVAL; + + DBG(dev, "printer_read trying to read %d bytes\n", (int)len); + + mutex_lock(&dev->lock_printer_io); + spin_lock_irqsave(&dev->lock, flags); + + /* We will use this flag later to check if a printer reset happened + * after we turn interrupts back on. + */ + dev->reset_printer = 0; + + setup_rx_reqs(dev); + + bytes_copied = 0; + current_rx_req = dev->current_rx_req; + current_rx_bytes = dev->current_rx_bytes; + current_rx_buf = dev->current_rx_buf; + dev->current_rx_req = NULL; + dev->current_rx_bytes = 0; + dev->current_rx_buf = NULL; + + /* Check if there is any data in the read buffers. Please note that + * current_rx_bytes is the number of bytes in the current rx buffer. + * If it is zero then check if there are any other rx_buffers that + * are on the completed list. We are only out of data if all rx + * buffers are empty. + */ + if ((current_rx_bytes == 0) && + (likely(list_empty(&dev->rx_buffers)))) { + /* Turn interrupts back on before sleeping. */ + spin_unlock_irqrestore(&dev->lock, flags); + + /* + * If no data is available check if this is a NON-Blocking + * call or not. + */ + if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) { + mutex_unlock(&dev->lock_printer_io); + return -EAGAIN; + } + + /* Sleep until data is available */ + wait_event_interruptible(dev->rx_wait, + (likely(!list_empty(&dev->rx_buffers)))); + spin_lock_irqsave(&dev->lock, flags); + } + + /* We have data to return then copy it to the caller's buffer.*/ + while ((current_rx_bytes || likely(!list_empty(&dev->rx_buffers))) + && len) { + if (current_rx_bytes == 0) { + req = container_of(dev->rx_buffers.next, + struct usb_request, list); + list_del_init(&req->list); + + if (req->actual && req->buf) { + current_rx_req = req; + current_rx_bytes = req->actual; + current_rx_buf = req->buf; + } else { + list_add(&req->list, &dev->rx_reqs); + continue; + } + } + + /* Don't leave irqs off while doing memory copies */ + spin_unlock_irqrestore(&dev->lock, flags); + + if (len > current_rx_bytes) + size = current_rx_bytes; + else + size = len; + + size -= copy_to_user(buf, current_rx_buf, size); + bytes_copied += size; + len -= size; + buf += size; + + spin_lock_irqsave(&dev->lock, flags); + + /* We've disconnected or reset so return. */ + if (dev->reset_printer) { + list_add(¤t_rx_req->list, &dev->rx_reqs); + spin_unlock_irqrestore(&dev->lock, flags); + mutex_unlock(&dev->lock_printer_io); + return -EAGAIN; + } + + /* If we not returning all the data left in this RX request + * buffer then adjust the amount of data left in the buffer. + * Othewise if we are done with this RX request buffer then + * requeue it to get any incoming data from the USB host. + */ + if (size < current_rx_bytes) { + current_rx_bytes -= size; + current_rx_buf += size; + } else { + list_add(¤t_rx_req->list, &dev->rx_reqs); + current_rx_bytes = 0; + current_rx_buf = NULL; + current_rx_req = NULL; + } + } + + dev->current_rx_req = current_rx_req; + dev->current_rx_bytes = current_rx_bytes; + dev->current_rx_buf = current_rx_buf; + + spin_unlock_irqrestore(&dev->lock, flags); + mutex_unlock(&dev->lock_printer_io); + + DBG(dev, "printer_read returned %d bytes\n", (int)bytes_copied); + + if (bytes_copied) + return bytes_copied; + else + return -EAGAIN; +} + +static ssize_t +printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr) +{ + struct printer_dev *dev = fd->private_data; + unsigned long flags; + size_t size; /* Amount of data in a TX request. */ + size_t bytes_copied = 0; + struct usb_request *req; + + DBG(dev, "printer_write trying to send %d bytes\n", (int)len); + + if (len == 0) + return -EINVAL; + + mutex_lock(&dev->lock_printer_io); + spin_lock_irqsave(&dev->lock, flags); + + /* Check if a printer reset happens while we have interrupts on */ + dev->reset_printer = 0; + + /* Check if there is any available write buffers */ + if (likely(list_empty(&dev->tx_reqs))) { + /* Turn interrupts back on before sleeping. */ + spin_unlock_irqrestore(&dev->lock, flags); + + /* + * If write buffers are available check if this is + * a NON-Blocking call or not. + */ + if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) { + mutex_unlock(&dev->lock_printer_io); + return -EAGAIN; + } + + /* Sleep until a write buffer is available */ + wait_event_interruptible(dev->tx_wait, + (likely(!list_empty(&dev->tx_reqs)))); + spin_lock_irqsave(&dev->lock, flags); + } + + while (likely(!list_empty(&dev->tx_reqs)) && len) { + + if (len > USB_BUFSIZE) + size = USB_BUFSIZE; + else + size = len; + + req = container_of(dev->tx_reqs.next, struct usb_request, + list); + list_del_init(&req->list); + + req->complete = tx_complete; + req->length = size; + + /* Check if we need to send a zero length packet. */ + if (len > size) + /* They will be more TX requests so no yet. */ + req->zero = 0; + else + /* If the data amount is not a multple of the + * maxpacket size then send a zero length packet. + */ + req->zero = ((len % dev->in_ep->maxpacket) == 0); + + /* Don't leave irqs off while doing memory copies */ + spin_unlock_irqrestore(&dev->lock, flags); + + if (copy_from_user(req->buf, buf, size)) { + list_add(&req->list, &dev->tx_reqs); + mutex_unlock(&dev->lock_printer_io); + return bytes_copied; + } + + bytes_copied += size; + len -= size; + buf += size; + + spin_lock_irqsave(&dev->lock, flags); + + /* We've disconnected or reset so free the req and buffer */ + if (dev->reset_printer) { + list_add(&req->list, &dev->tx_reqs); + spin_unlock_irqrestore(&dev->lock, flags); + mutex_unlock(&dev->lock_printer_io); + return -EAGAIN; + } + + if (usb_ep_queue(dev->in_ep, req, GFP_ATOMIC)) { + list_add(&req->list, &dev->tx_reqs); + spin_unlock_irqrestore(&dev->lock, flags); + mutex_unlock(&dev->lock_printer_io); + return -EAGAIN; + } + + list_add(&req->list, &dev->tx_reqs_active); + + } + + spin_unlock_irqrestore(&dev->lock, flags); + mutex_unlock(&dev->lock_printer_io); + + DBG(dev, "printer_write sent %d bytes\n", (int)bytes_copied); + + if (bytes_copied) { + return bytes_copied; + } else { + return -EAGAIN; + } +} + +static int +printer_fsync(struct file *fd, loff_t start, loff_t end, int datasync) +{ + struct printer_dev *dev = fd->private_data; + struct inode *inode = file_inode(fd); + unsigned long flags; + int tx_list_empty; + + mutex_lock(&inode->i_mutex); + spin_lock_irqsave(&dev->lock, flags); + tx_list_empty = (likely(list_empty(&dev->tx_reqs))); + spin_unlock_irqrestore(&dev->lock, flags); + + if (!tx_list_empty) { + /* Sleep until all data has been sent */ + wait_event_interruptible(dev->tx_flush_wait, + (likely(list_empty(&dev->tx_reqs_active)))); + } + mutex_unlock(&inode->i_mutex); + + return 0; +} + +static unsigned int +printer_poll(struct file *fd, poll_table *wait) +{ + struct printer_dev *dev = fd->private_data; + unsigned long flags; + int status = 0; + + mutex_lock(&dev->lock_printer_io); + spin_lock_irqsave(&dev->lock, flags); + setup_rx_reqs(dev); + spin_unlock_irqrestore(&dev->lock, flags); + mutex_unlock(&dev->lock_printer_io); + + poll_wait(fd, &dev->rx_wait, wait); + poll_wait(fd, &dev->tx_wait, wait); + + spin_lock_irqsave(&dev->lock, flags); + if (likely(!list_empty(&dev->tx_reqs))) + status |= POLLOUT | POLLWRNORM; + + if (likely(dev->current_rx_bytes) || + likely(!list_empty(&dev->rx_buffers))) + status |= POLLIN | POLLRDNORM; + + spin_unlock_irqrestore(&dev->lock, flags); + + return status; +} + +static long +printer_ioctl(struct file *fd, unsigned int code, unsigned long arg) +{ + struct printer_dev *dev = fd->private_data; + unsigned long flags; + int status = 0; + + DBG(dev, "printer_ioctl: cmd=0x%4.4x, arg=%lu\n", code, arg); + + /* handle ioctls */ + + spin_lock_irqsave(&dev->lock, flags); + + switch (code) { + case GADGET_GET_PRINTER_STATUS: + status = (int)dev->printer_status; + break; + case GADGET_SET_PRINTER_STATUS: + dev->printer_status = (u8)arg; + break; + default: + /* could not handle ioctl */ + DBG(dev, "printer_ioctl: ERROR cmd=0x%4.4xis not supported\n", + code); + status = -ENOTTY; + } + + spin_unlock_irqrestore(&dev->lock, flags); + + return status; +} + +/* used after endpoint configuration */ +static const struct file_operations printer_io_operations = { + .owner = THIS_MODULE, + .open = printer_open, + .read = printer_read, + .write = printer_write, + .fsync = printer_fsync, + .poll = printer_poll, + .unlocked_ioctl = printer_ioctl, + .release = printer_close, + .llseek = noop_llseek, +}; + +/*-------------------------------------------------------------------------*/ + +static int +set_printer_interface(struct printer_dev *dev) +{ + int result = 0; + + dev->in_ep->desc = ep_desc(dev->gadget, &hs_ep_in_desc, &fs_ep_in_desc); + dev->in_ep->driver_data = dev; + + dev->out_ep->desc = ep_desc(dev->gadget, &hs_ep_out_desc, + &fs_ep_out_desc); + dev->out_ep->driver_data = dev; + + result = usb_ep_enable(dev->in_ep); + if (result != 0) { + DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result); + goto done; + } + + result = usb_ep_enable(dev->out_ep); + if (result != 0) { + DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result); + goto done; + } + +done: + /* on error, disable any endpoints */ + if (result != 0) { + (void) usb_ep_disable(dev->in_ep); + (void) usb_ep_disable(dev->out_ep); + dev->in_ep->desc = NULL; + dev->out_ep->desc = NULL; + } + + /* caller is responsible for cleanup on error */ + return result; +} + +static void printer_reset_interface(struct printer_dev *dev) +{ + if (dev->interface < 0) + return; + + DBG(dev, "%s\n", __func__); + + if (dev->in_ep->desc) + usb_ep_disable(dev->in_ep); + + if (dev->out_ep->desc) + usb_ep_disable(dev->out_ep); + + dev->in_ep->desc = NULL; + dev->out_ep->desc = NULL; + dev->interface = -1; +} + +/* Change our operational Interface. */ +static int set_interface(struct printer_dev *dev, unsigned number) +{ + int result = 0; + + /* Free the current interface */ + printer_reset_interface(dev); + + result = set_printer_interface(dev); + if (result) + printer_reset_interface(dev); + else + dev->interface = number; + + if (!result) + INFO(dev, "Using interface %x\n", number); + + return result; +} + +static void printer_soft_reset(struct printer_dev *dev) +{ + struct usb_request *req; + + INFO(dev, "Received Printer Reset Request\n"); + + if (usb_ep_disable(dev->in_ep)) + DBG(dev, "Failed to disable USB in_ep\n"); + if (usb_ep_disable(dev->out_ep)) + DBG(dev, "Failed to disable USB out_ep\n"); + + if (dev->current_rx_req != NULL) { + list_add(&dev->current_rx_req->list, &dev->rx_reqs); + dev->current_rx_req = NULL; + } + dev->current_rx_bytes = 0; + dev->current_rx_buf = NULL; + dev->reset_printer = 1; + + while (likely(!(list_empty(&dev->rx_buffers)))) { + req = container_of(dev->rx_buffers.next, struct usb_request, + list); + list_del_init(&req->list); + list_add(&req->list, &dev->rx_reqs); + } + + while (likely(!(list_empty(&dev->rx_reqs_active)))) { + req = container_of(dev->rx_buffers.next, struct usb_request, + list); + list_del_init(&req->list); + list_add(&req->list, &dev->rx_reqs); + } + + while (likely(!(list_empty(&dev->tx_reqs_active)))) { + req = container_of(dev->tx_reqs_active.next, + struct usb_request, list); + list_del_init(&req->list); + list_add(&req->list, &dev->tx_reqs); + } + + if (usb_ep_enable(dev->in_ep)) + DBG(dev, "Failed to enable USB in_ep\n"); + if (usb_ep_enable(dev->out_ep)) + DBG(dev, "Failed to enable USB out_ep\n"); + + wake_up_interruptible(&dev->rx_wait); + wake_up_interruptible(&dev->tx_wait); + wake_up_interruptible(&dev->tx_flush_wait); +} + +/*-------------------------------------------------------------------------*/ + +/* + * The setup() callback implements all the ep0 functionality that's not + * handled lower down. + */ +static int printer_func_setup(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct printer_dev *dev = container_of(f, struct printer_dev, function); + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + int value = -EOPNOTSUPP; + u16 wIndex = le16_to_cpu(ctrl->wIndex); + u16 wValue = le16_to_cpu(ctrl->wValue); + u16 wLength = le16_to_cpu(ctrl->wLength); + + DBG(dev, "ctrl req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, wValue, wIndex, wLength); + + switch (ctrl->bRequestType&USB_TYPE_MASK) { + case USB_TYPE_CLASS: + switch (ctrl->bRequest) { + case 0: /* Get the IEEE-1284 PNP String */ + /* Only one printer interface is supported. */ + if ((wIndex>>8) != dev->interface) + break; + + value = (pnp_string[0]<<8)|pnp_string[1]; + memcpy(req->buf, pnp_string, value); + DBG(dev, "1284 PNP String: %x %s\n", value, + &pnp_string[2]); + break; + + case 1: /* Get Port Status */ + /* Only one printer interface is supported. */ + if (wIndex != dev->interface) + break; + + *(u8 *)req->buf = dev->printer_status; + value = min(wLength, (u16) 1); + break; + + case 2: /* Soft Reset */ + /* Only one printer interface is supported. */ + if (wIndex != dev->interface) + break; + + printer_soft_reset(dev); + + value = 0; + break; + + default: + goto unknown; + } + break; + + default: +unknown: + VDBG(dev, + "unknown ctrl req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + wValue, wIndex, wLength); + break; + } + /* host either stalls (value < 0) or reports success */ + return value; +} + +static int __init printer_func_bind(struct usb_configuration *c, + struct usb_function *f) +{ + struct printer_dev *dev = container_of(f, struct printer_dev, function); + struct usb_composite_dev *cdev = c->cdev; + struct usb_ep *in_ep; + struct usb_ep *out_ep = NULL; + int id; + int ret; + + id = usb_interface_id(c, f); + if (id < 0) + return id; + intf_desc.bInterfaceNumber = id; + + /* all we really need is bulk IN/OUT */ + in_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_in_desc); + if (!in_ep) { +autoconf_fail: + dev_err(&cdev->gadget->dev, "can't autoconfigure on %s\n", + cdev->gadget->name); + return -ENODEV; + } + in_ep->driver_data = in_ep; /* claim */ + + out_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_out_desc); + if (!out_ep) + goto autoconf_fail; + out_ep->driver_data = out_ep; /* claim */ + + /* assumes that all endpoints are dual-speed */ + hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress; + hs_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress; + + ret = usb_assign_descriptors(f, fs_printer_function, + hs_printer_function, NULL); + if (ret) + return ret; + + dev->in_ep = in_ep; + dev->out_ep = out_ep; + return 0; +} + +static void printer_func_unbind(struct usb_configuration *c, + struct usb_function *f) +{ + usb_free_all_descriptors(f); +} + +static int printer_func_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct printer_dev *dev = container_of(f, struct printer_dev, function); + int ret = -ENOTSUPP; + + if (!alt) + ret = set_interface(dev, intf); + + return ret; +} + +static void printer_func_disable(struct usb_function *f) +{ + struct printer_dev *dev = container_of(f, struct printer_dev, function); + unsigned long flags; + + DBG(dev, "%s\n", __func__); + + spin_lock_irqsave(&dev->lock, flags); + printer_reset_interface(dev); + spin_unlock_irqrestore(&dev->lock, flags); +} + +static void printer_cfg_unbind(struct usb_configuration *c) +{ + struct printer_dev *dev; + struct usb_request *req; + + dev = &usb_printer_gadget; + + DBG(dev, "%s\n", __func__); + + /* Remove sysfs files */ + device_destroy(usb_gadget_class, g_printer_devno); + + /* Remove Character Device */ + cdev_del(&dev->printer_cdev); + + /* we must already have been disconnected ... no i/o may be active */ + WARN_ON(!list_empty(&dev->tx_reqs_active)); + WARN_ON(!list_empty(&dev->rx_reqs_active)); + + /* Free all memory for this driver. */ + while (!list_empty(&dev->tx_reqs)) { + req = container_of(dev->tx_reqs.next, struct usb_request, + list); + list_del(&req->list); + printer_req_free(dev->in_ep, req); + } + + if (dev->current_rx_req != NULL) + printer_req_free(dev->out_ep, dev->current_rx_req); + + while (!list_empty(&dev->rx_reqs)) { + req = container_of(dev->rx_reqs.next, + struct usb_request, list); + list_del(&req->list); + printer_req_free(dev->out_ep, req); + } + + while (!list_empty(&dev->rx_buffers)) { + req = container_of(dev->rx_buffers.next, + struct usb_request, list); + list_del(&req->list); + printer_req_free(dev->out_ep, req); + } +} + +static struct usb_configuration printer_cfg_driver = { + .label = "printer", + .unbind = printer_cfg_unbind, + .bConfigurationValue = 1, + .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, +}; + +static int __init printer_bind_config(struct usb_configuration *c) +{ + struct usb_gadget *gadget = c->cdev->gadget; + struct printer_dev *dev; + int status = -ENOMEM; + size_t len; + u32 i; + struct usb_request *req; + + usb_ep_autoconfig_reset(gadget); + + dev = &usb_printer_gadget; + + dev->function.name = shortname; + dev->function.bind = printer_func_bind; + dev->function.setup = printer_func_setup; + dev->function.unbind = printer_func_unbind; + dev->function.set_alt = printer_func_set_alt; + dev->function.disable = printer_func_disable; + + status = usb_add_function(c, &dev->function); + if (status) + return status; + + /* Setup the sysfs files for the printer gadget. */ + dev->pdev = device_create(usb_gadget_class, NULL, g_printer_devno, + NULL, "g_printer"); + if (IS_ERR(dev->pdev)) { + ERROR(dev, "Failed to create device: g_printer\n"); + status = PTR_ERR(dev->pdev); + goto fail; + } + + /* + * Register a character device as an interface to a user mode + * program that handles the printer specific functionality. + */ + cdev_init(&dev->printer_cdev, &printer_io_operations); + dev->printer_cdev.owner = THIS_MODULE; + status = cdev_add(&dev->printer_cdev, g_printer_devno, 1); + if (status) { + ERROR(dev, "Failed to open char device\n"); + goto fail; + } + + if (iPNPstring) + strlcpy(&pnp_string[2], iPNPstring, (sizeof pnp_string)-2); + + len = strlen(pnp_string); + pnp_string[0] = (len >> 8) & 0xFF; + pnp_string[1] = len & 0xFF; + + 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; + } + + spin_lock_init(&dev->lock); + mutex_init(&dev->lock_printer_io); + INIT_LIST_HEAD(&dev->tx_reqs); + INIT_LIST_HEAD(&dev->tx_reqs_active); + INIT_LIST_HEAD(&dev->rx_reqs); + INIT_LIST_HEAD(&dev->rx_reqs_active); + INIT_LIST_HEAD(&dev->rx_buffers); + init_waitqueue_head(&dev->rx_wait); + init_waitqueue_head(&dev->tx_wait); + init_waitqueue_head(&dev->tx_flush_wait); + + dev->interface = -1; + dev->printer_cdev_open = 0; + dev->printer_status = PRINTER_NOT_ERROR; + dev->current_rx_req = NULL; + dev->current_rx_bytes = 0; + dev->current_rx_buf = NULL; + + for (i = 0; i < QLEN; i++) { + req = printer_req_alloc(dev->in_ep, USB_BUFSIZE, GFP_KERNEL); + if (!req) { + while (!list_empty(&dev->tx_reqs)) { + req = container_of(dev->tx_reqs.next, + struct usb_request, list); + list_del(&req->list); + printer_req_free(dev->in_ep, req); + } + return -ENOMEM; + } + list_add(&req->list, &dev->tx_reqs); + } + + for (i = 0; i < QLEN; i++) { + req = printer_req_alloc(dev->out_ep, USB_BUFSIZE, GFP_KERNEL); + if (!req) { + while (!list_empty(&dev->rx_reqs)) { + req = container_of(dev->rx_reqs.next, + struct usb_request, list); + list_del(&req->list); + printer_req_free(dev->out_ep, req); + } + return -ENOMEM; + } + list_add(&req->list, &dev->rx_reqs); + } + + /* finish hookup to lower layer ... */ + dev->gadget = gadget; + + INFO(dev, "%s, version: " DRIVER_VERSION "\n", driver_desc); + return 0; + +fail: + printer_cfg_unbind(c); + return status; +} + +static int printer_unbind(struct usb_composite_dev *cdev) +{ + return 0; +} + +static int __init printer_bind(struct usb_composite_dev *cdev) +{ + int ret; + + ret = usb_string_ids_tab(cdev, strings); + if (ret < 0) + 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_bind_config); + if (ret) + return ret; + usb_composite_overwrite_options(cdev, &coverwrite); + return ret; +} + +static __refdata struct usb_composite_driver printer_driver = { + .name = shortname, + .dev = &device_desc, + .strings = dev_strings, + .max_speed = USB_SPEED_HIGH, + .bind = printer_bind, + .unbind = printer_unbind, +}; + +static int __init +init(void) +{ + int status; + + usb_gadget_class = class_create(THIS_MODULE, "usb_printer_gadget"); + if (IS_ERR(usb_gadget_class)) { + status = PTR_ERR(usb_gadget_class); + pr_err("unable to create usb_gadget class %d\n", status); + return status; + } + + status = alloc_chrdev_region(&g_printer_devno, 0, 1, + "USB printer gadget"); + if (status) { + pr_err("alloc_chrdev_region %d\n", status); + class_destroy(usb_gadget_class); + return status; + } + + status = usb_composite_probe(&printer_driver); + if (status) { + class_destroy(usb_gadget_class); + unregister_chrdev_region(g_printer_devno, 1); + pr_err("usb_gadget_probe_driver %x\n", status); + } + + return status; +} +module_init(init); + +static void __exit +cleanup(void) +{ + mutex_lock(&usb_printer_gadget.lock_printer_io); + usb_composite_unregister(&printer_driver); + unregister_chrdev_region(g_printer_devno, 1); + class_destroy(usb_gadget_class); + mutex_unlock(&usb_printer_gadget.lock_printer_io); +} +module_exit(cleanup); + +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 0000000..1f5f978 --- /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 +#include +#include +#include +#include + +#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 __init 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 __refdata 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 0000000..6cdb7a5 --- /dev/null +++ b/drivers/usb/gadget/legacy/tcm_usb_gadget.c @@ -0,0 +1,2473 @@ +/* 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 + * License: GPLv2 as published by FSF. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tcm_usb_gadget.h" + +USB_GADGET_COMPOSITE_OPTIONS(); + +static struct target_fabric_configfs *usbg_fabric_configfs; + +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: + 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 = MSG_HEAD_TAG; + break; + case UAS_ORDERED_TAG: + cmd->prio_attr = MSG_ORDERED_TAG; + break; + case UAS_ACA: + cmd->prio_attr = MSG_ACA_TAG; + break; + default: + pr_debug_once("Unsupported prio_attr: %02x.\n", + cmd_iu->prio_attr); + case UAS_SIMPLE_TAG: + cmd->prio_attr = MSG_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 = MSG_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_fabric_configfs->tf_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 with the + * call to __transport_register_session() + */ + __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 struct target_core_fabric_ops usbg_ops = { + .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, +}; + +static int usbg_register_configfs(void) +{ + struct target_fabric_configfs *fabric; + int ret; + + fabric = target_fabric_configfs_init(THIS_MODULE, "usb_gadget"); + if (IS_ERR(fabric)) { + printk(KERN_ERR "target_fabric_configfs_init() failed\n"); + return PTR_ERR(fabric); + } + + fabric->tf_ops = usbg_ops; + fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = usbg_wwn_attrs; + fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = usbg_base_attrs; + fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = NULL; + fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = NULL; + fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = NULL; + fabric->tf_cit_tmpl.tfc_tpg_nacl_base_cit.ct_attrs = NULL; + fabric->tf_cit_tmpl.tfc_tpg_nacl_attrib_cit.ct_attrs = NULL; + fabric->tf_cit_tmpl.tfc_tpg_nacl_auth_cit.ct_attrs = NULL; + fabric->tf_cit_tmpl.tfc_tpg_nacl_param_cit.ct_attrs = NULL; + ret = target_fabric_configfs_register(fabric); + if (ret < 0) { + printk(KERN_ERR "target_fabric_configfs_register() failed" + " for usb-gadget\n"); + return ret; + } + usbg_fabric_configfs = fabric; + return 0; +}; + +static void usbg_deregister_configfs(void) +{ + if (!(usbg_fabric_configfs)) + return; + + target_fabric_configfs_deregister(usbg_fabric_configfs); + usbg_fabric_configfs = NULL; +}; + +/* 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 __refdata 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) +{ + int ret; + + ret = usbg_register_configfs(); + return ret; +} +module_init(usb_target_gadget_init); + +static void __exit usb_target_gadget_exit(void) +{ + usbg_deregister_configfs(); +} +module_exit(usb_target_gadget_exit); + +MODULE_AUTHOR("Sebastian Andrzej Siewior "); +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 0000000..8289219 --- /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 +/* #include */ +#include +#include +#include +#include +#include +#include + +#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 0000000..a11d8e4 --- /dev/null +++ b/drivers/usb/gadget/legacy/webcam.c @@ -0,0 +1,399 @@ +/* + * 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 +#include +#include + +#include "f_uvc.h" + +/* + * Kbuild is not very cooperative with respect to linking separately + * compiled library objects into one module. So for now we won't use + * separate compilation ... ensuring init/exit sections work to shrink + * the runtime footprint, and giving us at least some parts of what + * a "gcc --combine ... part1.c part2.c part3.c ... " build would. + */ +#include "uvc_queue.c" +#include "uvc_video.c" +#include "uvc_v4l2.c" +#include "f_uvc.c" + +USB_GADGET_COMPOSITE_OPTIONS(); +/* -------------------------------------------------------------------------- + * 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_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 __init +webcam_config_bind(struct usb_configuration *c) +{ + return uvc_bind_config(c, uvc_fs_control_cls, uvc_ss_control_cls, + uvc_fs_streaming_cls, uvc_hs_streaming_cls, + uvc_ss_streaming_cls); +} + +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 /* __init_or_exit */ +webcam_unbind(struct usb_composite_dev *cdev) +{ + return 0; +} + +static int __init +webcam_bind(struct usb_composite_dev *cdev) +{ + int ret; + + /* 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: + webcam_unbind(cdev); + return ret; +} + +/* -------------------------------------------------------------------------- + * Driver + */ + +static __refdata 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 0000000..c3d4968 --- /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 infrastucture 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 +#include +#include +#include +#include +#include + +#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 __init 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 __refdata 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"); diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c deleted file mode 100644 index 8e27a8c..0000000 --- a/drivers/usb/gadget/mass_storage.c +++ /dev/null @@ -1,276 +0,0 @@ -/* - * mass_storage.c -- Mass Storage USB Gadget - * - * Copyright (C) 2003-2008 Alan Stern - * Copyright (C) 2009 Samsung Electronics - * Author: Michal Nazarewicz - * 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 -#include -#include - -/*-------------------------------------------------------------------------*/ - -#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 __init 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 __init 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 __refdata 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/multi.c b/drivers/usb/gadget/multi.c deleted file mode 100644 index 39d27bb..0000000 --- a/drivers/usb/gadget/multi.c +++ /dev/null @@ -1,510 +0,0 @@ -/* - * 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 -#include -#include - -#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 __init 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 __init 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 __exit 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 __refdata struct usb_composite_driver multi_driver = { - .name = "g_multi", - .dev = &device_desc, - .strings = dev_strings, - .max_speed = USB_SPEED_HIGH, - .bind = multi_bind, - .unbind = __exit_p(multi_unbind), - .needs_serial = 1, -}; - -module_usb_composite_driver(multi_driver); diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/ncm.c deleted file mode 100644 index e90e23d..0000000 --- a/drivers/usb/gadget/ncm.c +++ /dev/null @@ -1,211 +0,0 @@ -/* - * ncm.c -- NCM gadget driver - * - * Copyright (C) 2010 Nokia Corporation - * Contact: Yauheni Kaliuta - * - * 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 -#include -#include - -#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 __init 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 __init 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 __exit 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 __refdata struct usb_composite_driver ncm_driver = { - .name = "g_ncm", - .dev = &device_desc, - .strings = dev_strings, - .max_speed = USB_SPEED_HIGH, - .bind = gncm_bind, - .unbind = __exit_p(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/nokia.c b/drivers/usb/gadget/nokia.c deleted file mode 100644 index 9b8fd70..0000000 --- a/drivers/usb/gadget/nokia.c +++ /dev/null @@ -1,350 +0,0 @@ -/* - * nokia.c -- Nokia Composite Gadget Driver - * - * Copyright (C) 2008-2010 Nokia Corporation - * Contact: Felipe Balbi - * - * 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 -#include -#include - -#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 __init 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 __init 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 __exit 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 __refdata struct usb_composite_driver nokia_driver = { - .name = "g_nokia", - .dev = &device_desc, - .strings = dev_strings, - .max_speed = USB_SPEED_HIGH, - .bind = nokia_bind, - .unbind = __exit_p(nokia_unbind), -}; - -module_usb_composite_driver(nokia_driver); diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c deleted file mode 100644 index 6474081..0000000 --- a/drivers/usb/gadget/printer.c +++ /dev/null @@ -1,1305 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "gadget_chips.h" - -USB_GADGET_COMPOSITE_OPTIONS(); - -#define DRIVER_DESC "Printer Gadget" -#define DRIVER_VERSION "2007 OCT 06" - -static DEFINE_MUTEX(printer_mutex); -static const char shortname [] = "printer"; -static const char driver_desc [] = DRIVER_DESC; - -static dev_t g_printer_devno; - -static struct class *usb_gadget_class; - -/*-------------------------------------------------------------------------*/ - -struct printer_dev { - spinlock_t lock; /* lock this structure */ - /* lock buffer lists during read/write calls */ - struct mutex lock_printer_io; - struct usb_gadget *gadget; - s8 interface; - struct usb_ep *in_ep, *out_ep; - - struct list_head rx_reqs; /* List of free RX structs */ - struct list_head rx_reqs_active; /* List of Active RX xfers */ - struct list_head rx_buffers; /* List of completed xfers */ - /* wait until there is data to be read. */ - wait_queue_head_t rx_wait; - struct list_head tx_reqs; /* List of free TX structs */ - struct list_head tx_reqs_active; /* List of Active TX xfers */ - /* Wait until there are write buffers available to use. */ - wait_queue_head_t tx_wait; - /* Wait until all write buffers have been sent. */ - wait_queue_head_t tx_flush_wait; - struct usb_request *current_rx_req; - size_t current_rx_bytes; - u8 *current_rx_buf; - u8 printer_status; - u8 reset_printer; - struct cdev printer_cdev; - struct device *pdev; - u8 printer_cdev_open; - wait_queue_head_t wait; - struct usb_function function; -}; - -static struct printer_dev usb_printer_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. - */ -#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 - -/*-------------------------------------------------------------------------*/ - -/* - * DESCRIPTORS ... most are static, but strings and (full) configuration - * descriptors are built on demand. - */ - -/* holds our biggest descriptor */ -#define USB_DESC_BUFSIZE 256 -#define USB_BUFSIZE 8192 - -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_interface_descriptor intf_desc = { - .bLength = sizeof intf_desc, - .bDescriptorType = USB_DT_INTERFACE, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_PRINTER, - .bInterfaceSubClass = 1, /* Printer Sub-Class */ - .bInterfaceProtocol = 2, /* Bi-Directional */ - .iInterface = 0 -}; - -static struct usb_endpoint_descriptor fs_ep_in_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK -}; - -static struct usb_endpoint_descriptor fs_ep_out_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK -}; - -static struct usb_descriptor_header *fs_printer_function[] = { - (struct usb_descriptor_header *) &intf_desc, - (struct usb_descriptor_header *) &fs_ep_in_desc, - (struct usb_descriptor_header *) &fs_ep_out_desc, - NULL -}; - -/* - * usb 2.0 devices need to expose both high speed and full speed - * descriptors, unless they only run at full speed. - */ - -static struct usb_endpoint_descriptor hs_ep_in_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = cpu_to_le16(512) -}; - -static struct usb_endpoint_descriptor hs_ep_out_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = cpu_to_le16(512) -}; - -static struct usb_qualifier_descriptor dev_qualifier = { - .bLength = sizeof dev_qualifier, - .bDescriptorType = USB_DT_DEVICE_QUALIFIER, - .bcdUSB = cpu_to_le16(0x0200), - .bDeviceClass = USB_CLASS_PRINTER, - .bNumConfigurations = 1 -}; - -static struct usb_descriptor_header *hs_printer_function[] = { - (struct usb_descriptor_header *) &intf_desc, - (struct usb_descriptor_header *) &hs_ep_in_desc, - (struct usb_descriptor_header *) &hs_ep_out_desc, - NULL -}; - -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, -}; - -/* maxpacket and other transfer characteristics vary by speed. */ -#define ep_desc(g, hs, fs) (((g)->speed == USB_SPEED_HIGH)?(hs):(fs)) - -/*-------------------------------------------------------------------------*/ - -/* descriptors that are built on-demand */ - -static char product_desc [40] = DRIVER_DESC; -static char serial_num [40] = "1"; -static char pnp_string [1024] = - "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_request * -printer_req_alloc(struct usb_ep *ep, unsigned len, gfp_t gfp_flags) -{ - struct usb_request *req; - - req = usb_ep_alloc_request(ep, gfp_flags); - - if (req != NULL) { - req->length = len; - req->buf = kmalloc(len, gfp_flags); - if (req->buf == NULL) { - usb_ep_free_request(ep, req); - return NULL; - } - } - - return req; -} - -static void -printer_req_free(struct usb_ep *ep, struct usb_request *req) -{ - if (ep != NULL && req != NULL) { - kfree(req->buf); - usb_ep_free_request(ep, req); - } -} - -/*-------------------------------------------------------------------------*/ - -static void rx_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct printer_dev *dev = ep->driver_data; - int status = req->status; - unsigned long flags; - - spin_lock_irqsave(&dev->lock, flags); - - list_del_init(&req->list); /* Remode from Active List */ - - switch (status) { - - /* normal completion */ - case 0: - if (req->actual > 0) { - list_add_tail(&req->list, &dev->rx_buffers); - DBG(dev, "G_Printer : rx length %d\n", req->actual); - } else { - list_add(&req->list, &dev->rx_reqs); - } - break; - - /* software-driven interface shutdown */ - case -ECONNRESET: /* unlink */ - case -ESHUTDOWN: /* disconnect etc */ - VDBG(dev, "rx shutdown, code %d\n", status); - list_add(&req->list, &dev->rx_reqs); - break; - - /* for hardware automagic (such as pxa) */ - case -ECONNABORTED: /* endpoint reset */ - DBG(dev, "rx %s reset\n", ep->name); - list_add(&req->list, &dev->rx_reqs); - break; - - /* data overrun */ - case -EOVERFLOW: - /* FALLTHROUGH */ - - default: - DBG(dev, "rx status %d\n", status); - list_add(&req->list, &dev->rx_reqs); - break; - } - - wake_up_interruptible(&dev->rx_wait); - spin_unlock_irqrestore(&dev->lock, flags); -} - -static void tx_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct printer_dev *dev = ep->driver_data; - - switch (req->status) { - default: - VDBG(dev, "tx err %d\n", req->status); - /* FALLTHROUGH */ - case -ECONNRESET: /* unlink */ - case -ESHUTDOWN: /* disconnect etc */ - break; - case 0: - break; - } - - spin_lock(&dev->lock); - /* Take the request struct off the active list and put it on the - * free list. - */ - list_del_init(&req->list); - list_add(&req->list, &dev->tx_reqs); - wake_up_interruptible(&dev->tx_wait); - if (likely(list_empty(&dev->tx_reqs_active))) - wake_up_interruptible(&dev->tx_flush_wait); - - spin_unlock(&dev->lock); -} - -/*-------------------------------------------------------------------------*/ - -static int -printer_open(struct inode *inode, struct file *fd) -{ - struct printer_dev *dev; - unsigned long flags; - int ret = -EBUSY; - - mutex_lock(&printer_mutex); - dev = container_of(inode->i_cdev, struct printer_dev, printer_cdev); - - spin_lock_irqsave(&dev->lock, flags); - - if (!dev->printer_cdev_open) { - dev->printer_cdev_open = 1; - fd->private_data = dev; - ret = 0; - /* Change the printer status to show that it's on-line. */ - dev->printer_status |= PRINTER_SELECTED; - } - - spin_unlock_irqrestore(&dev->lock, flags); - - DBG(dev, "printer_open returned %x\n", ret); - mutex_unlock(&printer_mutex); - return ret; -} - -static int -printer_close(struct inode *inode, struct file *fd) -{ - struct printer_dev *dev = fd->private_data; - unsigned long flags; - - spin_lock_irqsave(&dev->lock, flags); - dev->printer_cdev_open = 0; - fd->private_data = NULL; - /* Change printer status to show that the printer is off-line. */ - dev->printer_status &= ~PRINTER_SELECTED; - spin_unlock_irqrestore(&dev->lock, flags); - - DBG(dev, "printer_close\n"); - - return 0; -} - -/* This function must be called with interrupts turned off. */ -static void -setup_rx_reqs(struct printer_dev *dev) -{ - struct usb_request *req; - - while (likely(!list_empty(&dev->rx_reqs))) { - int error; - - req = container_of(dev->rx_reqs.next, - struct usb_request, list); - list_del_init(&req->list); - - /* The USB Host sends us whatever amount of data it wants to - * so we always set the length field to the full USB_BUFSIZE. - * If the amount of data is more than the read() caller asked - * for it will be stored in the request buffer until it is - * asked for by read(). - */ - req->length = USB_BUFSIZE; - req->complete = rx_complete; - - /* here, we unlock, and only unlock, to avoid deadlock. */ - spin_unlock(&dev->lock); - error = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC); - spin_lock(&dev->lock); - if (error) { - DBG(dev, "rx submit --> %d\n", error); - list_add(&req->list, &dev->rx_reqs); - break; - } - /* if the req is empty, then add it into dev->rx_reqs_active. */ - else if (list_empty(&req->list)) { - list_add(&req->list, &dev->rx_reqs_active); - } - } -} - -static ssize_t -printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr) -{ - struct printer_dev *dev = fd->private_data; - unsigned long flags; - size_t size; - size_t bytes_copied; - struct usb_request *req; - /* This is a pointer to the current USB rx request. */ - struct usb_request *current_rx_req; - /* This is the number of bytes in the current rx buffer. */ - size_t current_rx_bytes; - /* This is a pointer to the current rx buffer. */ - u8 *current_rx_buf; - - if (len == 0) - return -EINVAL; - - DBG(dev, "printer_read trying to read %d bytes\n", (int)len); - - mutex_lock(&dev->lock_printer_io); - spin_lock_irqsave(&dev->lock, flags); - - /* We will use this flag later to check if a printer reset happened - * after we turn interrupts back on. - */ - dev->reset_printer = 0; - - setup_rx_reqs(dev); - - bytes_copied = 0; - current_rx_req = dev->current_rx_req; - current_rx_bytes = dev->current_rx_bytes; - current_rx_buf = dev->current_rx_buf; - dev->current_rx_req = NULL; - dev->current_rx_bytes = 0; - dev->current_rx_buf = NULL; - - /* Check if there is any data in the read buffers. Please note that - * current_rx_bytes is the number of bytes in the current rx buffer. - * If it is zero then check if there are any other rx_buffers that - * are on the completed list. We are only out of data if all rx - * buffers are empty. - */ - if ((current_rx_bytes == 0) && - (likely(list_empty(&dev->rx_buffers)))) { - /* Turn interrupts back on before sleeping. */ - spin_unlock_irqrestore(&dev->lock, flags); - - /* - * If no data is available check if this is a NON-Blocking - * call or not. - */ - if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) { - mutex_unlock(&dev->lock_printer_io); - return -EAGAIN; - } - - /* Sleep until data is available */ - wait_event_interruptible(dev->rx_wait, - (likely(!list_empty(&dev->rx_buffers)))); - spin_lock_irqsave(&dev->lock, flags); - } - - /* We have data to return then copy it to the caller's buffer.*/ - while ((current_rx_bytes || likely(!list_empty(&dev->rx_buffers))) - && len) { - if (current_rx_bytes == 0) { - req = container_of(dev->rx_buffers.next, - struct usb_request, list); - list_del_init(&req->list); - - if (req->actual && req->buf) { - current_rx_req = req; - current_rx_bytes = req->actual; - current_rx_buf = req->buf; - } else { - list_add(&req->list, &dev->rx_reqs); - continue; - } - } - - /* Don't leave irqs off while doing memory copies */ - spin_unlock_irqrestore(&dev->lock, flags); - - if (len > current_rx_bytes) - size = current_rx_bytes; - else - size = len; - - size -= copy_to_user(buf, current_rx_buf, size); - bytes_copied += size; - len -= size; - buf += size; - - spin_lock_irqsave(&dev->lock, flags); - - /* We've disconnected or reset so return. */ - if (dev->reset_printer) { - list_add(¤t_rx_req->list, &dev->rx_reqs); - spin_unlock_irqrestore(&dev->lock, flags); - mutex_unlock(&dev->lock_printer_io); - return -EAGAIN; - } - - /* If we not returning all the data left in this RX request - * buffer then adjust the amount of data left in the buffer. - * Othewise if we are done with this RX request buffer then - * requeue it to get any incoming data from the USB host. - */ - if (size < current_rx_bytes) { - current_rx_bytes -= size; - current_rx_buf += size; - } else { - list_add(¤t_rx_req->list, &dev->rx_reqs); - current_rx_bytes = 0; - current_rx_buf = NULL; - current_rx_req = NULL; - } - } - - dev->current_rx_req = current_rx_req; - dev->current_rx_bytes = current_rx_bytes; - dev->current_rx_buf = current_rx_buf; - - spin_unlock_irqrestore(&dev->lock, flags); - mutex_unlock(&dev->lock_printer_io); - - DBG(dev, "printer_read returned %d bytes\n", (int)bytes_copied); - - if (bytes_copied) - return bytes_copied; - else - return -EAGAIN; -} - -static ssize_t -printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr) -{ - struct printer_dev *dev = fd->private_data; - unsigned long flags; - size_t size; /* Amount of data in a TX request. */ - size_t bytes_copied = 0; - struct usb_request *req; - - DBG(dev, "printer_write trying to send %d bytes\n", (int)len); - - if (len == 0) - return -EINVAL; - - mutex_lock(&dev->lock_printer_io); - spin_lock_irqsave(&dev->lock, flags); - - /* Check if a printer reset happens while we have interrupts on */ - dev->reset_printer = 0; - - /* Check if there is any available write buffers */ - if (likely(list_empty(&dev->tx_reqs))) { - /* Turn interrupts back on before sleeping. */ - spin_unlock_irqrestore(&dev->lock, flags); - - /* - * If write buffers are available check if this is - * a NON-Blocking call or not. - */ - if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) { - mutex_unlock(&dev->lock_printer_io); - return -EAGAIN; - } - - /* Sleep until a write buffer is available */ - wait_event_interruptible(dev->tx_wait, - (likely(!list_empty(&dev->tx_reqs)))); - spin_lock_irqsave(&dev->lock, flags); - } - - while (likely(!list_empty(&dev->tx_reqs)) && len) { - - if (len > USB_BUFSIZE) - size = USB_BUFSIZE; - else - size = len; - - req = container_of(dev->tx_reqs.next, struct usb_request, - list); - list_del_init(&req->list); - - req->complete = tx_complete; - req->length = size; - - /* Check if we need to send a zero length packet. */ - if (len > size) - /* They will be more TX requests so no yet. */ - req->zero = 0; - else - /* If the data amount is not a multple of the - * maxpacket size then send a zero length packet. - */ - req->zero = ((len % dev->in_ep->maxpacket) == 0); - - /* Don't leave irqs off while doing memory copies */ - spin_unlock_irqrestore(&dev->lock, flags); - - if (copy_from_user(req->buf, buf, size)) { - list_add(&req->list, &dev->tx_reqs); - mutex_unlock(&dev->lock_printer_io); - return bytes_copied; - } - - bytes_copied += size; - len -= size; - buf += size; - - spin_lock_irqsave(&dev->lock, flags); - - /* We've disconnected or reset so free the req and buffer */ - if (dev->reset_printer) { - list_add(&req->list, &dev->tx_reqs); - spin_unlock_irqrestore(&dev->lock, flags); - mutex_unlock(&dev->lock_printer_io); - return -EAGAIN; - } - - if (usb_ep_queue(dev->in_ep, req, GFP_ATOMIC)) { - list_add(&req->list, &dev->tx_reqs); - spin_unlock_irqrestore(&dev->lock, flags); - mutex_unlock(&dev->lock_printer_io); - return -EAGAIN; - } - - list_add(&req->list, &dev->tx_reqs_active); - - } - - spin_unlock_irqrestore(&dev->lock, flags); - mutex_unlock(&dev->lock_printer_io); - - DBG(dev, "printer_write sent %d bytes\n", (int)bytes_copied); - - if (bytes_copied) { - return bytes_copied; - } else { - return -EAGAIN; - } -} - -static int -printer_fsync(struct file *fd, loff_t start, loff_t end, int datasync) -{ - struct printer_dev *dev = fd->private_data; - struct inode *inode = file_inode(fd); - unsigned long flags; - int tx_list_empty; - - mutex_lock(&inode->i_mutex); - spin_lock_irqsave(&dev->lock, flags); - tx_list_empty = (likely(list_empty(&dev->tx_reqs))); - spin_unlock_irqrestore(&dev->lock, flags); - - if (!tx_list_empty) { - /* Sleep until all data has been sent */ - wait_event_interruptible(dev->tx_flush_wait, - (likely(list_empty(&dev->tx_reqs_active)))); - } - mutex_unlock(&inode->i_mutex); - - return 0; -} - -static unsigned int -printer_poll(struct file *fd, poll_table *wait) -{ - struct printer_dev *dev = fd->private_data; - unsigned long flags; - int status = 0; - - mutex_lock(&dev->lock_printer_io); - spin_lock_irqsave(&dev->lock, flags); - setup_rx_reqs(dev); - spin_unlock_irqrestore(&dev->lock, flags); - mutex_unlock(&dev->lock_printer_io); - - poll_wait(fd, &dev->rx_wait, wait); - poll_wait(fd, &dev->tx_wait, wait); - - spin_lock_irqsave(&dev->lock, flags); - if (likely(!list_empty(&dev->tx_reqs))) - status |= POLLOUT | POLLWRNORM; - - if (likely(dev->current_rx_bytes) || - likely(!list_empty(&dev->rx_buffers))) - status |= POLLIN | POLLRDNORM; - - spin_unlock_irqrestore(&dev->lock, flags); - - return status; -} - -static long -printer_ioctl(struct file *fd, unsigned int code, unsigned long arg) -{ - struct printer_dev *dev = fd->private_data; - unsigned long flags; - int status = 0; - - DBG(dev, "printer_ioctl: cmd=0x%4.4x, arg=%lu\n", code, arg); - - /* handle ioctls */ - - spin_lock_irqsave(&dev->lock, flags); - - switch (code) { - case GADGET_GET_PRINTER_STATUS: - status = (int)dev->printer_status; - break; - case GADGET_SET_PRINTER_STATUS: - dev->printer_status = (u8)arg; - break; - default: - /* could not handle ioctl */ - DBG(dev, "printer_ioctl: ERROR cmd=0x%4.4xis not supported\n", - code); - status = -ENOTTY; - } - - spin_unlock_irqrestore(&dev->lock, flags); - - return status; -} - -/* used after endpoint configuration */ -static const struct file_operations printer_io_operations = { - .owner = THIS_MODULE, - .open = printer_open, - .read = printer_read, - .write = printer_write, - .fsync = printer_fsync, - .poll = printer_poll, - .unlocked_ioctl = printer_ioctl, - .release = printer_close, - .llseek = noop_llseek, -}; - -/*-------------------------------------------------------------------------*/ - -static int -set_printer_interface(struct printer_dev *dev) -{ - int result = 0; - - dev->in_ep->desc = ep_desc(dev->gadget, &hs_ep_in_desc, &fs_ep_in_desc); - dev->in_ep->driver_data = dev; - - dev->out_ep->desc = ep_desc(dev->gadget, &hs_ep_out_desc, - &fs_ep_out_desc); - dev->out_ep->driver_data = dev; - - result = usb_ep_enable(dev->in_ep); - if (result != 0) { - DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result); - goto done; - } - - result = usb_ep_enable(dev->out_ep); - if (result != 0) { - DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result); - goto done; - } - -done: - /* on error, disable any endpoints */ - if (result != 0) { - (void) usb_ep_disable(dev->in_ep); - (void) usb_ep_disable(dev->out_ep); - dev->in_ep->desc = NULL; - dev->out_ep->desc = NULL; - } - - /* caller is responsible for cleanup on error */ - return result; -} - -static void printer_reset_interface(struct printer_dev *dev) -{ - if (dev->interface < 0) - return; - - DBG(dev, "%s\n", __func__); - - if (dev->in_ep->desc) - usb_ep_disable(dev->in_ep); - - if (dev->out_ep->desc) - usb_ep_disable(dev->out_ep); - - dev->in_ep->desc = NULL; - dev->out_ep->desc = NULL; - dev->interface = -1; -} - -/* Change our operational Interface. */ -static int set_interface(struct printer_dev *dev, unsigned number) -{ - int result = 0; - - /* Free the current interface */ - printer_reset_interface(dev); - - result = set_printer_interface(dev); - if (result) - printer_reset_interface(dev); - else - dev->interface = number; - - if (!result) - INFO(dev, "Using interface %x\n", number); - - return result; -} - -static void printer_soft_reset(struct printer_dev *dev) -{ - struct usb_request *req; - - INFO(dev, "Received Printer Reset Request\n"); - - if (usb_ep_disable(dev->in_ep)) - DBG(dev, "Failed to disable USB in_ep\n"); - if (usb_ep_disable(dev->out_ep)) - DBG(dev, "Failed to disable USB out_ep\n"); - - if (dev->current_rx_req != NULL) { - list_add(&dev->current_rx_req->list, &dev->rx_reqs); - dev->current_rx_req = NULL; - } - dev->current_rx_bytes = 0; - dev->current_rx_buf = NULL; - dev->reset_printer = 1; - - while (likely(!(list_empty(&dev->rx_buffers)))) { - req = container_of(dev->rx_buffers.next, struct usb_request, - list); - list_del_init(&req->list); - list_add(&req->list, &dev->rx_reqs); - } - - while (likely(!(list_empty(&dev->rx_reqs_active)))) { - req = container_of(dev->rx_buffers.next, struct usb_request, - list); - list_del_init(&req->list); - list_add(&req->list, &dev->rx_reqs); - } - - while (likely(!(list_empty(&dev->tx_reqs_active)))) { - req = container_of(dev->tx_reqs_active.next, - struct usb_request, list); - list_del_init(&req->list); - list_add(&req->list, &dev->tx_reqs); - } - - if (usb_ep_enable(dev->in_ep)) - DBG(dev, "Failed to enable USB in_ep\n"); - if (usb_ep_enable(dev->out_ep)) - DBG(dev, "Failed to enable USB out_ep\n"); - - wake_up_interruptible(&dev->rx_wait); - wake_up_interruptible(&dev->tx_wait); - wake_up_interruptible(&dev->tx_flush_wait); -} - -/*-------------------------------------------------------------------------*/ - -/* - * The setup() callback implements all the ep0 functionality that's not - * handled lower down. - */ -static int printer_func_setup(struct usb_function *f, - const struct usb_ctrlrequest *ctrl) -{ - struct printer_dev *dev = container_of(f, struct printer_dev, function); - struct usb_composite_dev *cdev = f->config->cdev; - struct usb_request *req = cdev->req; - int value = -EOPNOTSUPP; - u16 wIndex = le16_to_cpu(ctrl->wIndex); - u16 wValue = le16_to_cpu(ctrl->wValue); - u16 wLength = le16_to_cpu(ctrl->wLength); - - DBG(dev, "ctrl req%02x.%02x v%04x i%04x l%d\n", - ctrl->bRequestType, ctrl->bRequest, wValue, wIndex, wLength); - - switch (ctrl->bRequestType&USB_TYPE_MASK) { - case USB_TYPE_CLASS: - switch (ctrl->bRequest) { - case 0: /* Get the IEEE-1284 PNP String */ - /* Only one printer interface is supported. */ - if ((wIndex>>8) != dev->interface) - break; - - value = (pnp_string[0]<<8)|pnp_string[1]; - memcpy(req->buf, pnp_string, value); - DBG(dev, "1284 PNP String: %x %s\n", value, - &pnp_string[2]); - break; - - case 1: /* Get Port Status */ - /* Only one printer interface is supported. */ - if (wIndex != dev->interface) - break; - - *(u8 *)req->buf = dev->printer_status; - value = min(wLength, (u16) 1); - break; - - case 2: /* Soft Reset */ - /* Only one printer interface is supported. */ - if (wIndex != dev->interface) - break; - - printer_soft_reset(dev); - - value = 0; - break; - - default: - goto unknown; - } - break; - - default: -unknown: - VDBG(dev, - "unknown ctrl req%02x.%02x v%04x i%04x l%d\n", - ctrl->bRequestType, ctrl->bRequest, - wValue, wIndex, wLength); - break; - } - /* host either stalls (value < 0) or reports success */ - return value; -} - -static int __init printer_func_bind(struct usb_configuration *c, - struct usb_function *f) -{ - struct printer_dev *dev = container_of(f, struct printer_dev, function); - struct usb_composite_dev *cdev = c->cdev; - struct usb_ep *in_ep; - struct usb_ep *out_ep = NULL; - int id; - int ret; - - id = usb_interface_id(c, f); - if (id < 0) - return id; - intf_desc.bInterfaceNumber = id; - - /* all we really need is bulk IN/OUT */ - in_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_in_desc); - if (!in_ep) { -autoconf_fail: - dev_err(&cdev->gadget->dev, "can't autoconfigure on %s\n", - cdev->gadget->name); - return -ENODEV; - } - in_ep->driver_data = in_ep; /* claim */ - - out_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_out_desc); - if (!out_ep) - goto autoconf_fail; - out_ep->driver_data = out_ep; /* claim */ - - /* assumes that all endpoints are dual-speed */ - hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress; - hs_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress; - - ret = usb_assign_descriptors(f, fs_printer_function, - hs_printer_function, NULL); - if (ret) - return ret; - - dev->in_ep = in_ep; - dev->out_ep = out_ep; - return 0; -} - -static void printer_func_unbind(struct usb_configuration *c, - struct usb_function *f) -{ - usb_free_all_descriptors(f); -} - -static int printer_func_set_alt(struct usb_function *f, - unsigned intf, unsigned alt) -{ - struct printer_dev *dev = container_of(f, struct printer_dev, function); - int ret = -ENOTSUPP; - - if (!alt) - ret = set_interface(dev, intf); - - return ret; -} - -static void printer_func_disable(struct usb_function *f) -{ - struct printer_dev *dev = container_of(f, struct printer_dev, function); - unsigned long flags; - - DBG(dev, "%s\n", __func__); - - spin_lock_irqsave(&dev->lock, flags); - printer_reset_interface(dev); - spin_unlock_irqrestore(&dev->lock, flags); -} - -static void printer_cfg_unbind(struct usb_configuration *c) -{ - struct printer_dev *dev; - struct usb_request *req; - - dev = &usb_printer_gadget; - - DBG(dev, "%s\n", __func__); - - /* Remove sysfs files */ - device_destroy(usb_gadget_class, g_printer_devno); - - /* Remove Character Device */ - cdev_del(&dev->printer_cdev); - - /* we must already have been disconnected ... no i/o may be active */ - WARN_ON(!list_empty(&dev->tx_reqs_active)); - WARN_ON(!list_empty(&dev->rx_reqs_active)); - - /* Free all memory for this driver. */ - while (!list_empty(&dev->tx_reqs)) { - req = container_of(dev->tx_reqs.next, struct usb_request, - list); - list_del(&req->list); - printer_req_free(dev->in_ep, req); - } - - if (dev->current_rx_req != NULL) - printer_req_free(dev->out_ep, dev->current_rx_req); - - while (!list_empty(&dev->rx_reqs)) { - req = container_of(dev->rx_reqs.next, - struct usb_request, list); - list_del(&req->list); - printer_req_free(dev->out_ep, req); - } - - while (!list_empty(&dev->rx_buffers)) { - req = container_of(dev->rx_buffers.next, - struct usb_request, list); - list_del(&req->list); - printer_req_free(dev->out_ep, req); - } -} - -static struct usb_configuration printer_cfg_driver = { - .label = "printer", - .unbind = printer_cfg_unbind, - .bConfigurationValue = 1, - .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, -}; - -static int __init printer_bind_config(struct usb_configuration *c) -{ - struct usb_gadget *gadget = c->cdev->gadget; - struct printer_dev *dev; - int status = -ENOMEM; - size_t len; - u32 i; - struct usb_request *req; - - usb_ep_autoconfig_reset(gadget); - - dev = &usb_printer_gadget; - - dev->function.name = shortname; - dev->function.bind = printer_func_bind; - dev->function.setup = printer_func_setup; - dev->function.unbind = printer_func_unbind; - dev->function.set_alt = printer_func_set_alt; - dev->function.disable = printer_func_disable; - - status = usb_add_function(c, &dev->function); - if (status) - return status; - - /* Setup the sysfs files for the printer gadget. */ - dev->pdev = device_create(usb_gadget_class, NULL, g_printer_devno, - NULL, "g_printer"); - if (IS_ERR(dev->pdev)) { - ERROR(dev, "Failed to create device: g_printer\n"); - status = PTR_ERR(dev->pdev); - goto fail; - } - - /* - * Register a character device as an interface to a user mode - * program that handles the printer specific functionality. - */ - cdev_init(&dev->printer_cdev, &printer_io_operations); - dev->printer_cdev.owner = THIS_MODULE; - status = cdev_add(&dev->printer_cdev, g_printer_devno, 1); - if (status) { - ERROR(dev, "Failed to open char device\n"); - goto fail; - } - - if (iPNPstring) - strlcpy(&pnp_string[2], iPNPstring, (sizeof pnp_string)-2); - - len = strlen(pnp_string); - pnp_string[0] = (len >> 8) & 0xFF; - pnp_string[1] = len & 0xFF; - - 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; - } - - spin_lock_init(&dev->lock); - mutex_init(&dev->lock_printer_io); - INIT_LIST_HEAD(&dev->tx_reqs); - INIT_LIST_HEAD(&dev->tx_reqs_active); - INIT_LIST_HEAD(&dev->rx_reqs); - INIT_LIST_HEAD(&dev->rx_reqs_active); - INIT_LIST_HEAD(&dev->rx_buffers); - init_waitqueue_head(&dev->rx_wait); - init_waitqueue_head(&dev->tx_wait); - init_waitqueue_head(&dev->tx_flush_wait); - - dev->interface = -1; - dev->printer_cdev_open = 0; - dev->printer_status = PRINTER_NOT_ERROR; - dev->current_rx_req = NULL; - dev->current_rx_bytes = 0; - dev->current_rx_buf = NULL; - - for (i = 0; i < QLEN; i++) { - req = printer_req_alloc(dev->in_ep, USB_BUFSIZE, GFP_KERNEL); - if (!req) { - while (!list_empty(&dev->tx_reqs)) { - req = container_of(dev->tx_reqs.next, - struct usb_request, list); - list_del(&req->list); - printer_req_free(dev->in_ep, req); - } - return -ENOMEM; - } - list_add(&req->list, &dev->tx_reqs); - } - - for (i = 0; i < QLEN; i++) { - req = printer_req_alloc(dev->out_ep, USB_BUFSIZE, GFP_KERNEL); - if (!req) { - while (!list_empty(&dev->rx_reqs)) { - req = container_of(dev->rx_reqs.next, - struct usb_request, list); - list_del(&req->list); - printer_req_free(dev->out_ep, req); - } - return -ENOMEM; - } - list_add(&req->list, &dev->rx_reqs); - } - - /* finish hookup to lower layer ... */ - dev->gadget = gadget; - - INFO(dev, "%s, version: " DRIVER_VERSION "\n", driver_desc); - return 0; - -fail: - printer_cfg_unbind(c); - return status; -} - -static int printer_unbind(struct usb_composite_dev *cdev) -{ - return 0; -} - -static int __init printer_bind(struct usb_composite_dev *cdev) -{ - int ret; - - ret = usb_string_ids_tab(cdev, strings); - if (ret < 0) - 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_bind_config); - if (ret) - return ret; - usb_composite_overwrite_options(cdev, &coverwrite); - return ret; -} - -static __refdata struct usb_composite_driver printer_driver = { - .name = shortname, - .dev = &device_desc, - .strings = dev_strings, - .max_speed = USB_SPEED_HIGH, - .bind = printer_bind, - .unbind = printer_unbind, -}; - -static int __init -init(void) -{ - int status; - - usb_gadget_class = class_create(THIS_MODULE, "usb_printer_gadget"); - if (IS_ERR(usb_gadget_class)) { - status = PTR_ERR(usb_gadget_class); - pr_err("unable to create usb_gadget class %d\n", status); - return status; - } - - status = alloc_chrdev_region(&g_printer_devno, 0, 1, - "USB printer gadget"); - if (status) { - pr_err("alloc_chrdev_region %d\n", status); - class_destroy(usb_gadget_class); - return status; - } - - status = usb_composite_probe(&printer_driver); - if (status) { - class_destroy(usb_gadget_class); - unregister_chrdev_region(g_printer_devno, 1); - pr_err("usb_gadget_probe_driver %x\n", status); - } - - return status; -} -module_init(init); - -static void __exit -cleanup(void) -{ - mutex_lock(&usb_printer_gadget.lock_printer_io); - usb_composite_unregister(&printer_driver); - unregister_chrdev_region(g_printer_devno, 1); - class_destroy(usb_gadget_class); - mutex_unlock(&usb_printer_gadget.lock_printer_io); -} -module_exit(cleanup); - -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_AUTHOR("Craig Nadler"); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c deleted file mode 100644 index 1f5f978..0000000 --- a/drivers/usb/gadget/serial.c +++ /dev/null @@ -1,276 +0,0 @@ -/* - * 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 -#include -#include -#include -#include - -#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 __init 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 __refdata 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/tcm_usb_gadget.c b/drivers/usb/gadget/tcm_usb_gadget.c deleted file mode 100644 index 6cdb7a5..0000000 --- a/drivers/usb/gadget/tcm_usb_gadget.c +++ /dev/null @@ -1,2473 +0,0 @@ -/* 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 - * License: GPLv2 as published by FSF. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tcm_usb_gadget.h" - -USB_GADGET_COMPOSITE_OPTIONS(); - -static struct target_fabric_configfs *usbg_fabric_configfs; - -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: - 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 = MSG_HEAD_TAG; - break; - case UAS_ORDERED_TAG: - cmd->prio_attr = MSG_ORDERED_TAG; - break; - case UAS_ACA: - cmd->prio_attr = MSG_ACA_TAG; - break; - default: - pr_debug_once("Unsupported prio_attr: %02x.\n", - cmd_iu->prio_attr); - case UAS_SIMPLE_TAG: - cmd->prio_attr = MSG_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 = MSG_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_fabric_configfs->tf_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 with the - * call to __transport_register_session() - */ - __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 struct target_core_fabric_ops usbg_ops = { - .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, -}; - -static int usbg_register_configfs(void) -{ - struct target_fabric_configfs *fabric; - int ret; - - fabric = target_fabric_configfs_init(THIS_MODULE, "usb_gadget"); - if (IS_ERR(fabric)) { - printk(KERN_ERR "target_fabric_configfs_init() failed\n"); - return PTR_ERR(fabric); - } - - fabric->tf_ops = usbg_ops; - fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = usbg_wwn_attrs; - fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = usbg_base_attrs; - fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = NULL; - fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = NULL; - fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = NULL; - fabric->tf_cit_tmpl.tfc_tpg_nacl_base_cit.ct_attrs = NULL; - fabric->tf_cit_tmpl.tfc_tpg_nacl_attrib_cit.ct_attrs = NULL; - fabric->tf_cit_tmpl.tfc_tpg_nacl_auth_cit.ct_attrs = NULL; - fabric->tf_cit_tmpl.tfc_tpg_nacl_param_cit.ct_attrs = NULL; - ret = target_fabric_configfs_register(fabric); - if (ret < 0) { - printk(KERN_ERR "target_fabric_configfs_register() failed" - " for usb-gadget\n"); - return ret; - } - usbg_fabric_configfs = fabric; - return 0; -}; - -static void usbg_deregister_configfs(void) -{ - if (!(usbg_fabric_configfs)) - return; - - target_fabric_configfs_deregister(usbg_fabric_configfs); - usbg_fabric_configfs = NULL; -}; - -/* 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 __refdata 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) -{ - int ret; - - ret = usbg_register_configfs(); - return ret; -} -module_init(usb_target_gadget_init); - -static void __exit usb_target_gadget_exit(void) -{ - usbg_deregister_configfs(); -} -module_exit(usb_target_gadget_exit); - -MODULE_AUTHOR("Sebastian Andrzej Siewior "); -MODULE_DESCRIPTION("usb-gadget fabric"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/gadget/tcm_usb_gadget.h b/drivers/usb/gadget/tcm_usb_gadget.h deleted file mode 100644 index 8289219..0000000 --- a/drivers/usb/gadget/tcm_usb_gadget.h +++ /dev/null @@ -1,145 +0,0 @@ -#ifndef __TARGET_USB_GADGET_H__ -#define __TARGET_USB_GADGET_H__ - -#include -/* #include */ -#include -#include -#include -#include -#include -#include - -#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/webcam.c b/drivers/usb/gadget/webcam.c deleted file mode 100644 index a11d8e4..0000000 --- a/drivers/usb/gadget/webcam.c +++ /dev/null @@ -1,399 +0,0 @@ -/* - * 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 -#include -#include - -#include "f_uvc.h" - -/* - * Kbuild is not very cooperative with respect to linking separately - * compiled library objects into one module. So for now we won't use - * separate compilation ... ensuring init/exit sections work to shrink - * the runtime footprint, and giving us at least some parts of what - * a "gcc --combine ... part1.c part2.c part3.c ... " build would. - */ -#include "uvc_queue.c" -#include "uvc_video.c" -#include "uvc_v4l2.c" -#include "f_uvc.c" - -USB_GADGET_COMPOSITE_OPTIONS(); -/* -------------------------------------------------------------------------- - * 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_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 __init -webcam_config_bind(struct usb_configuration *c) -{ - return uvc_bind_config(c, uvc_fs_control_cls, uvc_ss_control_cls, - uvc_fs_streaming_cls, uvc_hs_streaming_cls, - uvc_ss_streaming_cls); -} - -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 /* __init_or_exit */ -webcam_unbind(struct usb_composite_dev *cdev) -{ - return 0; -} - -static int __init -webcam_bind(struct usb_composite_dev *cdev) -{ - int ret; - - /* 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: - webcam_unbind(cdev); - return ret; -} - -/* -------------------------------------------------------------------------- - * Driver - */ - -static __refdata 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/zero.c b/drivers/usb/gadget/zero.c deleted file mode 100644 index c3d4968..0000000 --- a/drivers/usb/gadget/zero.c +++ /dev/null @@ -1,417 +0,0 @@ -/* - * 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 infrastucture 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 -#include -#include -#include -#include -#include - -#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 __init 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 __refdata 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"); -- cgit v0.10.2