Folks: Below is the source for the first version of my File-backed Storage Gadget driver. The driver acts as a USB Mass Storage device using either a regular file or a block device file to provide backing storage. Operation of the driver and module parameters are explained in comments near the start of the file.
The source is in the form of a patch, meant to be applied against vanilla 2.6.0-test4, _not_ against David Brownell's usb-gadget tree. However the bulk of the patch is just the source for file_storage.c. The alterations to Kconfig and Makefile are small and can easily be inserted by hand if the patch fails to apply cleanly. The driver includes support for the NetChip-2280 and PXA250 controllers, as well as both full-speed and high-speed operation. It doesn't work with the SA1100 chip because of that controller's limitations. The driver needs a lot of testing. I can't run it in its intended environment -- as part of a USB peripheral -- because I don't have the hardware. It seems to work okay running under the dummy_hcd controller, but I still haven't done very much testing. Test reports and bug fixes are welcome. In a followup email, I will send my revised version of the dummy_hcd driver. The one included in the usb-gadget distribution won't work with the FSG driver because it's missing several crucial features (such as the ability for the host to clear endpoint halts!). Use of dummy_hcd has been absolutely crucial to the development of my driver, since I had no other way to test it. Thanks for providing such a useful tool, David! Still to come: I intend to write a sister gadget driver, a Scsi-backed Storage Gadget. Instead of appearing as a direct-access storage device using a regular file for backing storage, SSG will act as a transparent SCSI bridge. It will pass requests received from the USB host directly to a local SCSI device driver and pass the replies back to the host. Thus it will be able to appear as any kind of SCSI device, not just a disk drive. It should end up being a lot smaller and simpler than FSG. (In fact file_storage.c is rather too large; it deserves to be split into two or more parts. But I'm not motivated enough to do that.) Alan Stern ===== Kconfig 1.1 vs edited ===== --- 1.1/drivers/usb/gadget/Kconfig Thu May 29 03:36:51 2003 +++ edited/drivers/usb/gadget/Kconfig Thu Aug 28 16:20:40 2003 @@ -147,6 +147,30 @@ depends on USB_ETH && USB_SA1100 default y + +config USB_FILE_STORAGE + tristate "File-backed Storage Gadget (DEVELOPMENT)" + depends on USB_GADGET && (USB_DUMMY_HCD || USB_NET2280 || USB_PXA250) + # we don't support the SA1100 because of its limitations + help + The File-backed 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. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_file_storage". + +config USB_FILE_STORAGE_NET2280 + bool + depends on USB_FILE_STORAGE && USB_NET2280 + default y + +config USB_FILE_STORAGE_PXA250 + bool + depends on USB_FILE_STORAGE && USB_PXA250 + default y + endchoice # endmenuconfig --- 1.1/drivers/usb/gadget/Makefile Thu May 29 03:36:51 2003 +++ edited/drivers/usb/gadget/Makefile Thu Aug 28 16:21:11 2003 @@ -8,7 +8,9 @@ # g_zero-objs := zero.o usbstring.o g_ether-objs := ether.o usbstring.o +g_file_storage-objs := file_storage.o usbstring.o obj-$(CONFIG_USB_ZERO) += g_zero.o obj-$(CONFIG_USB_ETH) += g_ether.o +obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o ===== file_storage.c 1.1 vs edited ===== --- 1.1/drivers/usb/gadget/file_storage.c Thu May 29 03:36:51 2003 +++ edited/drivers/usb/gadget/file_storage.c Thu Aug 28 15:46:21 2003 @@ -0,0 +1,3087 @@ +/* + * file_storage.c -- File-backed USB Storage Gadget, for USB development + * + * Copyright (C) 2003 Alan Stern + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +/* + * The File-backed Storage Gadget acts as a USB Mass Storage device, + * appearing to the host as a disk 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. + * + * Backing storage is provided by a regular file or a block device, specified + * by the mandatory "file" module parameter. Access can be limited to + * read-only by setting the optional "ro" module parameter. + * + * The gadget supports the Control-Bulk (CB), Control-Bulk-Interrupt (CBI), + * and Bulk-Only (also known as Bulk-Bulk-Bulk or BBB) transports, selected + * by the optional "transport" module parameter. It always uses the + * Transparent SCSI protocol encapsulation. Requirements are modest; only + * a bulk-in and a bulk-out endpoint are needed (an interrupt-out endpoint + * is also needed for CBI). The memory requirement amounts to two 16K + * buffers, size configurable by a parameter. Support is included for both + * full-speed and high-speed operation. + * + * Module options: + * + * file=filename Mandatory, name of the file or block device + * used for backing storage + * ro Default false, boolean for read-only access + * transport=XXX Default BBB, transport name (CB, CBI, or BBB) + * buflen=N Default N=16384, buffer size used (will be + * rounded down to a multiple of + * PAGE_CACHE_SIZE) + * + * This gadget driver is heavily based on "Gadget Zero" by David Brownell. + */ + + +/* + * Principles of Operation + * + * Like all gadget drivers, this driver is event-driven. Events are + * primarily callbacks from the controller driver: request completion, + * and disconnect and unbind notification. However, since the driver + * needs to carry out file I/O, it must have an associated kernel thread + * to provide a process context. This thread can also generate events: + * file I/O completion. + * + * To keep things straight and to provide maximum throughput, the driver + * uses a circular pipeline of buffer heads (struct fsg_buffhd). In + * principle the pipeline can be arbitrarily long; in practice the + * benefits don't justify having more than 2 stages (i.e., double + * buffering). But it helps to think of the pipeline as being a long + * one. Each buffer head contains a bulk-in and a bulk-out request + * pointer (since the buffer can be used for both output and input) as + * well as a pointer to the buffer and various state variables. + * + * Use of the pipeline follows a simple protocol. There is a global + * variable (next_buffhd_to_fill) that points to the next buffer head to + * use. At any time that buffer head may still be in use from an earlier + * request, so each buffer head has a state variable indicating whether + * it is EMPTY, FULL, or BUSY. When the next buffer head is EMPTY, the + * dispatcher() function calls the function stored in the global variable + * next_fill_routine. This initiates whatever action is needed to fill + * the buffer. If it is an asynchronous USB transfer, the completion + * routine will mark the buffer head FULL and call its usb_done_routine + * pointer. If it is a synchronous software computation, the driver can + * mark the buffer and proceed directly to the next stage. + * + * The second stage of buffer handling involves draining a FULL buffer + * head. As before, this may involve a USB transfer whose completion + * routine will call the buffer head's usb_done_routine or it may involve + * a software computation. Either way, at the end the buffer head is + * marked EMPTY so that the dispatcher() function will be able to reuse + * it. + * + * A typical command execution cycle involves the following sequence of + * stages (when using the Bulk-only transport): + * + * Dispatcher(): + * get_next_command() Fill a buffer via a USB transfer. + * received_cbw() Drain the buffer by storing the command + * parameters, and set next_fill_routine. + * + * Dispatcher(): + * decode_scsi_cmnd() Figure out what the command should do. + * For commands that don't require file I/O, + * fill the next buffer with reply data. + * send_reply() Drain the reply buffer by transferring it + * to the host. + * reply_done() If the reply was shorter than expected, + * halt the bulk-in endpoint (required by the + * Bulk-only protocol). Set next_fill_routine. + * + * Dispatcher(): + * store_status() Fill the next buffer with the status data. + * Drain the buffer via a USB transfer. + * status_done() Set next_fill_routine to get_next_command. + * + * Commands like READ and WRITE that involve file I/O are more + * complicated. They will require multiple data transfers if the data + * size is larger than the buffer size. And they will involve waking up + * the file I/O thread to perform the actual reads and writes. Most of + * the time the thread just sits on a wait queue, sleeping until the + * thread_wakeup_code variable is set. Each time it wakes up the thread + * will try to process as many buffer heads as are available; thus it + * doesn't matter that the wait queue mechanism doesn't keep track of the + * number of pending wakeup requests. + * + * For READS, next_fill_routine is set to wakeup_io_thread. The thread + * fills a buffer with file data and then starts a USB transfer to drain + * it. Only for the last transfer does it set usb_done_routine for the + * buffer to read_done. Like reply_done, this function finishes the + * transfer and prepares the status. + * + * For WRITES, next_fill_routine is set to get_write_data_from_host. It + * fills a buffer via a USB transfer, sets the buffer's usb_done_routine + * to wakeup_io_thread, and sets next_fill_routine to point to itself. + * In this way the pipeline is kept as full as possible. When the thread + * wakes up it repeatedly takes the next available buffer and writes it + * to the file. At the end it calls write_done, which finishes the + * transfer and prepares the status. + * + * Exceptions to this normal orderly flow of control can occur at almost + * any time. Configuration or interface setting changes, disconnects, + * and class-specific device resets all require the current operation to + * be abandoned. The driver checks for pending exceptions whenever an + * event handler calls acquire_lock(). If an exception is taking place, + * acquire_lock() returns 0 so the caller knows that it doesn't own the + * device lock and should not continue what it was doing. + * + * The raise_exception() function does just what it says. It marks the + * exception as pending and cancels all ongoing USB transfers. (File I/O + * transfers cannot be cancelled; the driver has to wait until they + * complete.) It then calls handle_exception(), which checks whether all + * the transfers have completed yet. If some haven't then it does + * nothing; later on the completion routines for the transfers will call + * acquire_lock() which in turn calls handle_exception(). If everything + * is idle, handle_exception() resets the various buffer head states and + * pointers plus a few other things (SCSI command state for example) and + * then calls the routine stored in the handler global variable. The + * handler routine carries out the configuration or interface change, or + * completes the reset processing, or does whatever else is needed. + * + * The file I/O kernel thread does not block INT, TERM, or KILL signals, + * and it sleeps in an interruptible wait state. When it receives a + * signal it immediately unregisters the gadget and closes the backing + * file. Although the module remains resident in kernel memory, it is + * completely inactive. This is necessary for orderly system shutdowns; + * as long as the thread is running with the backing file open it is + * impossible to unmount the device where the file resides. During + * system shutdown processing the thread will be signalled and will exit + * cleanly, thus preventing filesystem errors. + */ + + +#undef DEBUG +#undef VERBOSE + +#include <linux/config.h> + +#include <asm/system.h> +#include <asm/uaccess.h> + +#include <linux/bitops.h> +#include <linux/blkdev.h> +#include <linux/compiler.h> +#include <linux/completion.h> +#include <linux/device.h> +#include <linux/file.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/sched.h> +#include <linux/signal.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/uts.h> +#include <linux/version.h> +#include <linux/wait.h> + +#include <linux/usb_ch9.h> +#include <linux/usb_gadget.h> + + +/*-------------------------------------------------------------------------*/ + +#define DRIVER_DESC "File-backed Storage Gadget" +#define DRIVER_NAME "file-storage" +#define DRIVER_VERSION "28 August 2003" + +#define DRIVER_VENDOR_ID 0xffff // FIXME! Use real IDs +#define DRIVER_PRODUCT_ID 0x0000 + +static const char longname[] = DRIVER_DESC; +static const char shortname[] = DRIVER_NAME; + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Alan Stern"); +MODULE_LICENSE("Dual BSD/GPL"); + + +/*-------------------------------------------------------------------------*/ + +/* + * Hardware-specific configuration, controlled by which device + * controller driver was configured. + * + * CHIP ... hardware identifier + * DRIVER_VERSION_NUM ... alerts the host side driver to differences + * EP0_MAXPACKET ... controls packetization of control requests + * EP_*_NAME ... which endpoints do we use for which purpose? + * EP_*_NUM ... numbers for them (often limited by hardware) + * HIGHSPEED ... define if ep0 and descriptors need high speed support + * MAX_USB_POWER ... define if we use other than 100 mA bus current + * SELFPOWER ... unless we can run on bus power, USB_CONFIG_ATT_SELFPOWER + */ + + +/* + * NetChip 2280, PCI based. + * + * This has half a dozen configurable endpoints, four with dedicated + * DMA channels to manage their FIFOs. It supports high speed. + * Those endpoints can be arranged in any desired configuration. + */ +#ifdef CONFIG_USB_FILE_STORAGE_NET2280 +#define CHIP "net2280" +#define DRIVER_VERSION_NUM 0x0101 +#define EP0_MAXPACKET 64 +static const char EP_BULK_IN_NAME[] = "ep-a"; +#define EP_BULK_IN_NUM 1 +static const char EP_BULK_OUT_NAME[] = "ep-b"; +#define EP_BULK_OUT_NUM 2 +static const char EP_INTR_IN_NAME[] = "ep-c"; +#define EP_INTR_IN_NUM 5 +#define HIGHSPEED +#define SELFPOWER USB_CONFIG_ATT_SELFPOWER +#define CAN_STALL_CONTROL_OUT +#endif + + +/* + * Dummy_hcd, software-based loopback controller. + * + * This imitates the abilities of the NetChip 2280, so we will use + * the same configuration. + */ +#ifdef CONFIG_USB_FILE_STORAGE_DUMMY +#define CHIP "dummy" +#define DRIVER_VERSION_NUM 0x0102 +#define EP0_MAXPACKET 64 +static const char EP_BULK_IN_NAME[] = "ep-a"; +#define EP_BULK_IN_NUM 1 +static const char EP_BULK_OUT_NAME[] = "ep-b"; +#define EP_BULK_OUT_NUM 2 +static const char EP_INTR_IN_NAME[] = "ep-c"; +#define EP_INTR_IN_NUM 5 +#define HIGHSPEED +#define SELFPOWER USB_CONFIG_ATT_SELFPOWER +#define CAN_STALL_CONTROL_OUT +#endif + +/* + * PXA-2xx UDC: widely used in second gen Linux-capable PDAs. + * + * This has fifteen fixed-function full speed endpoints, and it + * can support all USB transfer types. + * + * These supports three or four configurations, with fixed numbers. + * The hardware interprets SET_INTERFACE, net effect is that you + * can't use altsettings or reset the interfaces independently. + * So stick to a single interface. + */ +#ifdef CONFIG_USB_FILE_STORAGE_PXA2XX +#define CHIP "pxa2xx" +#define DRIVER_VERSION_NUM 0x0103 +#define EP0_MAXPACKET 16 +static const char EP_BULK_IN_NAME[] = "ep1in-bulk"; +#define EP_BULK_IN_NUM 1 +static const char EP_BULK_OUT_NAME[] = "ep2out-bulk"; +#define EP_BULK_OUT_NUM 2 +static const char EP_INTR_IN_NAME[] = "ep5in-int"; +#define EP_INTR_IN_NUM 5 +/* doesn't support bus-powered operation */ +#define SELFPOWER USB_CONFIG_ATT_SELFPOWER +#undef CAN_STALL_CONTROL_OUT +#endif + +/*-------------------------------------------------------------------------*/ + +#ifndef EP0_MAXPACKET +# error Configure some USB peripheral controller driver! +#endif + +#ifndef SELFPOWER +/* Default: say we rely on bus power */ +#define SELFPOWER 0 +/* else: + * - SELFPOWER value must be USB_CONFIG_ATT_SELFPOWER + * - MAX_USB_POWER may be nonzero. + */ +#endif + +#ifndef MAX_USB_POWER +/* Any hub supports this steady state bus power consumption */ +#define MAX_USB_POWER 100 /* mA */ +#endif + + +/*-------------------------------------------------------------------------*/ + +#define xprintk(d,level,fmt,args...) \ + dev_printk(level , &(d)->gadget->dev , fmt , ## args) + +#ifdef DEBUG +#define DEBUG_ON +#undef DEBUG +#define DEBUG(dev,fmt,args...) \ + xprintk(dev , KERN_DEBUG , fmt , ## args) +#define MDEBUG(fmt,args...) \ + printk(KERN_DEBUG DRIVER_NAME ": " fmt, ## args) +#else +#define DEBUG(dev,fmt,args...) \ + do { } while (0) +#define MDEBUG(fmt,args...) \ + do { } while (0) +#endif /* DEBUG */ + +#ifdef VERBOSE +#define VDEBUG DEBUG +#else +#define VDEBUG(dev,fmt,args...) \ + do { } while (0) +#endif /* DEBUG */ + +#define ERROR(dev,fmt,args...) \ + xprintk(dev , KERN_ERR , fmt , ## args) +#define WARN(dev,fmt,args...) \ + xprintk(dev , KERN_WARNING , fmt , ## args) +#define INFO(dev,fmt,args...) \ + xprintk(dev , KERN_INFO , fmt , ## args) + +#define MINFO(fmt,args...) \ + printk(KERN_INFO DRIVER_NAME ": " fmt, ## args) + + +/*-------------------------------------------------------------------------*/ + +static char *file = 0; +static int ro = 0; +static char *transport = "BBB"; +static unsigned int buflen = 16384; + +module_param(file, charp, S_IRUGO); +module_param(ro, bool, S_IRUGO); +module_param(transport, charp, S_IRUGO); +module_param(buflen, uint, S_IRUGO); + + +/*-------------------------------------------------------------------------*/ + +/* USB protocol value = the transport method */ +#define USB_PR_CBI 0x00 // Control/Bulk/Interrupt +#define USB_PR_CB 0x01 // Control/Bulk w/o interrupt +#define USB_PR_BULK 0x50 // Bulk-only + +/* USB subclass value = the protocol encapsulation */ +#define USB_SC_SCSI 0x06 // Transparent SCSI + +/* Bulk-only data structures */ + +/* Command Block Wrapper */ +struct bulk_cb_wrap { + u32 Signature; // Contains 'USBC' + u32 Tag; // Unique per command id + u32 DataTransferLength; // Size of the data + u8 Flags; // Direction in bit 7 + u8 Lun; // LUN normally 0 + u8 Length; // Of the CDB, <= MAX_COMMAND_SIZE + u8 CDB[16]; // Command Data Block +}; + +#define USB_BULK_CB_WRAP_LEN 31 +#define USB_BULK_CB_SIG 0x43425355 // Spells out USBC +#define USB_BULK_FLAG_IN 0x80 +#define USB_BULK_FLAG_OUT 0 + +/* Command Status Wrapper */ +struct bulk_cs_wrap { + u32 Signature; // Should = 'USBS' + u32 Tag; // Same as original command + u32 Residue; // Amount not transferred + u8 Status; // See below +}; + +#define USB_BULK_CS_WRAP_LEN 13 +#define USB_BULK_CS_SIG 0x53425355 // Spells out 'USBS' +#define USB_STATUS_PASS 0 +#define USB_STATUS_FAIL 1 +#define USB_STATUS_PHASE_ERROR 2 + +/* Bulk-only class specific requests */ +#define USB_BULK_RESET_REQUEST 0xff +#define USB_BULK_GET_MAX_LUN_REQUEST 0xfe + + +/* CBI Interrupt data structure */ +struct interrupt_data { + u8 bType; + u8 bValue; +}; + +#define CBI_INTERRUPT_DATA_LEN 2 + +/* CBI Accept Device-Specific Command request */ +#define USB_CBI_ADSC_REQUEST 0x00 + + +#define MAX_COMMAND_SIZE 16 // Length of a SCSI Command Data Block + +/* SCSI commands that we recognize */ +#define SC_FORMAT_UNIT 0x04 +#define SC_INQUIRY 0x12 +#define SC_MODE_SENSE_6 0x1a +#define SC_MODE_SENSE_10 0x5a +#define SC_READ_6 0x08 +#define SC_READ_10 0x28 +#define SC_READ_CAPACITY 0x25 +#define SC_RELEASE 0x17 +#define SC_REQUEST_SENSE 0x03 +#define SC_RESERVE 0x16 +#define SC_SEND_DIAGNOSTIC 0x1d +#define SC_SYNCHRONIZE_CACHE 0x35 +#define SC_TEST_UNIT_READY 0x00 +#define SC_WRITE_6 0x0a +#define SC_WRITE_10 0x2a + +/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */ +#define SS_NO_SENSE 0 +#define SS_COMMUNICATION_FAILURE 0x040800 +#define SS_INVALID_COMMAND 0x052000 +#define SS_INVALID_FIELD_IN_CDB 0x052400 +#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100 +#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500 +#define SS_RESET_OCCURRED 0x062900 +#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900 +#define SS_UNRECOVERED_READ_ERROR 0x031100 +#define SS_WRITE_ERROR 0x030c02 +#define SS_WRITE_PROTECTED 0x072700 + + +/*-------------------------------------------------------------------------*/ + +enum data_direction { + DATA_DIR_UNKNOWN = 0, + DATA_DIR_FROM_HOST, + DATA_DIR_TO_HOST, + DATA_DIR_NONE +}; + +/* Encapsulate the module settings and backing-file data */ +static struct { + char *filename; + int ro; + int transport_type; + char *transport_name; + int use_intr; + unsigned int buflen; + + struct file *filp; + loff_t file_length; + loff_t num_sectors; +} mod_data; + + +/* Big enough to hold our biggest descriptor */ +#define EP0_BUFSIZE 256 +#define DELAYED_STATUS (EP0_BUFSIZE + 999) // An impossibly large value + +/* Number of buffers we will use. 2 is enough for double-buffering */ +#define NUM_BUFFERS 2 + +enum fsg_buffer_state { + BUF_STATE_EMPTY = 0, + BUF_STATE_FULL, + BUF_STATE_BUSY +}; + +enum fsg_request_state { + REQ_STATE_IDLE = 0, + REQ_STATE_BUSY, + REQ_STATE_UNLINKING +}; + +struct fsg_dev; +struct fsg_buffhd; +typedef void (*fsg_routine_t)(struct fsg_dev *, struct fsg_buffhd *); + +struct fsg_buffhd { + void *buf; + dma_addr_t dma; + enum fsg_buffer_state state; + fsg_routine_t usb_done_routine; + struct fsg_buffhd *next; + + struct usb_request *inreq; + enum fsg_request_state inreq_state; + struct usb_request *outreq; + enum fsg_request_state outreq_state; +}; + +enum fsg_state { + FSG_STATE_COMMAND_PHASE = -10, + FSG_STATE_DATA_PHASE, + FSG_STATE_STATUS_PHASE, + + FSG_STATE_IDLE = 0, + FSG_STATE_ABORT_WRITE, + FSG_STATE_RESET, + FSG_STATE_INTERFACE_CHANGE, + FSG_STATE_CONFIG_CHANGE, + FSG_STATE_DISCONNECT, + FSG_STATE_UNBIND, +}; + +struct fsg_dev { + spinlock_t lock; + struct usb_gadget *gadget; + + struct usb_request *ep0req; // For control responses + enum fsg_request_state ep0req_state; + struct usb_request *intreq; // For interrupt responses + enum fsg_request_state intreq_state; + struct fsg_buffhd *intr_buffhd; + enum fsg_request_state fileio_state; + + enum fsg_state state; // For exception handling + fsg_routine_t handler; + struct completion *unbind_notifier; + + u8 config, new_config; + + unsigned registered : 1; + unsigned bulk_in_enabled : 1; + unsigned bulk_out_enabled : 1; + unsigned intr_in_enabled : 1; + unsigned phase_error : 1; + + unsigned long atomic_bitflags; +#define CAN_UNREGISTER 0 + + struct usb_ep *bulk_in; + struct usb_ep *bulk_out; + struct usb_ep *intr_in; + + fsg_routine_t next_fill_routine; + struct fsg_buffhd *next_buffhd_to_fill; + struct fsg_buffhd *next_buffhd_to_drain; + struct fsg_buffhd buffhds[NUM_BUFFERS]; + + wait_queue_head_t thread_wqh; + struct completion thread_notifier; + int thread_pid; + enum { THREAD_DO_NOTHING, + THREAD_DO_READ, + THREAD_DO_WRITE, + THREAD_DO_FSYNC } thread_action; + int thread_wakeup_code; +#define THREAD_WORK_CODE 1 +#define THREAD_STOP_CODE 2 + + int cmnd_size; + u8 cmnd[MAX_COMMAND_SIZE]; + enum data_direction data_dir; + u32 data_size; + u32 data_size_from_cmnd; + u32 residue; + u32 bulk_only_tag; + + u32 sense_data; + u32 sense_data_info; + u32 unit_attention_data; + + loff_t file_offset; + u32 file_amount_left; + u32 file_amount_done; + loff_t usb_offset; + u32 usb_amount_left; +}; + +static int inline exception_in_progress(struct fsg_dev *fsg) +{ + return (fsg->state > FSG_STATE_IDLE); +} + + +static struct fsg_dev *the_fsg; +static struct usb_gadget_driver fsg_driver; + +static void store_status(struct fsg_dev *, struct fsg_buffhd *); +static void get_next_command(struct fsg_dev *, struct fsg_buffhd *); +static void close_backing_file(void); + + +/*-------------------------------------------------------------------------*/ + +/* Routines for unaligned data access */ +static u16 inline get_be16(u8 *buf) +{ + return ((u16) buf[0] << 8) | ((u16) buf[1]); +} + +static u32 inline get_be32(u8 *buf) +{ + return ((u32) buf[0] << 24) | ((u32) buf[1] << 16) | + ((u32) buf[2] << 8) | ((u32) buf[3]); +} + +static void inline put_be16(u8 *buf, u16 val) +{ + buf[0] = val >> 8; + buf[1] = val; +} + +static void inline put_be32(u8 *buf, u32 val) +{ + buf[0] = val >> 24; + buf[1] = val >> 16; + buf[2] = val >> 8; + buf[3] = val; +} + + +/*-------------------------------------------------------------------------*/ + +/* + * DESCRIPTORS ... most are static, but strings and (full) configuration + * descriptors are built on demand. Also the (static) config and interface + * descriptors are adjusted during fsg_bind(). + */ +#define STRING_MANUFACTURER 7 +#define STRING_PRODUCT 8 +#define STRING_SERIAL 9 + +/* There is only one configuration. */ +#define CONFIG_VALUE 17 + +static const struct usb_device_descriptor +device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = __constant_cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_PER_INTERFACE, + .bMaxPacketSize0 = EP0_MAXPACKET, + + .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_ID), + .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_ID), + .bcdDevice = __constant_cpu_to_le16(DRIVER_VERSION_NUM), + .iManufacturer = STRING_MANUFACTURER, + .iProduct = STRING_PRODUCT, + .iSerialNumber = STRING_SERIAL, + .bNumConfigurations = 1, +}; + +static struct usb_config_descriptor +config_desc = { + .bLength = sizeof config_desc, + .bDescriptorType = USB_DT_CONFIG, + + /* wTotalLength adjusted during bind() */ + .bNumInterfaces = 1, + .bConfigurationValue = CONFIG_VALUE, + .bmAttributes = USB_CONFIG_ATT_ONE | SELFPOWER, + .bMaxPower = (MAX_USB_POWER + 1) / 2, +}; + +/* There is only one interface. */ + +static struct usb_interface_descriptor +intf_desc = { + .bLength = sizeof intf_desc, + .bDescriptorType = USB_DT_INTERFACE, + + .bNumEndpoints = 2, // Adjusted during bind() + .bInterfaceClass = USB_CLASS_MASS_STORAGE, + .bInterfaceSubClass = USB_SC_SCSI, + .bInterfaceProtocol = USB_PR_BULK, // Adjusted during bind() +}; + +/* Three full-speed endpoint descriptors: bulk-in, bulk-out, + * and interrupt-in. */ + +static const struct usb_endpoint_descriptor +fs_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = EP_BULK_IN_NUM | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(64), +}; + +static const struct usb_endpoint_descriptor +fs_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = EP_BULK_OUT_NUM, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(64), +}; + +static const struct usb_endpoint_descriptor +fs_intr_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = EP_INTR_IN_NUM | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(2), + .bInterval = 32, // frames -> 32 ms +}; + +#ifdef HIGHSPEED + +/* + * USB 2.0 devices need to expose both high speed and full speed + * descriptors, unless they only run at full speed. + * + * That means alternate endpoint descriptors (bigger packets) + * and a "device qualifier" ... plus more construction options + * for the config descriptor. + */ +static const struct usb_endpoint_descriptor +hs_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = EP_BULK_IN_NUM | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static const struct usb_endpoint_descriptor +hs_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = EP_BULK_OUT_NUM, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), + .bInterval = 1, // NAK every 1 uframe +}; + +static const struct usb_endpoint_descriptor +hs_intr_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = EP_INTR_IN_NUM | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(2), + .bInterval = 9, // 2**(9-1) = 256 uframes -> 32 ms +}; + +static const struct usb_qualifier_descriptor +dev_qualifier = { + .bLength = sizeof dev_qualifier, + .bDescriptorType = USB_DT_DEVICE_QUALIFIER, + + .bcdUSB = __constant_cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_PER_INTERFACE, + + /* assumes ep0 uses the same value for both speeds ... */ + .bMaxPacketSize0 = EP0_MAXPACKET, + .bNumConfigurations = 1, +}; + +/* Maxpacket and other transfer characteristics vary by speed. */ +#define ep_desc(g,fs,hs) (((g)->speed==USB_SPEED_HIGH) ? (hs) : (fs)) + +#else + +/* If there's no high speed support, maxpacket doesn't change. */ +#define ep_desc(g,fs,hs) fs + +#endif /* !HIGHSPEED */ + +/* The CBI specification limits the serial string to 12 uppercase hexadecimal + * characters. */ +static char serial[13]; + +/* Static strings, in ISO 8859/1 */ +static struct usb_string strings[] = { + { STRING_MANUFACTURER, UTS_SYSNAME " " UTS_RELEASE " with " CHIP, }, + { STRING_PRODUCT, longname, }, + { STRING_SERIAL, serial, }, + { } // end of list +}; + +static struct usb_gadget_strings stringtab = { + .language = 0x0409, // en-us + .strings = strings, +}; + +/* + * Config descriptors are handcrafted. They must agree with the code + * that sets configurations and with code managing interfaces and their + * altsettings. They must also handle different speeds and other-speed + * requests. + */ +static int populate_config_buf(enum usb_device_speed speed, + u8 *buf0, u8 type, unsigned index) +{ + u8 *buf = buf0; +#ifdef HIGHSPEED + int hs; +#endif + + if (index > 0) + return -EINVAL; + if (config_desc.wTotalLength > EP0_BUFSIZE) + return -EDOM; + + /* Config (or other speed config) */ + memcpy(buf, &config_desc, USB_DT_CONFIG_SIZE); + buf[1] = type; + buf += USB_DT_CONFIG_SIZE; + + /* Interface */ + memcpy(buf, &intf_desc, USB_DT_INTERFACE_SIZE); + buf += USB_DT_INTERFACE_SIZE; + + /* The endpoints in the interface (at that speed) */ +#ifdef HIGHSPEED + hs = (speed == USB_SPEED_HIGH); + if (type == USB_DT_OTHER_SPEED_CONFIG) + hs = !hs; + if (hs) { + memcpy(buf, &hs_bulk_in_desc, USB_DT_ENDPOINT_SIZE); + buf += USB_DT_ENDPOINT_SIZE; + memcpy(buf, &hs_bulk_out_desc, USB_DT_ENDPOINT_SIZE); + buf += USB_DT_ENDPOINT_SIZE; + if (mod_data.use_intr) { + memcpy(buf, &hs_intr_in_desc, USB_DT_ENDPOINT_SIZE); + buf += USB_DT_ENDPOINT_SIZE; + } + } else +#endif + { + memcpy(buf, &fs_bulk_in_desc, USB_DT_ENDPOINT_SIZE); + buf += USB_DT_ENDPOINT_SIZE; + memcpy(buf, &fs_bulk_out_desc, USB_DT_ENDPOINT_SIZE); + buf += USB_DT_ENDPOINT_SIZE; + if (mod_data.use_intr) { + memcpy(buf, &fs_intr_in_desc, USB_DT_ENDPOINT_SIZE); + buf += USB_DT_ENDPOINT_SIZE; + } + } + + return buf - buf0; +} + + +/*-------------------------------------------------------------------------*/ + +/* Must own the device lock */ +static int start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, + struct usb_request *req, enum fsg_request_state *pstate) +{ + int rc; + + rc = usb_ep_queue(ep, req, GFP_ATOMIC); + if (rc == 0) + *pstate = REQ_STATE_BUSY; + else { + + /* We can't do much more than wait for a reset */ + DEBUG(fsg, "ep_queue --> %d\n", rc); + } + return rc; +} + +/* Must own the device lock */ +static void cancel_transfer(struct fsg_dev *fsg, struct usb_ep *ep, + struct usb_request *req, enum fsg_request_state *pstate) +{ + *pstate = REQ_STATE_UNLINKING; + + /* + * Some controllers may call the completion routine from within the + * dequeue() function, so we must release the device lock here. + * In general just grabbing it back isn't safe because there might + * be a pending exception we wouldn't handle. That can't happen + * here since we don't release any requests when we grab the lock. + * Still, there could be an exception pending, so anyone who calls + * this routine should test for that before doing much else. + */ + spin_unlock(&fsg->lock); + usb_ep_dequeue(ep, req); + spin_lock(&fsg->lock); +} + + +/*-------------------------------------------------------------------------*/ + +/* Must own the device lock */ +static void handle_exception(struct fsg_dev *fsg) +{ + int i; + enum fsg_state old_state; + fsg_routine_t old_handler; + + if (!exception_in_progress(fsg) || !fsg->handler) + return; + + /* See whether all the transfers are idle */ + if (fsg->fileio_state != REQ_STATE_IDLE || + fsg->ep0req_state != REQ_STATE_IDLE || + fsg->intreq_state != REQ_STATE_IDLE) + return; + + for (i = 0; i < NUM_BUFFERS; ++i) { + struct fsg_buffhd *bh = &fsg->buffhds[i]; + + if (bh->inreq_state != REQ_STATE_IDLE || + bh->outreq_state != REQ_STATE_IDLE) + return; + } + + /* If we get here, then we are the first caller to see everything + * in an idle state. Reset the I/O buffer states and pointers, + * reset the SCSI sense and the exception, and invoke the handler. */ + for (i = 0; i < NUM_BUFFERS; ++i) { + struct fsg_buffhd *bh = &fsg->buffhds[i]; + + bh->state = BUF_STATE_EMPTY; + bh->usb_done_routine = NULL; + } + fsg->next_fill_routine = NULL; + fsg->next_buffhd_to_fill = fsg->next_buffhd_to_drain = + &fsg->buffhds[0]; + + fsg->phase_error = 0; + fsg->sense_data = fsg->unit_attention_data = SS_NO_SENSE; + fsg->sense_data_info = 0; + + old_state = fsg->state; + old_handler = fsg->handler; + fsg->state = FSG_STATE_IDLE; + fsg->handler = NULL; + old_handler(fsg, fsg->next_buffhd_to_fill); + + /* Prepare to receive the next command */ + if (fsg->config && old_state >= FSG_STATE_INTERFACE_CHANGE) + get_next_command(fsg, fsg->next_buffhd_to_fill); +} + +/* Must own the device lock */ +static void raise_exception(struct fsg_dev *fsg, + enum fsg_state new_state, fsg_routine_t new_handler) +{ + /* Do nothing if a higher-priority exception is already in progress. + * If a lower-priority exception is in progress, preempt it. */ + if (fsg->state >= new_state) + return; + fsg->state = new_state; + fsg->handler = NULL; + + /* Cancel any ongoing transfers. cancel_transfer() releases the + * device lock, so we always have to test the state afterward. */ + fsg->thread_wakeup_code = 0; + while (fsg->state == new_state) { + int i; + + for (i = 0; i < NUM_BUFFERS; ++i) { + struct fsg_buffhd *bh = &fsg->buffhds[i]; + + if (bh->inreq_state == REQ_STATE_BUSY) { + cancel_transfer(fsg, fsg->bulk_in, bh->inreq, + &bh->inreq_state); + continue; + } + if (bh->outreq_state == REQ_STATE_BUSY) { + cancel_transfer(fsg, fsg->bulk_out, bh->outreq, + &bh->outreq_state); + continue; + } + } + if (fsg->ep0req_state == REQ_STATE_BUSY) { + cancel_transfer(fsg, fsg->gadget->ep0, fsg->ep0req, + &fsg->ep0req_state); + continue; + } + if (fsg->intreq_state == REQ_STATE_BUSY) { + cancel_transfer(fsg, fsg->intr_in, fsg->intreq, + &fsg->intreq_state); + continue; + } + break; + } + + /* If we get here, then either we were preempted (fsg->state != + * new_state) or else everything has been cancelled so we can try + * to handle the exception. */ + if (fsg->state != new_state) + return; // Preempted + fsg->handler = new_handler; + handle_exception(fsg); +} + +/* Use this if you don't already own the device lock */ +static inline void lock_and_raise_exception(struct fsg_dev *fsg, + enum fsg_state new_state, fsg_routine_t new_handler) +{ + spin_lock(&fsg->lock); + raise_exception(fsg, new_state, new_handler); + spin_unlock(&fsg->lock); +} + + +/* + * acquire_lock - acquire the device lock + * @pstate: the fsg_request_state variable for the request just ended + * + * Call this routine to acquire the device lock -- don't just use spin_lock()! + * This sets the state variable pointed to by pstate back to REQ_STATE_IDLE + * (pstate may be NULL if this isn't being invoked from a request completion + * routine). It also checks to see whether an exception is pending; if one is + * it calls the handler provided everything is quiescent. + * + * Context: local interrupts must be disabled. + * + * Returns: 0 and the lock not held if an exception is in progress, + * Non-zero and the lock acquired otherwise. + */ +static int acquire_lock(struct fsg_dev *fsg, enum fsg_request_state *pstate) +{ + spin_lock(&fsg->lock); + if (pstate) + *pstate = REQ_STATE_IDLE; + if (exception_in_progress(fsg)) { + handle_exception(fsg); + spin_unlock(&fsg->lock); + return 0; + } + return 1; +} + +static inline void release_lock(struct fsg_dev *fsg) +{ + spin_unlock(&fsg->lock); +} + + +/*-------------------------------------------------------------------------*/ + +/* + * The event handlers (request completion callbacks and so on) invoke this + * routine. It simply tries to fill buffers sequentially, until nothing more + * can be done. + */ +static void dispatcher(struct fsg_dev *fsg) +{ + int retry; + struct fsg_buffhd *bh; + fsg_routine_t routine; + + /* Keep trying to fill buffers as long as we can */ + do { + retry = 0; + + bh = fsg->next_buffhd_to_fill; + if (bh->state == BUF_STATE_EMPTY && fsg->next_fill_routine) { + routine = fsg->next_fill_routine; + fsg->next_fill_routine = NULL; + routine(fsg, bh); + retry = 1; + } + } while (retry); +} + + +/*-------------------------------------------------------------------------*/ + +static void wakeup_io_thread(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + fsg->thread_wakeup_code = THREAD_WORK_CODE; + wake_up_all(&fsg->thread_wqh); +} + +static void stop_io_thread(struct fsg_dev *fsg) +{ + fsg->thread_wakeup_code = THREAD_STOP_CODE; + wake_up_all(&fsg->thread_wqh); +} + + +/*-------------------------------------------------------------------------*/ + +static void write_done(struct fsg_dev *fsg) +{ + /* Compute the residue. If we haven't yet received all the requested + * data, halt the endpoint. */ + fsg->residue = fsg->data_size - fsg->file_amount_done; + if (fsg->residue > 0) { + usb_ep_set_halt(fsg->bulk_out); + usb_ep_fifo_flush(fsg->bulk_out); + } + + /* Even if the write has fully finished, this will send back the + * status correctly. */ + raise_exception(fsg, FSG_STATE_ABORT_WRITE, store_status); +} + +static int do_file_writes(struct fsg_dev *fsg) +{ + struct fsg_buffhd *bh; + unsigned int amount; + loff_t file_offset; + ssize_t rc; + + /* At the start of the loop, we hold the device lock and fileio_state + * is REQ_STATE_IDLE. */ + while ((bh = fsg->next_buffhd_to_drain)->state == BUF_STATE_FULL) { + + /* Did anything go wrong with the reception, or are we at + * the end of the request? */ + if (bh->outreq->status || + bh->outreq->actual != bh->outreq->length) { + if (fsg->file_amount_left > 0 && + fsg->sense_data == SS_NO_SENSE) { + fsg->sense_data = SS_COMMUNICATION_FAILURE; + fsg->sense_data_info = fsg->file_offset >> 9; + } + write_done(fsg); + return 0; + } + + fsg->fileio_state = REQ_STATE_BUSY; + release_lock(fsg); + local_irq_enable(); + + /* Perform the write */ + amount = bh->outreq->actual; + file_offset = fsg->file_offset; + rc = vfs_write(mod_data.filp, (char __user *) bh->buf, + amount, &file_offset); + VDEBUG(fsg, "file write %u @ %llu -> %d\n", amount, + (unsigned long long) fsg->file_offset, + (int) rc); + if (signal_pending(current)) + return -EINTR; // Interrupted! + + if (rc < 0) { + DEBUG(fsg, "error in file write: %d\n", (int) rc); + rc = 0; + } else if (rc < amount) { + DEBUG(fsg, "partial file write: %d/%u\n", + (int) rc, amount); + rc -= (rc & 511); // Round down to a sector + } + fsg->file_offset += rc; + fsg->file_amount_done += rc; + fsg->file_amount_left -= rc; + + /* Re-acquire the device lock to update the various states */ + local_irq_disable(); + if (!acquire_lock(fsg, &fsg->fileio_state)) + return -EBUSY; // Exception in progress + + /* If an error occurred, report it and its position */ + if (rc < amount) { + fsg->sense_data = SS_WRITE_ERROR; + fsg->sense_data_info = fsg->file_offset >> 9; + write_done(fsg); + return 0; + } + + bh->state = BUF_STATE_EMPTY; + dispatcher(fsg); // Get another buffer + fsg->next_buffhd_to_drain = bh->next; + } + + /* At the end of the loop, we hold the device lock and fileio_state + * is REQ_STATE_IDLE. */ + fsg->thread_wakeup_code = 0; + return 0; +} + +static void get_write_data_from_host(struct fsg_dev *fsg, + struct fsg_buffhd *bh) +{ + unsigned int amount; + unsigned int partial_page; + + /* Figure out how much we want to get: + * Try to get the remaining amount. + * But don't get more than the buffer size. + * And don't try to go past the end of the file. + * Finally, if we're not at a page boundary, don't go past + * the next page. + * If this means getting 0 when the remaining amount is > 0, + * then we were asked to write past the end of file. */ + amount = fsg->usb_amount_left; + amount = min(amount, mod_data.buflen); + amount = min((loff_t) amount, + mod_data.file_length - fsg->usb_offset); + partial_page = fsg->usb_offset & (PAGE_CACHE_SIZE - 1); + if (partial_page > 0) + amount = min(amount, (unsigned int) PAGE_CACHE_SIZE - + partial_page); + if (amount == 0) { + if (fsg->usb_amount_left > 0) { + fsg->sense_data = + SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + fsg->sense_data_info = fsg->usb_offset >> 9; + } + + /* Tell the I/O thread to move on to the status phase by + * setting the request status to an error code. */ + bh->state = BUF_STATE_FULL; + bh->outreq->status = -EIO; + wakeup_io_thread(fsg, bh); + return; + } + + /* Get the buffer from the host */ + bh->usb_done_routine = wakeup_io_thread; + bh->state = BUF_STATE_BUSY; + bh->outreq->length = amount; + bh->outreq->context = bh; + start_transfer(fsg, fsg->bulk_out, bh->outreq, &bh->outreq_state); + + /* Prepare to get the next buffer */ + fsg->usb_offset += amount; + fsg->usb_amount_left -= amount; + fsg->next_fill_routine = get_write_data_from_host; + fsg->next_buffhd_to_fill = bh->next; +} + + +static void read_done(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + fsg->residue = fsg->data_size - fsg->file_amount_done; + if (mod_data.transport_type == USB_PR_BULK && fsg->residue > 0) + usb_ep_set_halt(fsg->bulk_in); + fsg->next_fill_routine = store_status; +} + +static int do_file_reads(struct fsg_dev *fsg) +{ + struct fsg_buffhd *bh; + unsigned int amount; + unsigned int partial_page; + loff_t file_offset; + ssize_t rc = 1; + + /* At the start of the loop, we hold the device lock and fileio_state + * is REQ_STATE_IDLE. */ + while ((bh = fsg->next_buffhd_to_fill)->state == BUF_STATE_EMPTY) { + fsg->fileio_state = REQ_STATE_BUSY; + bh->state = BUF_STATE_BUSY; + release_lock(fsg); + local_irq_enable(); + + /* Figure out how much we need to read: + * Try to read the remaining amount. + * But don't read more than the buffer size. + * And don't try to read past the end of the file. + * Finally, if we're not at a page boundary, don't read past + * the next page. + * If this means reading 0 when the remaining amount is > 0, + * then we were asked to read past the end of file. */ + amount = fsg->file_amount_left; + amount = min(amount, mod_data.buflen); + amount = min((loff_t) amount, + mod_data.file_length - fsg->file_offset); + partial_page = fsg->file_offset & (PAGE_CACHE_SIZE - 1); + if (partial_page > 0) + amount = min(amount, (unsigned int) PAGE_CACHE_SIZE - + partial_page); + if (amount == 0 && fsg->file_amount_left > 0) { + fsg->sense_data = + SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + fsg->sense_data_info = fsg->file_offset >> 9; + fsg->file_amount_left = 0; // Time to stop + } + + /* Perform the read */ + file_offset = fsg->file_offset; + rc = vfs_read(mod_data.filp, (char __user *) bh->buf, + amount, &file_offset); + VDEBUG(fsg, "file read %u @ %llu -> %d\n", amount, + (unsigned long long) fsg->file_offset, + (int) rc); + if (signal_pending(current)) + return -EINTR; // Interrupted! + + if (rc < 0) { + DEBUG(fsg, "error in file read: %d\n", (int) rc); + rc = 0; + } else if (rc < amount) { + DEBUG(fsg, "partial file read: %d/%u\n", + (int) rc, amount); + rc -= (rc & 511); // Round down to a sector + } + fsg->file_offset += rc; + fsg->file_amount_done += rc; + fsg->file_amount_left -= rc; + bh->inreq->length = rc; + + /* If an error occurred, report it and its position */ + if (rc < amount) { + fsg->sense_data = SS_UNRECOVERED_READ_ERROR; + fsg->sense_data_info = fsg->file_offset >> 9; + fsg->file_amount_left = 0; // Time to stop + } + + /* Re-acquire the device lock to update the various states */ + local_irq_disable(); + if (!acquire_lock(fsg, &fsg->fileio_state)) + return -EBUSY; // Exception in progress + + /* If all the requested data was sent, or if this was a + * zero-length buffer (indicating an early end) then we're + * finished. */ + if (fsg->file_amount_done == fsg->data_size) + rc = 0; // We're finished + + /* We assume there no errors will occur during the USB + * transfer -- if they do, the host will have to sort things + * out. So we don't need a usb_done_routine unless this is + * the last buffer, after which we must send the status. */ + if (rc == 0) + bh->usb_done_routine = read_done; + + /* Send the buffer to the host */ + bh->state = BUF_STATE_BUSY; + bh->inreq->context = bh; + bh->inreq->zero = 0; + start_transfer(fsg, fsg->bulk_in, bh->inreq, &bh->inreq_state); + fsg->next_buffhd_to_fill = bh->next; + + if (rc == 0) + break; // No more left to do + } + + /* At the end of the loop, we hold the device lock and fileio_state + * is REQ_STATE_IDLE. */ + fsg->thread_wakeup_code = 0; + if (rc > 0) // Still more data to read? + fsg->next_fill_routine = wakeup_io_thread; + return 0; +} + + +static int do_file_fsync(struct fsg_dev *fsg) +{ + int rc, err; + struct file *filp = mod_data.filp; + struct inode *inode = filp->f_dentry->d_inode; + + if (!filp->f_op || !filp->f_op->fsync) + rc = -EINVAL; + else { + + /* Prepare to perform the file sync */ + fsg->fileio_state = REQ_STATE_BUSY; + release_lock(fsg); + local_irq_enable(); + + /* Sync the file data, don't bother with the metadata. + * This code was copied from fs/buffer.c:sys_fdatasync(). */ + down(&inode->i_sem); + current->flags |= PF_SYNCWRITE; + rc = filemap_fdatawrite(inode->i_mapping); + err = filp->f_op->fsync(filp, filp->f_dentry, 1); + if (!rc) + rc = err; + err = filemap_fdatawait(inode->i_mapping); + if (!rc) + rc = err; + current->flags &= ~PF_SYNCWRITE; + up(&inode->i_sem); + VDEBUG(fsg, "fdatasync -> %d\n", rc); + + /* Re-acquire the device lock */ + if (signal_pending(current)) + return -EINTR; // Interrupted! + local_irq_disable(); + if (!acquire_lock(fsg, &fsg->fileio_state)) + return -EBUSY; // Exception in progress + } + + if (rc) + fsg->sense_data = SS_WRITE_ERROR; + fsg->next_fill_routine = store_status; + dispatcher(fsg); + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +/* + * This is the file-I/O helper thread. It is responsible for carrying out + * the read and write operations on the backing file. It is also responsible + * for closing the backing file upon exiting. + * + * Mostly it just sits in an interruptible wait state until woken up to + * handle a new I/O request. However, when it receives a signal or if it + * is asked to exit, it unregisters the gadget driver, closes the backing + * file, and terminates. + */ +static int fsg_io_thread(void *fsg_) +{ + struct fsg_dev *fsg = (struct fsg_dev *) fsg_; + sigset_t unblock; + int rc; + + /* Release all our userspace resources */ + daemonize("file-storage-gadget"); + + /* Wait until the gadget has been registered */ + wait_for_completion(&fsg->thread_notifier); + if (!fsg->registered) + goto out; + + /* Allow the thread to be killed by a signal */ + siginitset(&unblock, sigmask(SIGINT) | sigmask(SIGTERM) | + sigmask(SIGKILL)); + sigprocmask(SIG_UNBLOCK, &unblock, NULL); + + /* Arrange for userspace references to be interpreted as kernel + * pointers. That way we can pass a kernel pointer to a routine + * that expects a __user pointer and it will work okay. */ + set_fs(get_ds()); + + /* The main loop */ + for (;;) { + + /* At the start of the loop, we don't own the device lock + * and fsg->fileio_state is REQ_STATE_IDLE. */ + rc = wait_event_interruptible(fsg->thread_wqh, + fsg->thread_wakeup_code); + + /* If a signal arrived or the module is being unloaded, + * we must exit the main loop. */ + if (rc) + DEBUG(fsg, "I/O thread exiting on signal\n"); + if (rc || fsg->thread_wakeup_code == THREAD_STOP_CODE) + break; + fsg->thread_wakeup_code = 0; + + local_irq_disable(); + + /* Try to get the lock. If an exception is in progress, + * we must wait until it ends. */ + if (!acquire_lock(fsg, NULL)) { + local_irq_enable(); + continue; + } + + /* Process the pending request */ + switch (fsg->thread_action) { + case THREAD_DO_READ: + rc = do_file_reads(fsg); + break; + case THREAD_DO_WRITE: + rc = do_file_writes(fsg); + break; + case THREAD_DO_FSYNC: + rc = do_file_fsync(fsg); + break; + default: + DEBUG(fsg, "Unknown I/O thread command %d\n", + fsg->thread_action); + rc = 0; + break; + } + /* + * If the I/O transfer was interrupted by a signal then we + * don't own the device lock. We must clear our request + * state so that the unregistration below can succeed. + * Doing so without acquiring the lock first is dangerous; + * it could mean that a pending exception never gets handled. + * But in this case it doesn't matter, because we are going + * to preempt any existing exceptions when we unregister. + */ + if (rc == -EINTR) { + fsg->fileio_state = REQ_STATE_IDLE; + continue; + } + + /* If the I/O transfer was halted by an exception in progress + * then we don't own the device lock; otherwise we do. */ + if (rc == 0) + release_lock(fsg); + local_irq_enable(); + } + + /* In case we are exiting because of a signal, unregister the + * gadget driver and close the backing file. */ + if (test_and_clear_bit(CAN_UNREGISTER, &fsg->atomic_bitflags)) { + usb_gadget_unregister_driver(&fsg_driver); + close_backing_file(); + } + +out: + complete(&fsg->thread_notifier); // Signal that we're exiting + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +static int do_inquiry(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + u8 *buf = (u8 *) bh->buf; + + static char vendor_id[] = "Linux "; + static char product_id[] = "File-Stor Gadget"; + + memset(buf, 0, 8); // Non-removeable, direct-access device + buf[2] = 2; // ANSI SCSI level 2 + buf[3] = 2; // SCSI-2 INQUIRY data format + buf[4] = 31; // Additional length + // No special options + sprintf(buf + 8, "%-8s%-16s%04x", vendor_id, product_id, + DRIVER_VERSION_NUM); + return 36; +} + + +static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + u8 *buf = (u8 *) bh->buf; + + memset(buf, 0, 18); + buf[0] = 0x80 | 0x70; // Valid, current error + buf[2] = fsg->sense_data >> 16; // Sense key + put_be32(&buf[3], fsg->sense_data_info); // Sense information + buf[7] = 18 - 7; // Additional sense length + buf[12] = fsg->sense_data >> 8; // ASC + buf[13] = fsg->sense_data; // ASCQ + + fsg->sense_data = SS_NO_SENSE; + fsg->sense_data_info = 0; + return 18; +} + + +static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + u32 lba = get_be32(&fsg->cmnd[2]); + int pmi = fsg->cmnd[8]; + u8 *buf = (u8 *) bh->buf; + + /* Check the PMI and LBA fields */ + if (pmi > 1 || (pmi == 0 && lba != 0)) { + fsg->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + put_be32(&buf[0], mod_data.num_sectors - 1); // Max logical block + put_be32(&buf[4], 512); // Block length + return 8; +} + + +static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh, + int mslen) +{ + u8 *buf = (u8 *) bh->buf; + u8 *buf0 = buf; + int pc, page_code; + int changeable_values, all_pages; + int valid_page = 0; + int len, limit; + + if ((fsg->cmnd[1] & ~0x08) != 0) { // Mask away DBD + fsg->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + pc = fsg->cmnd[2] >> 6; + page_code = fsg->cmnd[2] & 0x3f; + if (pc == 3) { + fsg->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED; + return -EINVAL; + } + changeable_values = (pc == 1); + all_pages = (page_code == 0x3f); + + /* Write the mode parameter header. Fixed values are: default + * medium type, no cache control (DPOFUA), and no block descriptors. + * The only variable value is the WriteProtect bit. We will fill in + * the mode data length later. */ + memset(buf, 0, 8); + if (mslen == 6) { // SC_MODE_SENSE_6 + buf[2] = (mod_data.ro ? 0x80 : 0x00); // WP, DPOFUA + buf += 4; + limit = 255; + } else { // SC_MODE_SENSE_10 + buf[3] = (mod_data.ro ? 0x80 : 0x00); // WP, DPOFUA + buf += 8; + limit = 65535; // Should really be mod_data.buflen + } + + /* No block descriptors */ + + /* The mode pages, in numerical order. The only page we support + * is the Caching page. */ + if (page_code == 0x08 || all_pages) { + valid_page = 1; + buf[0] = 0x08; // Page code + buf[1] = 10; // Page length + memset(buf+2, 0, 10); // None of the fields are changeable + + if (!changeable_values) { + buf[2] = 0x04; // Write cache enable, + // Read cache not disabled + // No cache retention priorities + put_be16(&buf[4], 0xffff); // Don't disable prefetch + // Minimum prefetch = 0 + put_be16(&buf[8], 0xffff); // Maximum prefetch + put_be16(&buf[10], 0xffff); // Maximum prefetch ceiling + } + buf += 12; + } + + /* Check that a valid page was requested and the mode data length + * isn't too long. */ + len = buf - buf0; + if (!valid_page || len > limit) { + fsg->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + /* Store the mode data length */ + if (mslen == 6) + buf0[0] = len - 1; + else + put_be16(buf0, len - 2); + return len; +} + + +static int do_synchronize_cache(struct fsg_dev *fsg) +{ + if (mod_data.ro) + return 0; + + /* We ignore the requested LBA and ask the file I/O thread to + * write out all file's data buffers. */ + fsg->next_fill_routine = wakeup_io_thread; + fsg->thread_action = THREAD_DO_FSYNC; + return -EIO; // No return data or status +} + + +static int do_read(struct fsg_dev *fsg, int readlen) +{ + u32 lba; + + /* Get the starting Logical Block Address and check that it's + * not too big */ + if (readlen == 6) + lba = (fsg->cmnd[1] << 16) | get_be16(&fsg->cmnd[2]); + else + lba = get_be32(&fsg->cmnd[2]); + if (lba >= mod_data.num_sectors) { + fsg->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + + /* Prepare to carry out the file reads */ + if (unlikely(fsg->data_size_from_cmnd == 0)) { + fsg->data_dir = DATA_DIR_NONE; + return 0; // Move directly to the status phase + } + fsg->file_amount_left = fsg->data_size_from_cmnd; + fsg->file_amount_done = 0; + fsg->file_offset = ((loff_t) lba) << 9; + fsg->next_fill_routine = wakeup_io_thread; + fsg->thread_action = THREAD_DO_READ; + return -EIO; // No return data or status +} + + +static int do_write(struct fsg_dev *fsg, int writelen) +{ + u32 lba; + + if (mod_data.ro) { + fsg->sense_data = SS_WRITE_PROTECTED; + return -EINVAL; + } + + /* Get the starting Logical Block Address and check that it's + * not too big */ + if (writelen == 6) + lba = (fsg->cmnd[1] << 16) | get_be16(&fsg->cmnd[2]); + else + lba = get_be32(&fsg->cmnd[2]); + if (lba >= mod_data.num_sectors) { + fsg->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + + /* Prepare to carry out the file writes */ + if (unlikely(fsg->data_size_from_cmnd == 0)) { + fsg->data_dir = DATA_DIR_NONE; + return 0; // Move directly to the status phase + } + fsg->file_amount_left = fsg->usb_amount_left = + fsg->data_size_from_cmnd; + fsg->file_amount_done = 0; + fsg->file_offset = fsg->usb_offset = ((loff_t) lba) << 9; + fsg->next_buffhd_to_drain = fsg->next_buffhd_to_fill; + fsg->next_fill_routine = get_write_data_from_host; + fsg->thread_action = THREAD_DO_WRITE; + return -EIO; // No return data or status +} + + +/*-------------------------------------------------------------------------*/ + +static void status_done(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + fsg->state = FSG_STATE_IDLE; + fsg->next_fill_routine = get_next_command; +} + +static void store_status(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + u8 status; + + fsg->thread_action = THREAD_DO_NOTHING; + if (mod_data.transport_type == USB_PR_CB) { + + /* Control-Bulk transport has no status stage! */ + status_done(fsg, NULL); + return; + } + + status = USB_STATUS_PASS; + if (fsg->phase_error) { + DEBUG(fsg, "sending phase-error status\n"); + status = USB_STATUS_PHASE_ERROR; + } else if (fsg->sense_data != SS_NO_SENSE) { + DEBUG(fsg, "sending command-failure status\n"); + status = USB_STATUS_FAIL; + } + + if (mod_data.transport_type == USB_PR_BULK) { + struct bulk_cs_wrap *csw = (struct bulk_cs_wrap *) bh->buf; + + /* Store and send the Bulk-only CSW */ + csw->Signature = __constant_cpu_to_le32(USB_BULK_CS_SIG); + csw->Tag = fsg->bulk_only_tag; + csw->Residue = fsg->residue; + csw->Status = status; + bh->inreq->length = USB_BULK_CS_WRAP_LEN; + + bh->inreq->context = bh; + bh->inreq->zero = 0; + start_transfer(fsg, fsg->bulk_in, bh->inreq, &bh->inreq_state); + + } else { // USB_PR_CBI + struct interrupt_data *buf = (struct interrupt_data *) + bh->buf; + + /* Store and send the Interrupt data */ + buf->bType = 0; + buf->bValue = status; + fsg->intreq->length = CBI_INTERRUPT_DATA_LEN; + + fsg->intr_buffhd = bh; // Point to the right buffhd + fsg->intreq->buf = bh->inreq->buf; + fsg->intreq->dma = bh->inreq->dma; + fsg->intreq->context = bh; + start_transfer(fsg, fsg->intr_in, fsg->intreq, + &fsg->intreq_state); + } + + bh->state = BUF_STATE_BUSY; + bh->usb_done_routine = status_done; + fsg->next_buffhd_to_fill = bh->next; + fsg->state = FSG_STATE_STATUS_PHASE; +} + + +/*-------------------------------------------------------------------------*/ + +static void reply_done(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + if (mod_data.transport_type == USB_PR_BULK && fsg->residue > 0) + usb_ep_set_halt(fsg->bulk_in); + fsg->next_fill_routine = store_status; +} + + +/* Check whether the command is properly formed and whether its data size + * and direction agree with the values we already have. */ +static int check_command(struct fsg_dev *fsg, int cmnd_size, + enum data_direction data_dir, unsigned int mask) +{ + int i; + int lun = fsg->cmnd[1] >> 5; + + if (cmnd_size != fsg->cmnd_size) + goto illegal_command; + + if (fsg->data_dir == DATA_DIR_UNKNOWN) { + fsg->data_dir = data_dir; + fsg->data_size = fsg->data_size_from_cmnd; + } else if (fsg->data_dir != data_dir || + fsg->data_size < fsg->data_size_from_cmnd) + goto illegal_command; + + /* Check that only command bytes listed in the mask are non-zero */ + fsg->cmnd[1] &= 0x1f; // Mask away the LUN + for (i = 1; i < cmnd_size; ++i) { + if (fsg->cmnd[i] && !(mask & (1 << i))) + goto invalid_field; + } + + /* Check the LUN */ + if (lun != 0) { + if (fsg->cmnd[0] != SC_REQUEST_SENSE) + goto invalid_field; + + /* This is a hack, but there's no other way to return error + * information for a command sent to an invalid LUN. */ + fsg->sense_data = SS_LOGICAL_UNIT_NOT_SUPPORTED; + } + return 0; + +illegal_command: + fsg->phase_error = 1; + return -EINVAL; +invalid_field: + fsg->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; +} + +static void decode_scsi_cmnd(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + int reply = -EINVAL; + int i; + +#ifdef DEBUG_ON + { + char cmdbuf[3 * MAX_COMMAND_SIZE]; + + for (i = 0; i < fsg->cmnd_size; ++i) + sprintf(cmdbuf + i*3, " %02x", fsg->cmnd[i]); + DEBUG(fsg, "SCSI command length = %d:%s\n", fsg->cmnd_size, + cmdbuf); + } +#endif + + fsg->state = FSG_STATE_DATA_PHASE; + fsg->phase_error = 0; + fsg->residue = 0; + fsg->thread_action = THREAD_DO_NOTHING; + if (fsg->cmnd[0] != SC_REQUEST_SENSE) { + fsg->sense_data = SS_NO_SENSE; + fsg->sense_data_info = 0; + } + + /* If a unit attention condition exists, only INQUIRY and + * REQUEST-SENSE commands are allowed; anything else must fail. */ + if (fsg->unit_attention_data != SS_NO_SENSE && + fsg->cmnd[0] != SC_INQUIRY && + fsg->cmnd[0] != SC_REQUEST_SENSE) { + fsg->sense_data = fsg->unit_attention_data; + fsg->unit_attention_data = SS_NO_SENSE; + goto error_status; + } + + switch (fsg->cmnd[0]) { + + case SC_INQUIRY: + fsg->data_size_from_cmnd = fsg->cmnd[4]; + if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, + (1<<4))) == 0) + reply = do_inquiry(fsg, bh); + break; + + case SC_MODE_SENSE_6: + fsg->data_size_from_cmnd = fsg->cmnd[4]; + if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, + (1<<1) | (1<<2) | (1<<4))) == 0) + reply = do_mode_sense(fsg, bh, 6); + break; + + case SC_MODE_SENSE_10: + fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]); + if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, + (1<<1) | (1<<2) | (3<<7))) == 0) + reply = do_mode_sense(fsg, bh, 10); + break; + + case SC_READ_6: + i = fsg->cmnd[4]; + fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, + (7<<1) | (1<<4))) == 0) + reply = do_read(fsg, 6); + break; + + case SC_READ_10: + fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]) << 9; + if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, + (0xf<<2) | (3<<7))) == 0) + reply = do_read(fsg, 10); + break; + + case SC_READ_CAPACITY: + fsg->data_size_from_cmnd = 8; + if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, + (0xf<<2) | (1<<8))) == 0) + reply = do_read_capacity(fsg, bh); + break; + + case SC_REQUEST_SENSE: + fsg->data_size_from_cmnd = fsg->cmnd[4]; + if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, + (1<<4))) == 0) + reply = do_request_sense(fsg, bh); + break; + + case SC_SYNCHRONIZE_CACHE: + fsg->data_size_from_cmnd = 0; + if ((reply = check_command(fsg, 10, DATA_DIR_NONE, + (0xf<<2) | (3<<7))) == 0) + reply = do_synchronize_cache(fsg); + break; + + case SC_TEST_UNIT_READY: + fsg->data_size_from_cmnd = 0; + reply = check_command(fsg, 6, DATA_DIR_NONE, 0); + break; + + case SC_WRITE_6: + i = fsg->cmnd[4]; + fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST, + (7<<1) | (1<<4))) == 0) + reply = do_write(fsg, 6); + break; + + case SC_WRITE_10: + fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]) << 9; + if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST, + (0xf<<2) | (3<<7))) == 0) + reply = do_write(fsg, 10); + break; + + /* Some mandatory commands that we recognize but don't implement. + * They don't mean much in this setting. It's left as an exercise + * for anyone interested to implement RESERVE and RELEASE in terms + * of Posix locks. */ + case SC_FORMAT_UNIT: + case SC_RELEASE: + case SC_RESERVE: + case SC_SEND_DIAGNOSTIC: + // Fall through + + default: + fsg->sense_data = SS_INVALID_COMMAND; + break; + } + + /* See if we need to return an error status */ + if (fsg->phase_error || reply == -EINVAL) { + + /* STALL the appropriate pipe */ +error_status: + if (fsg->data_dir == DATA_DIR_TO_HOST) + usb_ep_set_halt(fsg->bulk_in); + else if (fsg->data_dir == DATA_DIR_FROM_HOST) + usb_ep_set_halt(fsg->bulk_out); + fsg->next_fill_routine = store_status; + + /* See if the reply and status should be delayed */ + } else if (reply < 0) { + ; // Do nothing + + /* See if we should send the status immediately */ + } else if (fsg->data_dir == DATA_DIR_NONE) { + fsg->next_fill_routine = store_status; + + /* Otherwise we must send reply data to the host */ + } else { + + /* Don't send more than was requested. But if we're sending + * less than was requested, store the residue and mark the + * short reply with a 0-length packet. */ + if (reply > fsg->data_size_from_cmnd) + reply = fsg->data_size_from_cmnd; + if (reply < fsg->data_size) { + fsg->residue = fsg->data_size - reply; + bh->inreq->zero = 1; + bh->inreq->length = reply; + } else { + bh->inreq->zero = 0; + bh->inreq->length = fsg->data_size; + } + + bh->inreq->context = bh; + bh->state = BUF_STATE_BUSY; + bh->usb_done_routine = reply_done; + start_transfer(fsg, fsg->bulk_in, bh->inreq, &bh->inreq_state); + fsg->next_buffhd_to_fill = bh->next; + } +} + + +/*-------------------------------------------------------------------------*/ + +static void reset_handler(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + struct usb_request *req = fsg->ep0req; + + usb_ep_fifo_flush(fsg->bulk_in); + usb_ep_fifo_flush(fsg->bulk_out); + if (mod_data.use_intr) + usb_ep_fifo_flush(fsg->intr_in); + + if (mod_data.transport_type == USB_PR_BULK) { + + /* Return the status ACK */ + req->length = 0; + req->context = NULL; + start_transfer(fsg, fsg->gadget->ep0, req, &fsg->ep0req_state); + get_next_command(fsg, bh); + + } else if (mod_data.transport_type == USB_PR_CBI) { + + /* Return status by interrupt pipe */ + store_status(fsg, bh); + } +} + +static void received_cbi_adsc(struct fsg_dev *fsg) +{ + struct usb_request *req = fsg->ep0req; + static u8 cbi_reset_cmnd[6] = { + SC_SEND_DIAGNOSTIC, 4, 0xff, 0xff, 0xff, 0xff}; + + /* Error in command transfer? */ + if (req->status || req->length != req->actual || + req->actual < 6 || req->actual > MAX_COMMAND_SIZE) { + +#ifdef CAN_STALL_CONTROL_OUT + usb_ep_set_halt(fsg->gadget->ep0); +#else + /* There isn't anything we can do! Wait for a reset... */ +#endif + return; + } + + /* Is it the special reset command? */ + if (req->actual >= sizeof cbi_reset_cmnd && + memcmp(req->buf, cbi_reset_cmnd, + sizeof cbi_reset_cmnd) == 0) { + + /* Raise an exception to stop the current operation + * and reinitialize our state. */ + DEBUG(fsg, "cbi reset request\n"); + raise_exception(fsg, FSG_STATE_RESET, reset_handler); + return; + } + + /* Don't accept this command if the previous one is + * still active. */ + if (fsg->state < FSG_STATE_STATUS_PHASE) { +#ifdef CAN_STALL_CONTROL_OUT + usb_ep_set_halt(fsg->gadget->ep0); +#else + /* There isn't anything we can do! Wait for a reset... */ +#endif + return; + } + + /* Save the command for later */ + fsg->cmnd_size = req->actual; + memcpy(fsg->cmnd, req->buf, fsg->cmnd_size); + fsg->data_dir = DATA_DIR_UNKNOWN; + fsg->state = FSG_STATE_COMMAND_PHASE; + + /* Is the previous status interrupt request still busy? The host + * is allowed to skip reading the status, so we must cancel it. */ + if (fsg->intreq_state == REQ_STATE_BUSY) { + cancel_transfer(fsg, fsg->intr_in, fsg->intreq, + &fsg->intreq_state); + if (exception_in_progress(fsg)) + return; + } + + /* If the status interrupt isn't idle yet, we must wait for it. + * Otherwise we can call the dispatcher to begin carrying out + * the command. */ + if (fsg->intreq_state != REQ_STATE_IDLE) + fsg->intr_buffhd->usb_done_routine = decode_scsi_cmnd; + else { + fsg->next_fill_routine = decode_scsi_cmnd; + dispatcher(fsg); + } +} + + +static void received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + struct usb_request *req = bh->outreq; + struct bulk_cb_wrap *cbw = (struct bulk_cb_wrap *) req->buf; + + /* Is the CBW valid? */ + if (req->status || req->actual != USB_BULK_CB_WRAP_LEN || + cbw->Signature != __constant_cpu_to_le32( + USB_BULK_CB_SIG) || + cbw->Length < 6 || cbw->Length > MAX_COMMAND_SIZE) { + DEBUG(fsg, "invalid CBW: status %d len %d sig %x cmdlen %d\n", + req->status, req->actual, + le32_to_cpu(cbw->Signature), cbw->Length); + usb_ep_set_halt(fsg->bulk_in); + usb_ep_set_halt(fsg->bulk_out); + return; + } + + /* Save the command for later */ + fsg->cmnd_size = cbw->Length; + memcpy(fsg->cmnd, cbw->CDB, fsg->cmnd_size); + if (cbw->Flags == USB_BULK_FLAG_OUT) + fsg->data_dir = DATA_DIR_FROM_HOST; + else if (cbw->Flags == USB_BULK_FLAG_IN) + fsg->data_dir = DATA_DIR_TO_HOST; + else + fsg->data_dir = DATA_DIR_UNKNOWN; + fsg->data_size = cbw->DataTransferLength; + fsg->bulk_only_tag = cbw->Tag; + + /* Is the CBW meaningful? */ + if (cbw->Lun != 0 || fsg->data_dir == DATA_DIR_UNKNOWN) { + DEBUG(fsg, "non-meaningful CBW: lun = %d, flags = %x\n", + cbw->Lun, cbw->Flags); + usb_ep_set_halt(fsg->bulk_in); + usb_ep_set_halt(fsg->bulk_out); + fsg->phase_error = 1; + return; + } + + if (fsg->data_size == 0) + fsg->data_dir = DATA_DIR_NONE; + + bh->state = BUF_STATE_EMPTY; // Done with this buffer + fsg->state = FSG_STATE_COMMAND_PHASE; + fsg->next_fill_routine = decode_scsi_cmnd; +} + +static void get_next_command(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + if (mod_data.transport_type == USB_PR_BULK) { + + /* Queue a request to read a Bulk-only CBW */ + bh->outreq->length = USB_BULK_CB_WRAP_LEN; + bh->state = BUF_STATE_BUSY; + bh->usb_done_routine = received_cbw; + start_transfer(fsg, fsg->bulk_out, bh->outreq, + &bh->outreq_state); + + /* We will drain the buffer in software, which means we + * can reuse it for the next filling. No need to advance + * next_buffhd_to_fill. */ + } +} + + +static int class_setup_req(struct fsg_dev *fsg, + const struct usb_ctrlrequest *ctrl) +{ + struct usb_request *req = fsg->ep0req; + int value = -EOPNOTSUPP; + + if (!fsg->config) + return value; + + /* Handle Bulk-Only class-specific requests */ + if (mod_data.transport_type == USB_PR_BULK) { + switch (ctrl->bRequest) { + + case USB_BULK_RESET_REQUEST: + if (ctrl->bRequestType != (USB_DIR_OUT | + USB_TYPE_CLASS | USB_RECIP_INTERFACE)) + break; + if (ctrl->wIndex != 0) { + value = -EDOM; + break; + } + + /* Raise an exception to stop the current operation + * and reinitialize our state. */ + DEBUG(fsg, "bulk reset request\n"); + raise_exception(fsg, FSG_STATE_RESET, reset_handler); + value = DELAYED_STATUS; + break; + + case USB_BULK_GET_MAX_LUN_REQUEST: + if (ctrl->bRequestType != (USB_DIR_IN | + USB_TYPE_CLASS | USB_RECIP_INTERFACE)) + break; + if (ctrl->wIndex != 0) { + value = -EDOM; + break; + } + *(u8 *) req->buf = 0; // Only 1 LUN + value = min(ctrl->wLength, (u16) 1); + req->context = NULL; + break; + } + } + + /* Handle CBI class-specific requests */ + else { + switch (ctrl->bRequest) { + + case USB_CBI_ADSC_REQUEST: + if (ctrl->bRequestType != (USB_DIR_OUT | + USB_TYPE_CLASS | USB_RECIP_INTERFACE)) + break; + if (ctrl->wIndex != 0) { + value = -EDOM; + break; + } + if (ctrl->wLength > MAX_COMMAND_SIZE) { + value = -EOVERFLOW; + break; + } + value = ctrl->wLength; + req->context = received_cbi_adsc; + break; + } + } + + if (value == -EOPNOTSUPP) + VDEBUG(fsg, + "unknown class-specific control req " + "%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + ctrl->wValue, ctrl->wIndex, ctrl->wLength); + return value; +} + + +/*-------------------------------------------------------------------------*/ + +static void control_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct fsg_dev *fsg = (struct fsg_dev *) ep->driver_data; + + if (req->status || req->actual != req->length) + DEBUG(fsg, "%s --> %d, %d/%d\n", __FUNCTION__, + req->status, req->actual, req->length); + if (req->status == -ECONNRESET) // Request was cancelled + usb_ep_fifo_flush(ep); + + /* Notify that the transfer is completed */ + if (acquire_lock(fsg, &fsg->ep0req_state)) { + if (req->context) + ((void (*)(struct fsg_dev *)) req->context)(fsg); + release_lock(fsg); + } +} + +static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct fsg_dev *fsg = (struct fsg_dev *) ep->driver_data; + struct fsg_buffhd *bh = (struct fsg_buffhd *) req->context; + + if (req->status || req->actual != req->length) + DEBUG(fsg, "%s --> %d, %d/%d\n", __FUNCTION__, + req->status, req->actual, req->length); + if (req->status == -ECONNRESET) // Request was cancelled + usb_ep_fifo_flush(ep); + + /* Invoke the usb_done_routine and notify the dispatcher that the + * transfer has completed. */ + if (acquire_lock(fsg, &bh->inreq_state)) { + bh->state = BUF_STATE_EMPTY; + if (bh->usb_done_routine) { + fsg_routine_t routine = bh->usb_done_routine; + + bh->usb_done_routine = NULL; + routine(fsg, bh); + } + dispatcher(fsg); + release_lock(fsg); + } +} + +static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct fsg_dev *fsg = (struct fsg_dev *) ep->driver_data; + struct fsg_buffhd *bh = (struct fsg_buffhd *) req->context; + + if (req->status || req->actual != req->length) + DEBUG(fsg, "%s --> %d, %d/%d\n", __FUNCTION__, + req->status, req->actual, req->length); + if (req->status == -ECONNRESET) // Request was cancelled + usb_ep_fifo_flush(ep); + + /* Invoke the usb_done_routine and notify the dispatcher that the + * transfer has completed. */ + if (acquire_lock(fsg, &bh->outreq_state)) { + bh->state = BUF_STATE_FULL; + if (bh->usb_done_routine) { + fsg_routine_t routine = bh->usb_done_routine; + + bh->usb_done_routine = NULL; + routine(fsg, bh); + } + dispatcher(fsg); + release_lock(fsg); + } +} + +static void intr_in_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct fsg_dev *fsg = (struct fsg_dev *) ep->driver_data; + struct fsg_buffhd *bh = (struct fsg_buffhd *) req->context; + + if (req->status || req->actual != req->length) + DEBUG(fsg, "%s --> %d, %d/%d\n", __FUNCTION__, + req->status, req->actual, req->length); + if (req->status == -ECONNRESET) // Request was cancelled + usb_ep_fifo_flush(ep); + + /* Invoke the usb_done_routine and notify the dispatcher that the + * transfer has completed. */ + if (acquire_lock(fsg, &fsg->intreq_state)) { + bh->state = BUF_STATE_EMPTY; + if (bh->usb_done_routine) { + fsg_routine_t routine = bh->usb_done_routine; + + bh->usb_done_routine = NULL; + routine(fsg, bh); + } + dispatcher(fsg); + release_lock(fsg); + } +} + + +/*-------------------------------------------------------------------------*/ + +static int enable_endpoint(struct fsg_dev *fsg, struct usb_ep *ep, + const struct usb_endpoint_descriptor *d) +{ + int rc; + + ep->driver_data = fsg; + rc = usb_ep_enable(ep, d); + if (rc) + ERROR(fsg, "can't enable %s, result %d\n", ep->name, rc); + return rc; +} + +static int alloc_request(struct fsg_dev *fsg, struct usb_ep *ep, + struct usb_request **preq) +{ + *preq = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (*preq) + return 0; + ERROR(fsg, "can't allocate request for %s\n", ep->name); + return -ENOMEM; +} + +/* + * Reset interface setting and re-init endpoint state (toggle etc). + * Call with altsetting < 0 to disable the interface. The only other + * available altsetting is 0, which enables the interface. + */ +static int do_set_interface(struct fsg_dev *fsg, int altsetting) +{ + int rc = 0; + int i; + const struct usb_endpoint_descriptor *d; + + if (altsetting < 0) { + DEBUG(fsg, "reset interface\n"); + +reset: + /* Deallocate the requests */ + for (i = 0; i < NUM_BUFFERS; ++i) { + struct fsg_buffhd *bh = &fsg->buffhds[i]; + + if (bh->inreq) { + usb_ep_free_request(fsg->bulk_in, bh->inreq); + bh->inreq = NULL; + } + if (bh->outreq) { + usb_ep_free_request(fsg->bulk_out, bh->outreq); + bh->outreq = NULL; + } + } + if (fsg->intreq) { + usb_ep_free_request(fsg->intr_in, fsg->intreq); + fsg->intreq = NULL; + } + + /* Disable the endpoints */ + if (fsg->bulk_in_enabled) { + usb_ep_disable(fsg->bulk_in); + fsg->bulk_in_enabled = 0; + } + if (fsg->bulk_out_enabled) { + usb_ep_disable(fsg->bulk_out); + fsg->bulk_out_enabled = 0; + } + if (fsg->intr_in_enabled) { + usb_ep_disable(fsg->intr_in); + fsg->intr_in_enabled = 0; + } + + return rc; + } + + DEBUG(fsg, "set interface %d\n", altsetting); + + /* Enable the endpoints */ + d = ep_desc(fsg->gadget, &fs_bulk_in_desc, &hs_bulk_in_desc); + if ((rc = enable_endpoint(fsg, fsg->bulk_in, d)) != 0) + goto reset; + fsg->bulk_in_enabled = 1; + + d = ep_desc(fsg->gadget, &fs_bulk_out_desc, &hs_bulk_out_desc); + if ((rc = enable_endpoint(fsg, fsg->bulk_out, d)) != 0) + goto reset; + fsg->bulk_out_enabled = 1; + + if (mod_data.use_intr) { + d = ep_desc(fsg->gadget, &fs_intr_in_desc, &hs_intr_in_desc); + if ((rc = enable_endpoint(fsg, fsg->intr_in, d)) != 0) + goto reset; + fsg->intr_in_enabled = 1; + } + + /* Allocate the requests */ + for (i = 0; i < NUM_BUFFERS; ++i) { + struct fsg_buffhd *bh = &fsg->buffhds[i]; + + if ((rc = alloc_request(fsg, fsg->bulk_in, &bh->inreq)) != 0) + goto reset; + if ((rc = alloc_request(fsg, fsg->bulk_out, &bh->outreq)) != 0) + goto reset; + bh->inreq->buf = bh->outreq->buf = bh->buf; + bh->inreq->dma = bh->outreq->dma = bh->dma; + bh->inreq->context = bh->outreq->context = bh; + bh->inreq->complete = bulk_in_complete; + bh->outreq->complete = bulk_out_complete; + } + if (mod_data.use_intr) { + if ((rc = alloc_request(fsg, fsg->intr_in, &fsg->intreq)) != 0) + goto reset; + fsg->intreq->complete = intr_in_complete; + } + + fsg->unit_attention_data = SS_RESET_OCCURRED; + return rc; +} + +static void set_interface_handler(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + struct usb_request *req = fsg->ep0req; + + if (do_set_interface(fsg, 0) < 0) // STALL on errors + usb_ep_set_halt(fsg->gadget->ep0); + else { + + /* Return the status ACK */ + req->length = 0; + req->context = NULL; + start_transfer(fsg, fsg->gadget->ep0, req, &fsg->ep0req_state); + } +} + + +/* + * Change our operational configuration. This code must agree with the code + * that returns config descriptors, and with interface altsetting code. + * + * It's also responsible for power management interactions. Some + * configurations might not work with our current power sources. + * For now we just assume the gadget is always self-powered. + */ +static int do_set_config(struct fsg_dev *fsg) +{ + int rc = 0; + + /* Disable the single interface */ + if (fsg->config != 0) { + DEBUG(fsg, "reset config\n"); + fsg->config = 0; + rc = do_set_interface(fsg, -1); + } + + /* Enable the interface */ + if (fsg->new_config != 0) { + fsg->config = fsg->new_config; + if ((rc = do_set_interface(fsg, 0)) < 0) { + fsg->config = 0; // Reset on errors + do_set_interface(fsg, -1); + } else { + char *speed; + + switch (fsg->gadget->speed) { + case USB_SPEED_LOW: speed = "low"; break; + case USB_SPEED_FULL: speed = "full"; break; + case USB_SPEED_HIGH: speed = "high"; break; + default: speed = "?"; break; + } + INFO(fsg, "%s speed config #%d\n", speed, fsg->config); + } + } + return rc; +} + +static void set_config_handler(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + struct usb_request *req = fsg->ep0req; + + if (do_set_config(fsg) < 0) // STALL on errors + usb_ep_set_halt(fsg->gadget->ep0); + else { + + /* Return the status ACK */ + req->length = 0; + req->context = NULL; + start_transfer(fsg, fsg->gadget->ep0, req, &fsg->ep0req_state); + } +} + + +/*-------------------------------------------------------------------------*/ + +static int standard_setup_req(struct fsg_dev *fsg, + const struct usb_ctrlrequest *ctrl) +{ + struct usb_request *req = fsg->ep0req; + int value = -EOPNOTSUPP; + + /* Usually this just stores reply data in the pre-allocated ep0 buffer, + * but config change events will also reconfigure hardware. */ + switch (ctrl->bRequest) { + + case USB_REQ_GET_DESCRIPTOR: + if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | + USB_RECIP_DEVICE)) + break; + switch (ctrl->wValue >> 8) { + + case USB_DT_DEVICE: + value = min(ctrl->wLength, (u16) sizeof device_desc); + memcpy(req->buf, &device_desc, value); + break; +#ifdef HIGHSPEED + case USB_DT_DEVICE_QUALIFIER: + value = min(ctrl->wLength, (u16) sizeof dev_qualifier); + memcpy(req->buf, &dev_qualifier, value); + break; + + case USB_DT_OTHER_SPEED_CONFIG: + // FALLTHROUGH +#endif /* HIGHSPEED */ + case USB_DT_CONFIG: + value = populate_config_buf(fsg->gadget->speed, + req->buf, + ctrl->wValue >> 8, + ctrl->wValue & 0xff); + if (value >= 0) + value = min(ctrl->wLength, (u16) value); + break; + + case USB_DT_STRING: + /* wIndex == language code */ + value = usb_gadget_get_string(&stringtab, + ctrl->wValue & 0xff, req->buf); + if (value >= 0) + value = min(ctrl->wLength, (u16) value); + break; + } + break; + + /* One config, two speeds */ + case USB_REQ_SET_CONFIGURATION: + if (ctrl->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD | + USB_RECIP_DEVICE)) + break; + if (ctrl->wValue == CONFIG_VALUE || ctrl->wValue == 0) { + fsg->new_config = ctrl->wValue; + + /* Raise an exception to wipe out previous transaction + * state (queued bufs, etc) and set the new config. */ + raise_exception(fsg, FSG_STATE_CONFIG_CHANGE, + set_config_handler); + value = DELAYED_STATUS; + } + break; + case USB_REQ_GET_CONFIGURATION: + if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | + USB_RECIP_DEVICE)) + break; + *(u8 *) req->buf = fsg->config; + value = min(ctrl->wLength, (u16) 1); + break; + + case USB_REQ_SET_INTERFACE: + if (ctrl->bRequestType != (USB_DIR_OUT| USB_TYPE_STANDARD | + USB_RECIP_INTERFACE)) + break; + if (fsg->config && ctrl->wIndex == 0) { + + /* Raise an exception to wipe out previous transaction + * state (queued bufs, etc) and install the new + * interface altsetting. */ + raise_exception(fsg, FSG_STATE_INTERFACE_CHANGE, + set_interface_handler); + value = DELAYED_STATUS; + } + break; + case USB_REQ_GET_INTERFACE: + if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | + USB_RECIP_INTERFACE)) + break; + if (!fsg->config) + break; + if (ctrl->wIndex != 0) { + value = -EDOM; + break; + } + *(u8 *) req->buf = 0; + value = min(ctrl->wLength, (u16) 1); + break; + + default: + VDEBUG(fsg, + "unknown control req %02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + ctrl->wValue, ctrl->wIndex, ctrl->wLength); + } + + req->context = NULL; + return value; +} + + +static int fsg_setup(struct usb_gadget *gadget, + const struct usb_ctrlrequest *ctrl) +{ + struct fsg_dev *fsg = get_gadget_data(gadget); + int rc; + + /* If an exception is in progress, we can't respond */ + if (!acquire_lock(fsg, NULL)) + return -EBUSY; + + /* If a previous reply is in progress, we can't respond */ + if (unlikely(fsg->ep0req_state)) { + if (fsg->ep0req_state == REQ_STATE_BUSY) + cancel_transfer(fsg, gadget->ep0, fsg->ep0req, + &fsg->ep0req_state); + rc = -EBUSY; + goto out; + } + + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) + rc = class_setup_req(fsg, ctrl); + else + rc = standard_setup_req(fsg, ctrl); + + /* Respond with data/status or defer until later? */ + if (rc >= 0 && rc != DELAYED_STATUS) { + fsg->ep0req->length = rc; + rc = start_transfer(fsg, gadget->ep0, fsg->ep0req, + &fsg->ep0req_state); + } + + /* Device either stalls (rc < 0) or reports success */ +out: + release_lock(fsg); + return rc; +} + + +/*-------------------------------------------------------------------------*/ + +static void disconnect_handler(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + /* On disconnect, go to the unconfigured state */ + fsg->new_config = 0; + do_set_config(fsg); +} + +static void fsg_disconnect(struct usb_gadget *gadget) +{ + struct fsg_dev *fsg = get_gadget_data(gadget); + + lock_and_raise_exception(fsg, FSG_STATE_DISCONNECT, + disconnect_handler); +} + + +static void unbind_handler(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + complete(fsg->unbind_notifier); +} + +static void fsg_unbind(struct usb_gadget *gadget) +{ + struct fsg_dev *fsg = get_gadget_data(gadget); + int i; + struct usb_request *req = fsg->ep0req; + DECLARE_COMPLETION(unbind_notifier); + + DEBUG(fsg, "unbind\n"); + + /* Raise the unbind exception */ + local_irq_disable(); + fsg->unbind_notifier = &unbind_notifier; + lock_and_raise_exception(fsg, FSG_STATE_UNBIND, unbind_handler); + local_irq_enable(); + + wait_for_completion(&unbind_notifier); // Wait for things to quiesce + fsg->registered = 0; + + /* Free the data buffers */ + for (i = 0; i < NUM_BUFFERS; ++i) { + struct fsg_buffhd *bh = &fsg->buffhds[i]; + + if (bh->buf) + usb_ep_free_buffer(fsg->bulk_in, bh->buf, bh->dma, + mod_data.buflen); + } + + /* Free the request and buffer for endpoint 0 */ + if (req) { + if (req->buf) + usb_ep_free_buffer(gadget->ep0, req->buf, + req->dma, EP0_BUFSIZE); + usb_ep_free_request(gadget->ep0, req); + } + + set_gadget_data(gadget, 0); +} + + +static int fsg_bind(struct usb_gadget *gadget) +{ + struct fsg_dev *fsg = the_fsg; + int rc; + int i; + struct usb_ep *ep; + struct usb_request *req; + + fsg->gadget = gadget; + set_gadget_data(gadget, fsg); + gadget->ep0->driver_data = fsg; + + /* Fix up the descriptors */ + i = (mod_data.use_intr ? 3 : 2); // Number of endpoints + config_desc.wTotalLength = USB_DT_CONFIG_SIZE + USB_DT_INTERFACE_SIZE + + USB_DT_ENDPOINT_SIZE * i; + intf_desc.bNumEndpoints = i; + intf_desc.bInterfaceProtocol = mod_data.transport_type; + + /* Find all the endpoints we will use */ + gadget_for_each_ep(ep, gadget) { + if (strcmp(ep->name, EP_BULK_IN_NAME) == 0) + fsg->bulk_in = ep; + else if (strcmp(ep->name, EP_BULK_OUT_NAME) == 0) + fsg->bulk_out = ep; + else if (strcmp(ep->name, EP_INTR_IN_NAME) == 0) + fsg->intr_in = ep; + } + if (!fsg->bulk_in || !fsg->bulk_out || + (mod_data.use_intr && !fsg->intr_in)) { + DEBUG(fsg, "unable to find all endpoints\n"); + rc = -ENOTSUPP; + goto out; + } + + rc = -ENOMEM; + + /* Allocate the request and buffer for endpoint 0 */ + fsg->ep0req = req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); + if (!req) + goto out; + req->buf = usb_ep_alloc_buffer(gadget->ep0, EP0_BUFSIZE, + &req->dma, GFP_KERNEL); + if (!req->buf) + goto out; + req->complete = control_complete; + + /* Allocate the data buffers */ + for (i = 0; i < NUM_BUFFERS; ++i) { + struct fsg_buffhd *bh = &fsg->buffhds[i]; + + bh->buf = usb_ep_alloc_buffer(fsg->bulk_in, mod_data.buflen, + &bh->dma, GFP_KERNEL); + if (!bh->buf) + goto out; + bh->next = bh + 1; + } + fsg->buffhds[NUM_BUFFERS - 1].next = &fsg->buffhds[0]; + + /* This should reflect the actual gadget power source */ + usb_gadget_set_selfpowered(gadget); + + /* On a real device, serial[] would be loaded from permanent + * storage. We just encode it from the driver version string. */ + for (i = 0; i < sizeof(serial) - 2; i += 2) { + unsigned char c = DRIVER_VERSION[i / 2]; + + if (!c) + break; + sprintf(&serial[i], "%02X", c); + } + + INFO(fsg, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); + DEBUG(fsg, "backing file: %s\n", mod_data.filename); + DEBUG(fsg, "transport=%s, buflen=%u, ro=%d\n", + mod_data.transport_name, mod_data.buflen, + mod_data.ro); + DEBUG(fsg, "I/O thread pid: %d\n", fsg->thread_pid); + fsg->registered = 1; + return 0; + +out: + fsg_unbind(gadget); + return rc; +} + + +/*-------------------------------------------------------------------------*/ + +static int __init check_parameters(void) +{ + if (!file) { + MINFO("no file parameter given\n"); + return -EINVAL; + } + mod_data.filename = file; + + if (strnicmp(transport, "CB", 10) == 0) { + mod_data.transport_type = USB_PR_CB; + mod_data.transport_name = "Control-Bulk"; + } else if (strnicmp(transport, "CBI", 10) == 0) { + mod_data.transport_type = USB_PR_CBI; + mod_data.transport_name = "Control-Bulk-Interrupt"; + mod_data.use_intr = 1; + } else if (strnicmp(transport, "BBB", 10) == 0) { + mod_data.transport_type = USB_PR_BULK; + mod_data.transport_name = "Bulk-only"; + } else { + MINFO("invalid transport\n"); + return -EINVAL; + } + + buflen &= PAGE_CACHE_MASK; + if (buflen <= 0) { + MINFO("invalid buflen\n"); + return -ETOOSMALL; + } + mod_data.buflen = buflen; + return 0; +} + + +static int __init open_backing_file(void) +{ + struct file *filp = NULL; + int rc = -EINVAL; + struct inode *inode = NULL; + loff_t size; + loff_t num_sectors; + + /* R/W if we can, R/O if we must */ + if (!ro) { + filp = filp_open(mod_data.filename, O_RDWR | O_LARGEFILE, 0); + if (-EROFS == PTR_ERR(filp)) + ro = 1; + } + if (ro) + filp = filp_open(mod_data.filename, O_RDONLY | O_LARGEFILE, 0); + if (IS_ERR(filp)) { + MINFO("unable to open backing file\n"); + return PTR_ERR(filp); + } + + if (!(filp->f_mode & FMODE_WRITE)) + ro = 1; + + if (filp->f_dentry) + inode = filp->f_dentry->d_inode; + if (inode && S_ISBLK(inode->i_mode)) { + if (bdev_read_only(inode->i_bdev)) + ro = 1; + } else if (!inode || !S_ISREG(inode->i_mode)) { + MINFO("invalid file type\n"); + goto out; + } + + /* If we can't read the file, it's no good. + * If we can't write the file, use it read-only. */ + if (!filp->f_op || !(filp->f_op->read || filp->f_op->aio_read)) { + MINFO("file not readable\n"); + goto out; + } + if (!(filp->f_op->write || filp->f_op->aio_write)) + ro = 1; + + size = i_size_read(inode->i_mapping->host); + if (size < 0) { + MINFO("unable to find file size\n"); + rc = (int) size; + goto out; + } + num_sectors = size >> 9; // File size in 512-byte sectors + if (num_sectors == 0) { + MINFO("file too small\n"); + rc = -ETOOSMALL; + goto out; + } + + get_file(filp); + mod_data.filp = filp; + mod_data.ro = ro; + mod_data.file_length = size; + mod_data.num_sectors = num_sectors; + rc = 0; + +out: + filp_close(filp, current->files); + return rc; +} + + +static void close_backing_file(void) +{ + if (mod_data.filp) + fput(mod_data.filp); + mod_data.filp = NULL; +} + + +static int __init fsg_alloc(void) +{ + struct fsg_dev *fsg; + + fsg = kmalloc(sizeof *fsg, GFP_KERNEL); + if (!fsg) + return -ENOMEM; + memset(fsg, 0, sizeof *fsg); + spin_lock_init(&fsg->lock); + init_waitqueue_head(&fsg->thread_wqh); + init_completion(&fsg->thread_notifier); + + the_fsg = fsg; + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +static struct usb_gadget_driver fsg_driver = { +#ifdef HIGHSPEED + .speed = USB_SPEED_HIGH, +#else + .speed = USB_SPEED_FULL, +#endif + .function = (char *) longname, + .bind = fsg_bind, + .unbind = fsg_unbind, + .disconnect = fsg_disconnect, + .setup = fsg_setup, + + .driver = { + .name = (char *) shortname, + // .release = ... + // .suspend = ... + // .resume = ... + }, +}; + + +static int __init init(void) +{ + int rc; + struct fsg_dev *fsg = NULL; + + if ((rc = check_parameters()) != 0) + goto out; + if ((rc = open_backing_file()) != 0) + goto out; + if ((rc = fsg_alloc()) != 0) + goto out; + fsg = the_fsg; + if ((rc = kernel_thread(fsg_io_thread, fsg, CLONE_KERNEL)) < 0) + goto out; + fsg->thread_pid = rc; + + rc = usb_gadget_register_driver(&fsg_driver); + if (rc == 0) { + set_bit(CAN_UNREGISTER, &fsg->atomic_bitflags); + complete(&fsg->thread_notifier); + return 0; + } + + /* Wait for the thread to terminate */ + complete(&fsg->thread_notifier); + wait_for_completion(&fsg->thread_notifier); + +out: + if (fsg) + kfree(fsg); + close_backing_file(); + return rc; +} +module_init(init); + + +static void __exit cleanup(void) +{ + struct fsg_dev *fsg = the_fsg; + + if (test_and_clear_bit(CAN_UNREGISTER, &fsg->atomic_bitflags)) + usb_gadget_unregister_driver(&fsg_driver); + + /* It's not safe to stop the thread until after the gadget has + * been unregistered. */ + stop_io_thread(fsg); + wait_for_completion(&fsg->thread_notifier); + + kfree(fsg); + close_backing_file(); +} +module_exit(cleanup); ------------------------------------------------------- This sf.net email is sponsored by:ThinkGeek Welcome to geek heaven. http://thinkgeek.com/sf _______________________________________________ [EMAIL PROTECTED] To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel