This patch provides also a per-LUN configuration of removable, CD-ROM and read-only flags so that gadget may provide several LUNs each with different configuration. It adds more configuration options to configuration as one may turn off various features (removable and CD-ROM support, turn read only always on) which may come in handy for small systems. The "can_stall" flag is also configurable and turned off by default as it seemd to break the mass storage functions while it was tested. Signed-off-by: Michal Nazarewicz <m.nazarewicz@xxxxxxxxxxx> --- drivers/usb/gadget/f_file_storage.c | 3824 ++++++++++++++++++++++++++++ drivers/usb/gadget/f_file_storage.h | 356 +++ drivers/usb/gadget/f_file_storage_params.c | 182 ++ drivers/usb/gadget/f_file_storage_params.h | 102 + 4 files changed, 4464 insertions(+), 0 deletions(-) create mode 100644 drivers/usb/gadget/f_file_storage.c create mode 100644 drivers/usb/gadget/f_file_storage.h create mode 100644 drivers/usb/gadget/f_file_storage_params.c create mode 100644 drivers/usb/gadget/f_file_storage_params.h diff --git a/drivers/usb/gadget/f_file_storage.c b/drivers/usb/gadget/f_file_storage.c new file mode 100644 index 0000000..ab5ace7 --- /dev/null +++ b/drivers/usb/gadget/f_file_storage.c @@ -0,0 +1,3824 @@ +/* + * f_file_storage.c -- File-backed USB Storage Function, for USB development + * + * Copyright (C) 2003-2008 Alan Stern + * All rights reserved. + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood <lockwood@xxxxxxxxxxx> + * + * Copyright (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz <m.nazarewicz@xxxxxxxxxxx> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* See comments in f_file_storage.h file. */ + + +/* #define VERBOSE_DEBUG */ +/* #define DUMP_MSGS */ + + +#include <linux/blkdev.h> +#include <linux/completion.h> +#include <linux/dcache.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/fcntl.h> +#include <linux/file.h> +#include <linux/fs.h> +#include <linux/kref.h> +#include <linux/kthread.h> +#include <linux/limits.h> +#include <linux/rwsem.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/freezer.h> +#include <linux/utsname.h> + +#include <linux/usb.h> +#include <linux/usb_usual.h> +#include <linux/usb/ch9.h> +#include <linux/usb/composite.h> +#include <linux/usb/gadget.h> + +#include "f_file_storage.h" +#include "gadget_chips.h" + + +/*-------------------------------------------------------------------------*/ + +#define DRIVER_NAME "f_file_storage" +#define DRIVER_DESC "File Storage Function" +#define DRIVER_VERSION "09/07/2009" + +/* + * This driver assumes self-powered hardware and has no way for users to + * trigger remote wakeup. It uses autoconfiguration to select endpoints + * and endpoint addresses. + */ + +/*-------------------------------------------------------------------------*/ + + +#ifndef DEBUG +# undef VERBOSE_DEBUG +# undef DUMP_MSGS +#endif /* DEBUG */ + +#define LERROR(l, fmt, args...) dev_err (&(l)->dev, fmt, ## args) +#define LWARN(l, fmt, args...) dev_warn(&(l)->dev, fmt, ## args) +#define LINFO(l, fmt, args...) dev_info(&(l)->dev, fmt, ## args) +#define VLDBG(l, fmt, args...) dev_vdbg(&(l)->dev, fmt, ## args) +#define LDBG(l, fmt, args...) dev_dbg (&(l)->dev, fmt, ## args) + +#define FERROR(d, fmt, args...) dev_err (&(d)->cdev->gadget->dev, fmt, ## args) +#define FWARN(d, fmt, args...) dev_warn(&(d)->cdev->gadget->dev, fmt, ## args) +#define FINFO(d, fmt, args...) dev_info(&(d)->cdev->gadget->dev, fmt, ## args) +#define FDBG(d, fmt, args...) dev_dbg (&(d)->cdev->gadget->dev, fmt, ## args) +#define VFDBG(d, fmt, args...) dev_vdbg(&(d)->cdev->gadget->dev, fmt, ## args) + + +/*-------------------------------------------------------------------------*/ +#ifdef CONFIG_USB_FSF_TEST + +static unsigned int _fsf_buflen = 16384; +/* Cast so that (i) _fsf_buflen can be unsigned int and work nice with + module_param_named() below and (ii) fsf_buflen is not an lvalue + (you cannot assign to it). */ +#define fsf_buflen ((u32)_fsf_buflen) + +module_param_named(buflen, _fsf_buflen, uint, S_IRUGO); +MODULE_PARM_DESC(buflen, "I/O buffer size"); + +#else + +#define fsf_buflen ((u32)16384) + +#endif + + +#ifdef CONFIG_USB_FSF_CAN_STALL_SUPPORT + +static int fsf_can_stall = 1; + +module_param_named(stall, fsf_can_stall, bool, S_IRUGO); +MODULE_PARM_DESC(stall, "false to prevent bulk stalls"); + +#endif + + +/*-------------------------------------------------------------------------*/ + +/* Bulk-only data structures */ + +/* Command Block Wrapper */ +struct bulk_cb_wrap { + __le32 Signature; /* Contains 'USBC' */ + u32 Tag; /* Unique per command id */ + __le32 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_IN_FLAG 0x80 + +/* Command Status Wrapper */ +struct bulk_cs_wrap { + __le32 Signature; /* Should = 'USBS' */ + u32 Tag; /* Same as original command */ + __le32 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 + +/* Length of a SCSI Command Data Block */ +#define MAX_COMMAND_SIZE 16 + +/* SCSI commands that we recognize */ +#define SC_FORMAT_UNIT 0x04 +#define SC_INQUIRY 0x12 +#define SC_MODE_SELECT_6 0x15 +#define SC_MODE_SELECT_10 0x55 +#define SC_MODE_SENSE_6 0x1a +#define SC_MODE_SENSE_10 0x5a +#ifndef CONFIG_USB_FSF_REMOVABLE_OFF +#define SC_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e +#endif +#define SC_READ_6 0x08 +#define SC_READ_10 0x28 +#define SC_READ_12 0xa8 +#define SC_READ_CAPACITY 0x25 +#define SC_READ_FORMAT_CAPACITIES 0x23 +#ifndef CONFIG_USB_FSF_CDROM_OFF +#define SC_READ_HEADER 0x44 +#define SC_READ_TOC 0x43 +#endif +#define SC_RELEASE 0x17 +#define SC_REQUEST_SENSE 0x03 +#define SC_RESERVE 0x16 +#define SC_SEND_DIAGNOSTIC 0x1d +#ifndef CONFIG_USB_FSF_REMOVABLE_OFF +#define SC_START_STOP_UNIT 0x1b +#endif +#define SC_SYNCHRONIZE_CACHE 0x35 +#define SC_TEST_UNIT_READY 0x00 +#define SC_VERIFY 0x2f +#define SC_WRITE_6 0x0a +#define SC_WRITE_10 0x2a +#define SC_WRITE_12 0xaa + +/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */ +#define SS_NO_SENSE 0 +#define SS_COMMUNICATION_FAILURE 0x040800 +#define SS_INVALID_COMMAND 0x052000 +#define SS_INVALID_FIELD_IN_CDB 0x052400 +#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100 +#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500 +#define SS_MEDIUM_NOT_PRESENT 0x023a00 +#define SS_MEDIUM_REMOVAL_PREVENTED 0x055302 +#define SS_NOT_READY_TO_READY_TRANSITION 0x062800 +#define SS_RESET_OCCURRED 0x062900 +#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900 +#define SS_UNRECOVERED_READ_ERROR 0x031100 +#define SS_WRITE_ERROR 0x030c02 +#define SS_WRITE_PROTECTED 0x072700 + +#define SK(x) ((u8) ((x) >> 16)) /* Sense Key byte, etc. */ +#define ASC(x) ((u8) ((x) >> 8)) +#define ASCQ(x) ((u8) (x)) + + +/*-------------------------------------------------------------------------*/ + + +struct fsf_lun { + struct file *filp; + loff_t file_length; + loff_t num_sectors; + +#ifndef CONFIG_USB_FSF_READONLY_ON + unsigned int readonly:1; +#endif +#ifdef CONFIG_USB_FSF_REMOVABLE_CONFIG + unsigned int removable:1; +#endif +#ifndef CONFIG_USB_FSF_REMOVABLE_OFF + unsigned int prevent_medium_removal:1; +#endif +#ifdef CONFIG_USB_FSF_CDROM_CONFIG + unsigned int cdrom:1; +#endif + unsigned int registered:1; + unsigned int info_valid:1; + + u32 sense_data; + u32 sense_data_info; + u32 unit_attention_data; + + struct device dev; +}; + + +static inline int fsf_lun_is_readonly(struct fsf_lun *lun) +{ +#ifndef CONFIG_USB_FSF_READONLY_ON + return lun && lun->readonly; +#else + return 1; +#endif +} + +static inline int fsf_lun_is_removable(struct fsf_lun *lun) +{ +#if defined CONFIG_USB_FSF_REMOVABLE_CONFIG + return lun && lun->removable; +#elif defined CONDIF_USB_FSF_REMOVABLE_CONFIG_ON + return 1; +#else + return 0; +#endif +} + +static inline int fsf_lun_is_cdrom(struct fsf_lun *lun) +{ +#if defined CONFIG_USB_FSF_CDROM_CONFIG + return lun && lun->cdrom; +#elif defined CONDIF_USB_FSF_CDROM_CONFIG_ON + return 1; +#else + return 0; +#endif +} + +static inline int fsf_lun_is_file_open(struct fsf_lun *lun) +{ + return lun->filp != NULL; +} + +static inline struct fsf_lun *fsf_lun_from_dev(struct device *dev) +{ + return container_of(dev, struct fsf_lun, dev); +} + + +/* 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 fsf_buffer_state { + BUF_STATE_EMPTY = 0, + BUF_STATE_FULL, + BUF_STATE_BUSY +}; + +struct fsf_buffhd { + void *buf; + enum fsf_buffer_state state; + struct fsf_buffhd *next; + + /* The NetChip 2280 is faster, and handles some protocol faults + * better, if we don't submit any short bulk-out read requests. + * So we will record the intended request length here. */ + unsigned int bulk_out_intended_length; + + struct usb_request *inreq; + int inreq_busy; + struct usb_request *outreq; + int outreq_busy; +}; + +enum fsf_state { + /* This one isn't used anywhere */ + FSF_STATE_COMMAND_PHASE = -10, + FSF_STATE_DATA_PHASE, + FSF_STATE_STATUS_PHASE, + + FSF_STATE_IDLE = 0, + FSF_STATE_ABORT_BULK_OUT, + FSF_STATE_RESET, + FSF_STATE_CONFIG_CHANGE, + FSF_STATE_EXIT, + FSF_STATE_TERMINATED +}; + +enum data_direction { + DATA_DIR_UNKNOWN = 0, + DATA_DIR_FROM_HOST, + DATA_DIR_TO_HOST, + DATA_DIR_NONE +}; + + +struct fsf_common { + /* filesem protects: backing files in use */ + struct rw_semaphore filesem; + + unsigned int lun; + unsigned int nluns; + + struct fsf_lun *luns; + struct fsf_lun *curlun; + +#define FOREACH_LUN(i, lun, common) \ + for (i = (common)->nluns, lun = (common)->luns; i; --i, ++lun) + + struct fsf_buffhd *next_buffhd_to_fill; + struct fsf_buffhd *next_buffhd_to_drain; + struct fsf_buffhd buffhds[NUM_BUFFERS]; + +#define FOREACH_BUFFHD(i, bh, common) \ + for (i = NUM_BUFFERS, bh = (common)->buffhds; i; --i, ++bh) + + const char *vendor; + const char *product; + u16 release; + + struct kref ref; +}; + + +struct fsf { + struct usb_function function; + struct usb_composite_dev *cdev; + + struct fsf_common *common; + + /* lock protects: state, all the req_busy's, and cbbuf_cmnd */ + spinlock_t lock; + + unsigned int ep0_req_tag; + +#ifdef CONFIG_USB_FSF_TEST + struct usb_request *intreq; /* For interrupt responses */ + int intreq_busy; + struct fsf_buffhd *intr_buffhd; +#endif + + unsigned int bulk_out_maxpacket; + enum fsf_state state; /* For exception handling */ + unsigned int exception_req_tag; + + u8 config, new_config; + + unsigned int running:1; + unsigned int bulk_in_enabled:1; + unsigned int bulk_out_enabled:1; +#ifdef CONFIG_USB_FSF_TEST + unsigned int intr_in_enabled:1; +#endif + unsigned int phase_error:1; + unsigned int short_packet_received:1; + unsigned int bad_lun_okay:1; + +#ifdef CONFIG_USB_FSF_CAN_STALL_SUPPORT + unsigned int can_stall:1; +#endif + + unsigned long atomic_bitflags; +#define REGISTERED 0 +#define IGNORE_BULK_OUT 1 + + struct usb_ep *bulk_in; + struct usb_ep *bulk_out; +#ifdef CONFIG_USB_FSF_TEST + struct usb_ep *intr_in; +#endif + + int thread_wakeup_needed; + struct completion thread_notifier; + struct task_struct *thread_task; + + int cmnd_size; + u8 cmnd[MAX_COMMAND_SIZE]; + enum data_direction data_dir; + u32 data_size; + u32 data_size_from_cmnd; + u32 tag; + u32 residue; + u32 usb_amount_left; + +#ifdef CONFIG_USB_FSF_TEST + /* The CB protocol offers no way for a host to know when a command + * has completed. As a result the next command may arrive early, + * and we will still have to handle it. For that reason we need + * a buffer to store new commands when using CB (or CBI, which + * does not oblige a host to wait for command completion either). */ + int cbbuf_cmnd_size; + u8 cbbuf_cmnd[MAX_COMMAND_SIZE]; +#endif + +#ifdef CONFIG_USB_FSF_TEST + const char *transport_name; + const char *protocol_name; + + int transport_type; + int protocol_type; +#endif +}; + +static inline struct fsf *fsf_from_func(struct usb_function *func) +{ + return container_of(func, struct fsf, function); +} + +static inline struct usb_ep *ep0(struct fsf *fsf) +{ + return fsf->cdev->gadget->ep0; +} + + +/* + * These definitions will permit the compiler to avoid generating code for + * parts of the driver that aren't used in the non-TEST version. Even gcc + * can recognize when a test of a constant expression yields a dead code + * path. + */ + +#ifdef CONFIG_USB_FSF_TEST + +# define fsf_transport_type(fsf) ((fsf)->transport_type) +# define fsf_protocol_type(fsf) ((fsf)->protocol_type) +# define fsf_transport_name(fsf) ((fsf)->transport_name) +# define fsf_protocol_name(fsf) ((fsf)->protocol_name) + +#else + +# define fsf_transport_type(fsf) US_PR_BULK +# define fsf_protocol_type(fsf) US_SC_SCSI +# define fsf_transport_name(fsf) "Bulk-only" +# define fsf_protocol_name(fsf) "Transparent SCSI" + +#endif /* CONFIG_USB_FSF_TEST */ + +#define transport_is_bbb(fsf) likely(fsf_transport_type(fsf) == US_PR_BULK) +#define transport_is_cbi(fsf) unlikely(fsf_transport_type(fsf) == US_PR_CBI) +#define protocol_is_scsi(fsf) likely(fsf_protocol_type(fsf) == US_SC_SCSI) + + +#ifdef CONFIG_USB_FSF_CAN_STALL_SUPPORT +# define fsf_can_stall(fsf) ((fsf)->can_stall) +#else +# define fsf_can_stall(fsf) 0 +#endif + + +static inline int fsf_exception_in_progress(struct fsf *fsf) +{ + return (fsf->state > FSF_STATE_IDLE); +} + +/* Make bulk-out requests be divisible by the maxpacket size */ +static void set_bulk_out_req_length(struct fsf *fsf, + struct fsf_buffhd *bh, unsigned int length) +{ + unsigned int rem; + + bh->bulk_out_intended_length = length; + rem = length % fsf->bulk_out_maxpacket; + if (rem > 0) + length += fsf->bulk_out_maxpacket - rem; + bh->outreq->length = length; +} + +static void fsf_lun_close_file(struct fsf_lun *lun); + + +/*-------------------------------------------------------------------------*/ + +#ifdef DUMP_MSGS + +static void dump_msg(struct fsf *fsf, const char *label, + const u8 *buf, unsigned int length) +{ + if (length < 512) { + FDBG(fsf, "%s, length %u:\n", label, length); + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, + 16, 1, buf, length, 0); + } +} + +# define dump_cdb(fsf) do { } while (0) + +#else + +# define dump_msg(fsf, label, buf, length) do { } while (0) + +# ifdef VERBOSE_DEBUG + +static void dump_cdb(struct fsf *fsf) +{ + print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE, + 16, 1, fsf->cmnd, fsf->cmnd_size, 0); +} + +# else + +# define dump_cdb(fsf) do { } while (0) + +# endif /* VERBOSE_DEBUG */ +#endif /* DUMP_MSGS */ + + +static int fsf_set_halt(struct fsf *fsf, struct usb_ep *ep) +{ + const char *name; + + if (ep == fsf->bulk_in) + name = "bulk-in"; + else if (ep == fsf->bulk_out) + name = "bulk-out"; + else + name = ep->name; + FDBG(fsf, "%s set halt\n", name); + return usb_ep_set_halt(ep); +} + + +/*-------------------------------------------------------------------------*/ + +/* Routines for unaligned data access */ + +static inline u16 get_be16(u8 *buf) +{ + return ((u16) buf[0] << 8) | ((u16) buf[1]); +} + +static inline u32 get_be32(u8 *buf) +{ + return ((u32) buf[0] << 24) | ((u32) buf[1] << 16) | + ((u32) buf[2] << 8) | ((u32) buf[3]); +} + +static inline void put_be16(u8 *buf, u16 val) +{ + buf[0] = val >> 8; + buf[1] = val; +} + +static inline void put_be32(u8 *buf, u32 val) +{ + buf[0] = val >> 24; + buf[1] = val >> 16; + buf[2] = val >> 8; + buf[3] = val & 0xff; +} + + +/*-------------------------------------------------------------------------*/ + +/* + * DESCRIPTORS ... most are static, but strings and (full) configuration + * descriptors are built on demand. Also the (static) config and interface + * descriptors are adjusted during fsf_bind(). + */ +#define STRING_INTERFACE_IDX 0 + + +/* There is only one interface. */ +static struct usb_interface_descriptor fsf_intf_desc = { + .bLength = sizeof fsf_intf_desc, + .bDescriptorType = USB_DT_INTERFACE, + + .bNumEndpoints = 2, /* Adjusted during fsf_bind() */ + .bInterfaceClass = USB_CLASS_MASS_STORAGE, + .bInterfaceSubClass = US_SC_SCSI, /* Adjusted during fsf_bind() */ + .bInterfaceProtocol = US_PR_BULK, /* Adjusted during fsf_bind() */ + /* .iInterface = DYNAMIC, */ +}; + +/* Three full-speed endpoint descriptors: bulk-in, bulk-out, + * and interrupt-in. */ + +static struct usb_endpoint_descriptor fsf_fs_bulk_in_desc = { + .bLength = sizeof fsf_fs_bulk_in_desc, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + /* wMaxPacketSize set by autoconfiguration */ +}; + +static struct usb_endpoint_descriptor fsf_fs_bulk_out_desc = { + .bLength = sizeof fsf_fs_bulk_out_desc, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + /* wMaxPacketSize set by autoconfiguration */ +}; + +#ifdef CONFIG_USB_FSF_TEST +static struct usb_endpoint_descriptor fsf_fs_intr_in_desc = { + .bLength = sizeof fsf_fs_intr_in_desc, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(2), + .bInterval = 32, /* frames -> 32 ms */ +}; +#endif + +static struct usb_descriptor_header *fsf_fs_function[] = { + (struct usb_descriptor_header *) &fsf_intf_desc, + (struct usb_descriptor_header *) &fsf_fs_bulk_in_desc, + (struct usb_descriptor_header *) &fsf_fs_bulk_out_desc, +#ifdef CONFIG_USB_FSF_TEST + (struct usb_descriptor_header *) &fsf_fs_intr_in_desc, +#endif + NULL, +}; +#define FSF_FS_FUNCTION_PRE_EP_ENTRIES 1 + + +/* + * 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) plus + * more construction options for the config descriptor. + */ +static struct usb_endpoint_descriptor fsf_hs_bulk_in_desc = { + .bLength = sizeof fsf_hs_bulk_in_desc, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fsf_fs_bulk_in_desc during + * fsf_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor fsf_hs_bulk_out_desc = { + .bLength = sizeof fsf_hs_bulk_out_desc, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fsf_fs_bulk_out_desc during + * fsf_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), + .bInterval = 1, /* NAK every 1 uframe */ +}; + +#ifdef CONFIG_USB_FSF_TEST +static struct usb_endpoint_descriptor fsf_hs_intr_in_desc = { + .bLength = sizeof fsf_hs_intr_in_desc, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fsf_fs_intr_in_desc during + * fsf_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(2), + .bInterval = 9, /* 2**(9-1) = 256 uframes -> 32 ms */ +}; +#endif + +static struct usb_descriptor_header *fsf_hs_function[] = { + (struct usb_descriptor_header *) &fsf_intf_desc, + (struct usb_descriptor_header *) &fsf_hs_bulk_in_desc, + (struct usb_descriptor_header *) &fsf_hs_bulk_out_desc, +#ifdef CONFIG_USB_FSF_TEST + (struct usb_descriptor_header *) &fsf_hs_intr_in_desc, +#endif + NULL, +}; + +#define FSF_HS_FUNCTION_PRE_EP_ENTRIES 1 + +/* Maxpacket and other transfer characteristics vary by speed. */ +static inline struct usb_endpoint_descriptor * +ep_desc(struct usb_gadget *g, + struct usb_endpoint_descriptor *fs, + struct usb_endpoint_descriptor *hs) +{ + return gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH ? hs : fs; +} + + +#define FSF_STRING_INTERFACE_IDX 0 + +/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */ +static struct usb_string fsf_string_defs[] = { + [FSF_STRING_INTERFACE_IDX].s = "Linux Mass Storage Device", + { /* ZEROES END LIST */ }, +}; + +static struct usb_gadget_strings fsf_string_table = { + .language = 0x0409, /* en-us */ + .strings = fsf_string_defs, +}; + +static struct usb_gadget_strings *fsf_strings[] = { + &fsf_string_table, + NULL, +}; + + +/*-------------------------------------------------------------------------*/ + +/* These routines may be called in process context or in_irq */ + +/* Caller must hold fsf->lock */ +static void fsf_wakeup_thread(struct fsf *fsf) +{ + /* Tell the main thread that something has happened */ + fsf->thread_wakeup_needed = 1; + if (likely(fsf->thread_task)) + wake_up_process(fsf->thread_task); +} + + +static void fsf_raise_exception(struct fsf *fsf, enum fsf_state new_state) +{ + unsigned long flags; + + /* Do nothing if a higher-priority exception is already in progress. + * If a lower-or-equal priority exception is in progress, preempt it + * and notify the main thread by sending it a signal. */ + spin_lock_irqsave(&fsf->lock, flags); + if (fsf->state <= new_state) { + fsf->exception_req_tag = fsf->ep0_req_tag; + fsf->state = new_state; + if (likely(fsf->thread_task)) + send_sig_info(SIGUSR1, SEND_SIG_FORCED, + fsf->thread_task); + } + spin_unlock_irqrestore(&fsf->lock, flags); +} + + +/*-------------------------------------------------------------------------*/ + +static int fsf_ep0_queue(struct fsf *fsf) +{ + int rc = usb_ep_queue(ep0(fsf), fsf->cdev->req, GFP_ATOMIC); + ep0(fsf)->driver_data = fsf; + if (unlikely(rc != 0 && rc != -ESHUTDOWN)) + /* We can't do much more than wait for a reset */ + FWARN(fsf, "error in submission: %s --> %d\n", + ep0(fsf)->name, rc); + return rc; +} + +/*-------------------------------------------------------------------------*/ + +/* Bulk and interrupt endpoint completion handlers. + * These always run in_irq. */ + +static void fsf_bulk_in_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct fsf *fsf = ep->driver_data; + struct fsf_buffhd *bh = req->context; + + if (req->status || req->actual != req->length) + FDBG(fsf, "%s --> %d, %u/%u\n", __func__, + req->status, req->actual, req->length); + if (req->status == -ECONNRESET) /* Request was cancelled */ + usb_ep_fifo_flush(ep); + + /* Hold the lock while we update the request and buffer states */ + smp_wmb(); + spin_lock(&fsf->lock); + bh->inreq_busy = 0; + bh->state = BUF_STATE_EMPTY; + fsf_wakeup_thread(fsf); + spin_unlock(&fsf->lock); +} + +static void fsf_bulk_out_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct fsf *fsf = ep->driver_data; + struct fsf_buffhd *bh = req->context; + + dump_msg(fsf, "bulk-out", req->buf, req->actual); + if (req->status || req->actual != bh->bulk_out_intended_length) + FDBG(fsf, "%s --> %d, %u/%u\n", __func__, + req->status, req->actual, bh->bulk_out_intended_length); + + if (req->status == -ECONNRESET) /* Request was cancelled */ + usb_ep_fifo_flush(ep); + + /* Hold the lock while we update the request and buffer states */ + smp_wmb(); + spin_lock(&fsf->lock); + bh->outreq_busy = 0; + bh->state = BUF_STATE_FULL; + fsf_wakeup_thread(fsf); + spin_unlock(&fsf->lock); +} + +#ifdef CONFIG_USB_FSF_TEST +static void fsf_intr_in_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct fsf *fsf = ep->driver_data; + struct fsf_buffhd *bh = req->context; + + if (req->status || req->actual != req->length) + FDBG(fsf, "%s --> %d, %u/%u\n", __func__, + req->status, req->actual, req->length); + + if (req->status == -ECONNRESET) /* Request was cancelled */ + usb_ep_fifo_flush(ep); + + /* Hold the lock while we update the request and buffer states */ + smp_wmb(); + spin_lock(&fsf->lock); + fsf->intreq_busy = 0; + bh->state = BUF_STATE_EMPTY; + fsf_wakeup_thread(fsf); + spin_unlock(&fsf->lock); +} +#endif /* CONFIG_USB_FSF_TEST */ + + + +/*-------------------------------------------------------------------------*/ + +/* Ep0 class-specific handlers. These always run in_irq. */ + +#ifdef CONFIG_USB_FSF_TEST +static void fsf_received_cbi_adsc(struct fsf *fsf, struct fsf_buffhd *bh) +{ + struct usb_request *req = fsf->cdev->req; + 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) { + + /* Not all controllers allow a protocol stall after + * receiving control-out data, but we'll try anyway. */ + fsf_set_halt(fsf, ep0(fsf)); + return; /* Wait for reset */ + } + + /* 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. */ + FDBG(fsf, "cbi reset request\n"); + fsf_raise_exception(fsf, FSF_STATE_RESET); + return; + } + + VFDBG(fsf, "CB[I] accept device-specific command\n"); + spin_lock(&fsf->lock); + + /* Save the command for later */ + if (unlikely(fsf->cbbuf_cmnd_size)) + FWARN(fsf, "CB[I] overwriting previous command\n"); + fsf->cbbuf_cmnd_size = req->actual; + memcpy(fsf->cbbuf_cmnd, req->buf, fsf->cbbuf_cmnd_size); + + fsf_wakeup_thread(fsf); + spin_unlock(&fsf->lock); +} +#endif /* CONFIG_USB_FSF_TEST */ + + +/* + * This is called from composite when a control request is recieved on + * ep0 addressed to our interface. + */ +static int fsf_setup(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct fsf *fsf = fsf_from_func(f); + struct usb_composite_dev *cdev = fsf->cdev; + u16 wIndex = le16_to_cpu(ctrl->wIndex); + u16 wValue = le16_to_cpu(ctrl->wValue); + u16 wLength = le16_to_cpu(ctrl->wLength); + + FINFO(fsf, "%s (wIndex = %d, wValue = %d)\n", __func__, wIndex, wValue); + + if (!fsf->config) + return -EOPNOTSUPP; + + /* Handle Bulk-only class-specific requests */ + if (transport_is_bbb(fsf)) { + switch (ctrl->bRequest) { + case USB_BULK_RESET_REQUEST: + FINFO(fsf, "USB_BULK_RESET_REQUEST\n"); + if (ctrl->bRequestType != + (USB_DIR_OUT|USB_TYPE_CLASS|USB_RECIP_INTERFACE)) + break; + /* XXX */ + /* if (wIndex != usb_interface_id(f->config, f) || */ + /* wValue != 0) */ + /* return -EDOM; */ + + /* Raise an exception to stop the current + * operation and reinitialize our state. */ + FDBG(fsf, "bulk reset request\n"); + fsf_raise_exception(fsf, FSF_STATE_RESET); + return DELAYED_STATUS; + + case USB_BULK_GET_MAX_LUN_REQUEST: + FINFO(fsf, "USB_BULK_GET_MAX_LUN_REQUEST\n"); + if (ctrl->bRequestType != + (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) + break; + /* XXX */ + /* if (wIndex != usb_interface_id(f->config, f) || */ + /* wValue != 0) */ + /* return -EDOM; */ + + VFDBG(fsf, "get max LUN\n"); + *(u8 *)cdev->req->buf = fsf->common->nluns - 1; + return 1; + } + } + +#ifdef CONFIG_USB_FSF_TEST + /* 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; + /* XXX */ + /* if (wIndex != usb_interface_id(f->config, f) || */ + /* wValue != 0) */ + /* return -EDOM; */ + if (wLength > MAX_COMMAND_SIZE) + return -EOVERFLOW; + + fsf->cdev->req->context = fsf_received_cbi_adsc; + return wLength; + } + } +#endif + + FDBG(fsf, + "unknown class-specific control req " + "%02x.%02x v%04x i%04x l%u\n", + ctrl->bRequestType, ctrl->bRequest, + wValue, wIndex, wLength); + return -EOPNOTSUPP; +} + + +/*-------------------------------------------------------------------------*/ + +/* All the following routines run in process context */ + + +/* Use this for bulk or interrupt transfers, not ep0 */ +static void fsf_start_transfer(struct fsf *fsf, struct usb_ep *ep, + struct usb_request *req, int *pbusy, + enum fsf_buffer_state *state) +{ + int rc; + + if (ep == fsf->bulk_in) + dump_msg(fsf, "bulk-in", req->buf, req->length); +#ifdef CONFIG_USB_FSF_TEST + else if (ep == fsf->intr_in) + dump_msg(fsf, "intr-in", req->buf, req->length); +#endif + + spin_lock_irq(&fsf->lock); + *pbusy = 1; + *state = BUF_STATE_BUSY; + spin_unlock_irq(&fsf->lock); + + rc = usb_ep_queue(ep, req, GFP_KERNEL); + if (rc != 0) { + *pbusy = 0; + *state = BUF_STATE_EMPTY; + + /* We can't do much more than wait for a reset */ + + /* Note: currently the net2280 driver fails zero-length + * submissions if DMA is enabled. */ + if (rc != -ESHUTDOWN && + !(rc == -EOPNOTSUPP && req->length == 0)) + FWARN(fsf, "error in submission: %s --> %d\n", + ep->name, rc); + } +} + + +static int fsf_sleep_thread(struct fsf *fsf) +{ + int rc = 0; + + /* Wait until a signal arrives or we are woken up */ + for (;;) { + try_to_freeze(); + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) { + rc = -EINTR; + break; + } + if (fsf->thread_wakeup_needed) + break; + schedule(); + } + __set_current_state(TASK_RUNNING); + fsf->thread_wakeup_needed = 0; + return rc; +} + + +/*-------------------------------------------------------------------------*/ + +static int fsf_do_read(struct fsf *fsf) +{ + struct fsf_lun *curlun = fsf->common->curlun; + u32 lba; + struct fsf_buffhd *bh; + int rc; + u32 amount_left; + loff_t file_offset, file_offset_tmp; + unsigned int amount; + unsigned int partial_page; + ssize_t nread; + + /* Get the starting Logical Block Address and check that it's + * not too big */ + if (fsf->cmnd[0] == SC_READ_6) + lba = (fsf->cmnd[1] << 16) | get_be16(&fsf->cmnd[2]); + else { + lba = get_be32(&fsf->cmnd[2]); + + /* We allow DPO (Disable Page Out = don't save data in the + * cache) and FUA (Force Unit Access = don't read from the + * cache), but we don't implement them. */ + if ((fsf->cmnd[1] & ~0x18) != 0) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + } + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + file_offset = ((loff_t) lba) << 9; + + /* Carry out the file reads */ + amount_left = fsf->data_size_from_cmnd; + if (unlikely(amount_left == 0)) + return -EIO; /* No default reply */ + + for (;;) { + + /* Figure out how much we need to read: + * Try to read the remaining amount. + * But don't read more than the buffer size. + * And don't try to read past the end of the file. + * Finally, if we're not at a page boundary, don't read past + * the next page. + * If this means reading 0 then we were asked to read past + * the end of file. */ + amount = min(amount_left, fsf_buflen); + amount = min((loff_t) amount, + curlun->file_length - file_offset); + partial_page = file_offset & (PAGE_CACHE_SIZE - 1); + if (partial_page > 0) + amount = min(amount, (unsigned int) PAGE_CACHE_SIZE - partial_page); + + /* Wait for the next buffer to become available */ + bh = fsf->common->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { + rc = fsf_sleep_thread(fsf); + if (rc) + return rc; + } + + /* If we were asked to read past the end of file, + * end with an empty buffer. */ + if (amount == 0) { + curlun->sense_data = + SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + curlun->sense_data_info = file_offset >> 9; + curlun->info_valid = 1; + bh->inreq->length = 0; + bh->state = BUF_STATE_FULL; + break; + } + + /* Perform the read */ + file_offset_tmp = file_offset; + nread = vfs_read(curlun->filp, (char __user *) bh->buf, + amount, &file_offset_tmp); + VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, + (unsigned long long) file_offset, (int) nread); + if (signal_pending(current)) + return -EINTR; + + if (nread < 0) { + LDBG(curlun, "error in file read: %d\n", + (int) nread); + nread = 0; + } else if (nread < amount) { + LDBG(curlun, "partial file read: %d/%u\n", + (int) nread, amount); + nread -= (nread & 511); /* Round down to a block */ + } + file_offset += nread; + amount_left -= nread; + fsf->residue -= nread; + bh->inreq->length = nread; + bh->state = BUF_STATE_FULL; + + /* If an error occurred, report it and its position */ + if (nread < amount) { + curlun->sense_data = SS_UNRECOVERED_READ_ERROR; + curlun->sense_data_info = file_offset >> 9; + curlun->info_valid = 1; + break; + } + + if (amount_left == 0) + break; /* No more left to read */ + + /* Send this buffer and go read some more */ + bh->inreq->zero = 0; + fsf_start_transfer(fsf, fsf->bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state); + fsf->common->next_buffhd_to_fill = bh->next; + } + + return -EIO; /* No default reply */ +} + + +/*-------------------------------------------------------------------------*/ + +static int fsf_do_write(struct fsf *fsf) +{ + struct fsf_lun *curlun = fsf->common->curlun; + u32 lba; + struct fsf_buffhd *bh; + int get_some_more; + u32 amount_left_to_req, amount_left_to_write; + loff_t usb_offset, file_offset, file_offset_tmp; + unsigned int amount; + unsigned int partial_page; + ssize_t nwritten; + int rc; + + if (fsf_lun_is_readonly(curlun)) { + curlun->sense_data = SS_WRITE_PROTECTED; + return -EINVAL; + } + spin_lock(&curlun->filp->f_lock); + curlun->filp->f_flags &= ~O_SYNC; /* Default is not to wait */ + spin_unlock(&curlun->filp->f_lock); + + /* Get the starting Logical Block Address and check that it's + * not too big */ + if (fsf->cmnd[0] == SC_WRITE_6) + lba = (fsf->cmnd[1] << 16) | get_be16(&fsf->cmnd[2]); + else { + lba = get_be32(&fsf->cmnd[2]); + + /* We allow DPO (Disable Page Out = don't save data in the + * cache) and FUA (Force Unit Access = write directly to the + * medium). We don't implement DPO; we implement FUA by + * performing synchronous output. */ + if ((fsf->cmnd[1] & ~0x18) != 0) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + if (fsf->cmnd[1] & 0x08) { /* FUA */ + spin_lock(&curlun->filp->f_lock); + curlun->filp->f_flags |= O_SYNC; + spin_unlock(&curlun->filp->f_lock); + } + } + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + + /* Carry out the file writes */ + get_some_more = 1; + file_offset = usb_offset = ((loff_t) lba) << 9; + amount_left_to_req = amount_left_to_write = fsf->data_size_from_cmnd; + + while (amount_left_to_write > 0) { + + /* Queue a request for more data from the host */ + bh = fsf->common->next_buffhd_to_fill; + if (bh->state == BUF_STATE_EMPTY && get_some_more) { + + /* Figure out how much we want to get: + * Try to get the remaining amount. + * But don't get more than the buffer size. + * And don't try to go past the end of the file. + * If we're not at a page boundary, + * don't go past the next page. + * If this means getting 0, then we were asked + * to write past the end of file. + * Finally, round down to a block boundary. */ + amount = min(amount_left_to_req, fsf_buflen); + amount = min((loff_t) amount, curlun->file_length - + usb_offset); + partial_page = usb_offset & (PAGE_CACHE_SIZE - 1); + if (partial_page > 0) + amount = min(amount, (unsigned int) PAGE_CACHE_SIZE - partial_page); + + if (amount == 0) { + get_some_more = 0; + curlun->sense_data = + SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + curlun->sense_data_info = usb_offset >> 9; + curlun->info_valid = 1; + continue; + } + amount -= (amount & 511); + if (amount == 0) { + + /* Why were we were asked to transfer a + * partial block? */ + get_some_more = 0; + continue; + } + + /* Get the next buffer */ + usb_offset += amount; + fsf->usb_amount_left -= amount; + amount_left_to_req -= amount; + if (amount_left_to_req == 0) + get_some_more = 0; + + /* amount is always divisible by 512, hence by + * the bulk-out maxpacket size */ + bh->outreq->length = bh->bulk_out_intended_length = + amount; + bh->outreq->short_not_ok = 1; + fsf_start_transfer(fsf, fsf->bulk_out, bh->outreq, + &bh->outreq_busy, &bh->state); + fsf->common->next_buffhd_to_fill = bh->next; + continue; + } + + /* Write the received data to the backing file */ + bh = fsf->common->next_buffhd_to_drain; + if (bh->state == BUF_STATE_EMPTY && !get_some_more) + break; /* We stopped early */ + if (bh->state == BUF_STATE_FULL) { + smp_rmb(); + fsf->common->next_buffhd_to_drain = bh->next; + bh->state = BUF_STATE_EMPTY; + + /* Did something go wrong with the transfer? */ + if (bh->outreq->status != 0) { + curlun->sense_data = SS_COMMUNICATION_FAILURE; + curlun->sense_data_info = file_offset >> 9; + curlun->info_valid = 1; + break; + } + + amount = bh->outreq->actual; + if (curlun->file_length - file_offset < amount) { + LERROR(curlun, + "write %u @ %llu beyond end %llu\n", + amount, + (unsigned long long)file_offset, + (unsigned long long)curlun->file_length); + amount = curlun->file_length - file_offset; + } + + /* Perform the write */ + file_offset_tmp = file_offset; + nwritten = vfs_write(curlun->filp, + (char __user *) bh->buf, + amount, &file_offset_tmp); + VLDBG(curlun, "file write %u @ %llu -> %d\n", amount, + (unsigned long long) file_offset, + (int) nwritten); + if (signal_pending(current)) + return -EINTR; /* Interrupted! */ + + if (nwritten < 0) { + LDBG(curlun, "error in file write: %d\n", + (int) nwritten); + nwritten = 0; + } else if (nwritten < amount) { + LDBG(curlun, "partial file write: %d/%u\n", + (int) nwritten, amount); + nwritten -= (nwritten & 511); + /* Round down to a block */ + } + file_offset += nwritten; + amount_left_to_write -= nwritten; + fsf->residue -= nwritten; + + /* If an error occurred, report it and its position */ + if (nwritten < amount) { + curlun->sense_data = SS_WRITE_ERROR; + curlun->sense_data_info = file_offset >> 9; + curlun->info_valid = 1; + break; + } + + /* Did the host decide to stop early? */ + if (bh->outreq->actual != bh->outreq->length) { + fsf->short_packet_received = 1; + break; + } + continue; + } + + /* Wait for something to happen */ + rc = fsf_sleep_thread(fsf); + if (rc) + return rc; + } + + return -EIO; /* No default reply */ +} + + +/*-------------------------------------------------------------------------*/ + +/* Sync the file data, don't bother with the metadata. + * This code was copied from fs/buffer.c:sys_fdatasync(). */ +static int fsf_fsync_sub(struct fsf_lun *curlun) +{ + struct file *filp = curlun->filp; + + if (fsf_lun_is_readonly(curlun) || !filp) + return 0; + return vfs_fsync(filp, filp->f_path.dentry, 1); +} + + +/*-------------------------------------------------------------------------*/ + +static int fsf_do_verify(struct fsf *fsf) +{ + struct fsf_lun *curlun = fsf->common->curlun; + u32 lba; + u32 verification_length; + struct fsf_buffhd *bh = fsf->common->next_buffhd_to_fill; + loff_t file_offset, file_offset_tmp; + u32 amount_left; + unsigned int amount; + ssize_t nread; + + /* Get the starting Logical Block Address and check that it's + * not too big */ + lba = get_be32(&fsf->cmnd[2]); + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + + /* We allow DPO (Disable Page Out = don't save data in the + * cache) but we don't implement it. */ + if ((fsf->cmnd[1] & ~0x10) != 0) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + verification_length = get_be16(&fsf->cmnd[7]); + if (unlikely(verification_length == 0)) + return -EIO; /* No default reply */ + + /* Prepare to carry out the file verify */ + amount_left = verification_length << 9; + file_offset = ((loff_t) lba) << 9; + + /* Write out all the dirty buffers before invalidating them */ + fsf_fsync_sub(curlun); + if (signal_pending(current)) + return -EINTR; + + /* Invalidate sub */ + { + unsigned long rc = + invalidate_mapping_pages(curlun->filp->f_path.dentry->d_inode->i_mapping, 0, -1); + VLDBG(curlun, "invalidate_inode_pages -> %ld\n", rc); + } + if (signal_pending(current)) + return -EINTR; + + /* Just try to read the requested blocks */ + while (amount_left > 0) { + + /* Figure out how much we need to read: + * Try to read the remaining amount, but not more than + * the buffer size. + * And don't try to read past the end of the file. + * If this means reading 0 then we were asked to read + * past the end of file. */ + amount = min(amount_left, fsf_buflen); + amount = min((loff_t) amount, + curlun->file_length - file_offset); + if (amount == 0) { + curlun->sense_data = + SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + curlun->sense_data_info = file_offset >> 9; + curlun->info_valid = 1; + break; + } + + /* Perform the read */ + file_offset_tmp = file_offset; + nread = vfs_read(curlun->filp, + (char __user *) bh->buf, + amount, &file_offset_tmp); + VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, + (unsigned long long) file_offset, + (int) nread); + if (signal_pending(current)) + return -EINTR; + + if (nread < 0) { + LDBG(curlun, "error in file verify: %d\n", + (int) nread); + nread = 0; + } else if (nread < amount) { + LDBG(curlun, "partial file verify: %d/%u\n", + (int) nread, amount); + nread -= (nread & 511); /* Round down to a sector */ + } + if (nread == 0) { + curlun->sense_data = SS_UNRECOVERED_READ_ERROR; + curlun->sense_data_info = file_offset >> 9; + curlun->info_valid = 1; + break; + } + file_offset += nread; + amount_left -= nread; + } + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +static int fsf_do_inquiry(struct fsf *fsf, struct fsf_buffhd *bh) +{ + u8 *buf = (u8 *) bh->buf; + + if (!fsf->common->curlun) { /* Unsupported LUNs are okay */ + fsf->bad_lun_okay = 1; + memset(buf, 0, 36); + buf[0] = 0x7f; /* Unsupported, no device-type */ + buf[4] = 31; /* Additional length */ + return 36; + } + + memset(buf, 0, 8); /* Non-removable, direct-access device */ + if (fsf_lun_is_cdrom(fsf->common->curlun)) + buf[0] = 0x05; /* type: CD-ROM */ + if (fsf_lun_is_removable(fsf->common->curlun)) + buf[1] = 0x80; /* set removable bit */ + buf[2] = 2; /* ANSI SCSI level 2 */ + buf[3] = 2; /* SCSI-2 INQUIRY data format */ + buf[4] = 31; /* Additional length */ + /* No special options */ + snprintf(buf + 8, 29, "%-8s%-16s%04x", + fsf->common->vendor, fsf->common->product, + fsf->common->release); + return 36; +} + + +static int fsf_do_request_sense(struct fsf *fsf, struct fsf_buffhd *bh) +{ + struct fsf_lun *curlun = fsf->common->curlun; + u8 *buf = (u8 *) bh->buf; + u32 sd, sdinfo; + int valid; + + /* + * From the SCSI-2 spec., section 7.9 (Unit attention condition): + * + * If a REQUEST SENSE command is received from an initiator + * with a pending unit attention condition (before the target + * generates the contingent allegiance condition), then the + * target shall either: + * a) report any pending sense data and preserve the unit + * attention condition on the logical unit, or, + * b) report the unit attention condition, may discard any + * pending sense data, and clear the unit attention + * condition on the logical unit for that initiator. + * + * FSF normally uses option a); enable this code to use option b). + */ +#if 0 + if (curlun && curlun->unit_attention_data != SS_NO_SENSE) { + curlun->sense_data = curlun->unit_attention_data; + curlun->unit_attention_data = SS_NO_SENSE; + } +#endif + + if (!curlun) { /* Unsupported LUNs are okay */ + fsf->bad_lun_okay = 1; + sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; + sdinfo = 0; + valid = 0; + } else { + sd = curlun->sense_data; + sdinfo = curlun->sense_data_info; + valid = curlun->info_valid << 7; + curlun->sense_data = SS_NO_SENSE; + curlun->sense_data_info = 0; + curlun->info_valid = 0; + } + + memset(buf, 0, 18); + buf[0] = valid | 0x70; /* Valid, current error */ + buf[2] = SK(sd); + put_be32(&buf[3], sdinfo); /* Sense information */ + buf[7] = 18 - 8; /* Additional sense length */ + buf[12] = ASC(sd); + buf[13] = ASCQ(sd); + return 18; +} + + +static int fsf_do_read_capacity(struct fsf *fsf, struct fsf_buffhd *bh) +{ + struct fsf_lun *curlun = fsf->common->curlun; + u32 lba = get_be32(&fsf->cmnd[2]); + int pmi = fsf->cmnd[8]; + u8 *buf = (u8 *) bh->buf; + + /* Check the PMI and LBA fields */ + if (pmi > 1 || (pmi == 0 && lba != 0)) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + put_be32(&buf[0], curlun->num_sectors - 1); /* Max logical block */ + put_be32(&buf[4], 512); /* Block length */ + return 8; +} + + +#ifndef CONFIG_USB_FSF_CDROM_OFF + +static void store_cdrom_address(u8 *dest, int msf, u32 addr) +{ + if (msf) { + /* Convert to Minutes-Seconds-Frames */ + addr >>= 2; /* Convert to 2048-byte frames */ + addr += 2*75; /* Lead-in occupies 2 seconds */ + dest[3] = addr % 75; /* Frames */ + addr /= 75; + dest[2] = addr % 60; /* Seconds */ + addr /= 60; + dest[1] = addr; /* Minutes */ + dest[0] = 0; /* Reserved */ + } else { + /* Absolute sector */ + put_be32(dest, addr); + } +} + +static int fsf_do_read_header(struct fsf *fsf, struct fsf_buffhd *bh) +{ + struct fsf_lun *curlun = fsf->common->curlun; + int msf = fsf->cmnd[1] & 0x02; + u32 lba = get_be32(&fsf->cmnd[2]); + u8 *buf = (u8 *) bh->buf; + + if ((fsf->cmnd[1] & ~0x02) != 0) { /* Mask away MSF */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + + memset(buf, 0, 8); + buf[0] = 0x01; /* 2048 bytes of user data, rest is EC */ + store_cdrom_address(&buf[4], msf, lba); + return 8; +} + + +static int fsf_do_read_toc(struct fsf *fsf, struct fsf_buffhd *bh) +{ + struct fsf_lun *curlun = fsf->common->curlun; + int msf = fsf->cmnd[1] & 0x02; + int start_track = fsf->cmnd[6]; + u8 *buf = (u8 *) bh->buf; + + if ((fsf->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */ + start_track > 1) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + memset(buf, 0, 20); + buf[1] = (20-2); /* TOC data length */ + buf[2] = 1; /* First track number */ + buf[3] = 1; /* Last track number */ + buf[5] = 0x16; /* Data track, copying allowed */ + buf[6] = 0x01; /* Only track is number 1 */ + store_cdrom_address(&buf[8], msf, 0); + + buf[13] = 0x16; /* Lead-out track is data */ + buf[14] = 0xAA; /* Lead-out track number */ + store_cdrom_address(&buf[16], msf, curlun->num_sectors); + return 20; +} + +#endif + +static int fsf_do_mode_sense(struct fsf *fsf, struct fsf_buffhd *bh) +{ + struct fsf_lun *curlun = fsf->common->curlun; + int mscmnd = fsf->cmnd[0]; + u8 *buf = (u8 *) bh->buf; + u8 *buf0 = buf; + int pc, page_code; + int changeable_values, all_pages; + int valid_page = 0; + int len, limit; + + if ((fsf->cmnd[1] & ~0x08) != 0) { /* Mask away DBD */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + pc = fsf->cmnd[2] >> 6; + page_code = fsf->cmnd[2] & 0x3f; + if (pc == 3) { + curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED; + return -EINVAL; + } + changeable_values = (pc == 1); + all_pages = (page_code == 0x3f); + + /* Write the mode parameter header. Fixed values are: default + * medium type, no cache control (DPOFUA), and no block descriptors. + * The only variable value is the WriteProtect bit. We will fill in + * the mode data length later. */ + memset(buf, 0, 8); + if (mscmnd == SC_MODE_SENSE_6) { + /* WP, DPOFUA */ + buf[2] = (fsf_lun_is_readonly(curlun) ? 0x80 : 0x00); + buf += 4; + limit = 255; + } else { /* SC_MODE_SENSE_10 */ + /* WP, DPOFUA */ + buf[3] = (fsf_lun_is_readonly(curlun) ? 0x80 : 0x00); + buf += 8; + limit = 65535; /* Should be fsf_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 */ + /* Maximum prefetch ceiling */ + put_be16(&buf[10], 0xffff); + } + buf += 12; + } + + /* Check that a valid page was requested and the mode data length + * isn't too long. */ + len = buf - buf0; + if (!valid_page || len > limit) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + /* Store the mode data length */ + if (mscmnd == SC_MODE_SENSE_6) + buf0[0] = len - 1; + else + put_be16(buf0, len - 2); + return len; +} + + +#ifndef CONFIG_USB_FSF_REMOVABLE_OFF + +static int fsf_do_start_stop(struct fsf *fsf) +{ + struct fsf_lun *curlun = fsf->common->curlun; + int loej, start; + + if (!fsf_lun_is_removable(fsf->common->curlun)) { + curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; + } + + /* int immed = fsf->cmnd[1] & 0x01; */ + loej = fsf->cmnd[4] & 0x02; + start = fsf->cmnd[4] & 0x01; + +#ifdef CONFIG_USB_FSF_TEST + if ((fsf->cmnd[1] & ~0x01) != 0 || /* Mask away Immed */ + (fsf->cmnd[4] & ~0x03) != 0) { /* Mask LoEj, Start */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + if (!start) { + + /* Are we allowed to unload the media? */ + if (curlun->prevent_medium_removal) { + LDBG(curlun, "unload attempt prevented\n"); + curlun->sense_data = SS_MEDIUM_REMOVAL_PREVENTED; + return -EINVAL; + } + if (loej) { /* Simulate an unload/eject */ + up_read(&fsf->common->filesem); + down_write(&fsf->common->filesem); + fsf_lun_close_file(curlun); + up_write(&fsf->common->filesem); + down_read(&fsf->common->filesem); + } + } else { + + /* Our emulation doesn't support mounting; the medium is + * available for use as soon as it is loaded. */ + if (!fsf_lun_is_file_open(curlun)) { + curlun->sense_data = SS_MEDIUM_NOT_PRESENT; + return -EINVAL; + } + } +#endif + return 0; +} + + +static int fsf_do_prevent_allow(struct fsf *fsf) +{ + struct fsf_lun *curlun = fsf->common->curlun; + int prevent; + + if (!fsf_lun_is_removable(fsf->common->curlun)) { + curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; + } + + prevent = fsf->cmnd[4] & 0x01; + if ((fsf->cmnd[4] & ~0x01) != 0) { /* Mask away Prevent */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + if (curlun->prevent_medium_removal && !prevent) + fsf_fsync_sub(curlun); + curlun->prevent_medium_removal = prevent; + return 0; +} + +#endif + + +static int fsf_do_read_format_capacities(struct fsf *fsf, + struct fsf_buffhd *bh) +{ + struct fsf_lun *curlun = fsf->common->curlun; + u8 *buf = (u8 *) bh->buf; + + buf[0] = buf[1] = buf[2] = 0; + buf[3] = 8; /* Only the Current/Maximum Capacity Descriptor */ + buf += 4; + + put_be32(&buf[0], curlun->num_sectors); /* Number of blocks */ + put_be32(&buf[4], 512); /* Block length */ + buf[4] = 0x02; /* Current capacity */ + return 12; +} + + +static int fsf_do_mode_select(struct fsf *fsf, struct fsf_buffhd *bh) +{ + struct fsf_lun *curlun = fsf->common->curlun; + + /* We don't support MODE SELECT */ + curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; +} + + +/*-------------------------------------------------------------------------*/ + +static int fsf_halt_bulk_in_endpoint(struct fsf *fsf) +{ + int rc; + + rc = fsf_set_halt(fsf, fsf->bulk_in); + if (rc == -EAGAIN) + VFDBG(fsf, "delayed bulk-in endpoint halt\n"); + while (rc != 0) { + if (rc != -EAGAIN) { + FWARN(fsf, "usb_ep_set_halt -> %d\n", rc); + rc = 0; + break; + } + + /* Wait for a short time and then try again */ + if (msleep_interruptible(100) != 0) + return -EINTR; + rc = usb_ep_set_halt(fsf->bulk_in); + } + return rc; +} + +static int fsf_wedge_bulk_in_endpoint(struct fsf *fsf) +{ + int rc; + + FDBG(fsf, "bulk-in set wedge\n"); + rc = usb_ep_set_wedge(fsf->bulk_in); + if (rc == -EAGAIN) + VFDBG(fsf, "delayed bulk-in endpoint wedge\n"); + while (rc != 0) { + if (rc != -EAGAIN) { + FWARN(fsf, "usb_ep_set_wedge -> %d\n", rc); + rc = 0; + break; + } + + /* Wait for a short time and then try again */ + if (msleep_interruptible(100) != 0) + return -EINTR; + rc = usb_ep_set_wedge(fsf->bulk_in); + } + return rc; +} + +static int fsf_pad_with_zeros(struct fsf *fsf) +{ + struct fsf_buffhd *bh = fsf->common->next_buffhd_to_fill; + u32 nkeep = bh->inreq->length; + u32 nsend; + int rc; + + bh->state = BUF_STATE_EMPTY; /* For the first iteration */ + fsf->usb_amount_left = nkeep + fsf->residue; + while (fsf->usb_amount_left > 0) { + + /* Wait for the next buffer to be free */ + while (bh->state != BUF_STATE_EMPTY) { + rc = fsf_sleep_thread(fsf); + if (rc) + return rc; + } + + nsend = min(fsf->usb_amount_left, fsf_buflen); + memset(bh->buf + nkeep, 0, nsend - nkeep); + bh->inreq->length = nsend; + bh->inreq->zero = 0; + fsf_start_transfer(fsf, fsf->bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state); + bh = fsf->common->next_buffhd_to_fill = bh->next; + fsf->usb_amount_left -= nsend; + nkeep = 0; + } + return 0; +} + +static int fsf_throw_away_data(struct fsf *fsf) +{ + struct fsf_buffhd *bh; + u32 amount; + int rc; + + while ((bh = fsf->common->next_buffhd_to_drain)->state != BUF_STATE_EMPTY || + fsf->usb_amount_left > 0) { + + /* Throw away the data in a filled buffer */ + if (bh->state == BUF_STATE_FULL) { + smp_rmb(); + bh->state = BUF_STATE_EMPTY; + fsf->common->next_buffhd_to_drain = bh->next; + + /* A short packet or an error ends everything */ + if (bh->outreq->actual != bh->outreq->length || + bh->outreq->status != 0) { + fsf_raise_exception(fsf, FSF_STATE_ABORT_BULK_OUT); + return -EINTR; + } + continue; + } + + /* Try to submit another request if we need one */ + bh = fsf->common->next_buffhd_to_fill; + if (bh->state == BUF_STATE_EMPTY && fsf->usb_amount_left > 0) { + amount = min(fsf->usb_amount_left, fsf_buflen); + + /* amount is always divisible by 512, hence by + * the bulk-out maxpacket size */ + bh->outreq->length = bh->bulk_out_intended_length = + amount; + bh->outreq->short_not_ok = 1; + fsf_start_transfer(fsf, fsf->bulk_out, bh->outreq, + &bh->outreq_busy, &bh->state); + fsf->common->next_buffhd_to_fill = bh->next; + fsf->usb_amount_left -= amount; + continue; + } + + /* Otherwise wait for something to happen */ + rc = fsf_sleep_thread(fsf); + if (rc) + return rc; + } + return 0; +} + + +static int fsf_finish_reply(struct fsf *fsf) +{ + struct fsf_buffhd *bh = fsf->common->next_buffhd_to_fill; + int rc = 0; + + switch (fsf->data_dir) { + case DATA_DIR_NONE: + break; /* Nothing to send */ + + /* If we don't know whether the host wants to read or write, + * this must be CB or CBI with an unknown command. We mustn't + * try to send or receive any data. So stall both bulk pipes + * if we can and wait for a reset. */ + case DATA_DIR_UNKNOWN: + if (fsf_can_stall(fsf)) { + fsf_set_halt(fsf, fsf->bulk_out); + rc = fsf_halt_bulk_in_endpoint(fsf); + } + rc = -EINVAL; + break; + + /* All but the last buffer of data must have already been sent */ + case DATA_DIR_TO_HOST: + if (fsf->data_size == 0) { + /* Nothing to send */ + + /* If there's no residue, simply send the last buffer */ + } else if (fsf->residue == 0) { + bh->inreq->zero = 0; + fsf_start_transfer(fsf, fsf->bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state); + fsf->common->next_buffhd_to_fill = bh->next; + + /* There is a residue. For CB and CBI, simply mark the end + * of the data with a short packet. However, if we are + * allowed to stall, there was no data at all (residue == + * data_size), and the command failed (invalid LUN or + * sense data is set), then halt the bulk-in endpoint + * instead. */ + } else if (!transport_is_bbb(fsf)) { + if (fsf_can_stall(fsf) && + fsf->residue == fsf->data_size && + (!fsf->common->curlun || + fsf->common->curlun->sense_data != SS_NO_SENSE)) { + bh->state = BUF_STATE_EMPTY; + rc = fsf_halt_bulk_in_endpoint(fsf); + } else { + bh->inreq->zero = 1; + fsf_start_transfer(fsf, fsf->bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state); + fsf->common->next_buffhd_to_fill = bh->next; + } + + /* For Bulk-only, if we're allowed to stall then send the + * short packet and halt the bulk-in endpoint. If we can't + * stall, pad out the remaining data with 0's. */ + } else { + if (fsf_can_stall(fsf)) { + bh->inreq->zero = 1; + fsf_start_transfer(fsf, fsf->bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state); + fsf->common->next_buffhd_to_fill = bh->next; + rc = fsf_halt_bulk_in_endpoint(fsf); + } else + rc = fsf_pad_with_zeros(fsf); + } + break; + + /* We have processed all we want from the data the host has sent. + * There may still be outstanding bulk-out requests. */ + case DATA_DIR_FROM_HOST: + if (fsf->residue == 0) { + /* Nothing to receive */ + + /* Did the host stop sending unexpectedly early? */ + } else if (fsf->short_packet_received) { + fsf_raise_exception(fsf, FSF_STATE_ABORT_BULK_OUT); + rc = -EINTR; + + /* We haven't processed all the incoming data. Even though + * we may be allowed to stall, doing so would cause a race. + * The controller may already have ACK'ed all the remaining + * bulk-out packets, in which case the host wouldn't see a + * STALL. Not realizing the endpoint was halted, it wouldn't + * clear the halt -- leading to problems later on. */ + } else if (fsf_can_stall(fsf)) { + fsf_set_halt(fsf, fsf->bulk_out); + fsf_raise_exception(fsf, FSF_STATE_ABORT_BULK_OUT); + rc = -EINTR; + + /* We can't stall. Read in the excess data and throw it + * all away. */ + } else { + rc = fsf_throw_away_data(fsf); + } + break; + } + return rc; +} + + +static int fsf_send_status(struct fsf *fsf) +{ + struct fsf_lun *curlun = fsf->common->curlun; + struct fsf_buffhd *bh; + int rc; + u8 status = USB_STATUS_PASS; + u32 sd, sdinfo = 0; + + /* Wait for the next buffer to become available */ + bh = fsf->common->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { + rc = fsf_sleep_thread(fsf); + if (rc) + return rc; + } + + if (curlun) { + sd = curlun->sense_data; + sdinfo = curlun->sense_data_info; + } else if (fsf->bad_lun_okay) + sd = SS_NO_SENSE; + else + sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; + + if (fsf->phase_error) { + FDBG(fsf, "sending phase-error status\n"); + status = USB_STATUS_PHASE_ERROR; + sd = SS_INVALID_COMMAND; + } else if (sd != SS_NO_SENSE) { + FDBG(fsf, "sending command-failure status\n"); + status = USB_STATUS_FAIL; + VFDBG(fsf, " sense data: SK x%02x, ASC x%02x, ASCQ x%02x;" + " info x%x\n", + SK(sd), ASC(sd), ASCQ(sd), sdinfo); + } + + if (transport_is_bbb(fsf)) { + struct bulk_cs_wrap *csw = bh->buf; + + /* Store and send the Bulk-only CSW */ + csw->Signature = cpu_to_le32(USB_BULK_CS_SIG); + csw->Tag = fsf->tag; + csw->Residue = cpu_to_le32(fsf->residue); + csw->Status = status; + + bh->inreq->length = USB_BULK_CS_WRAP_LEN; + bh->inreq->zero = 0; + fsf_start_transfer(fsf, fsf->bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state); + +#ifdef CONFIG_USB_FSF_TEST + } else if (fsf_transport_type(fsf) == US_PR_CB) { + /* Control-Bulk transport has no status phase! */ + return 0; + + } else { /* US_PR_CBI */ + struct interrupt_data *buf = bh->buf; + + /* Store and send the Interrupt data. UFI sends the ASC + * and ASCQ bytes. Everything else sends a Type (which + * is always 0) and the status Value. */ + if (fsf_protocol_type(fsf) == US_SC_UFI) { + buf->bType = ASC(sd); + buf->bValue = ASCQ(sd); + } else { + buf->bType = 0; + buf->bValue = status; + } + fsf->intreq->length = CBI_INTERRUPT_DATA_LEN; + + fsf->intr_buffhd = bh; /* Point to the right buffhd */ + fsf->intreq->buf = bh->inreq->buf; + fsf->intreq->context = bh; + fsf_start_transfer(fsf, fsf->intr_in, fsf->intreq, + &fsf->intreq_busy, &bh->state); +#endif + } + + fsf->common->next_buffhd_to_fill = bh->next; + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +/* Check whether the command is properly formed and whether its data size + * and direction agree with the values we already have. */ +static int fsf_check_command(struct fsf *fsf, int cmnd_size, + enum data_direction data_dir, unsigned int mask, + int needs_medium, const char *name) +{ + int i; + int lun = fsf->cmnd[1] >> 5; + static const char dirletter[4] = {'u', 'o', 'i', 'n'}; + char hdlen[20]; + struct fsf_lun *curlun; + + /* Adjust the expected cmnd_size for protocol encapsulation padding. + * Transparent SCSI doesn't pad. */ + if (!protocol_is_scsi(fsf)) { + /* There's some disagreement as to whether RBC pads + * commands or not. We'll play it safe and accept + * either form. */ + if (fsf_protocol_type(fsf) == US_SC_RBC) { + if (fsf->cmnd_size == 12) + cmnd_size = 12; + /* All the other protocols pad to 12 bytes */ + } else + cmnd_size = 12; + } + + hdlen[0] = 0; + if (fsf->data_dir != DATA_DIR_UNKNOWN) + sprintf(hdlen, ", H%c=%u", dirletter[(int) fsf->data_dir], + fsf->data_size); + VFDBG(fsf, "SCSI command: %s; Dc=%d, D%c=%u; Hc=%d%s\n", + name, cmnd_size, dirletter[(int) data_dir], + fsf->data_size_from_cmnd, fsf->cmnd_size, hdlen); + + /* We can't reply at all until we know the correct data direction + * and size. */ + if (fsf->data_size_from_cmnd == 0) + data_dir = DATA_DIR_NONE; + if (fsf->data_dir == DATA_DIR_UNKNOWN) { /* CB or CBI */ + fsf->data_dir = data_dir; + fsf->data_size = fsf->data_size_from_cmnd; + + } else { /* Bulk-only */ + if (fsf->data_size < fsf->data_size_from_cmnd) { + + /* Host data size < Device data size is a phase error. + * Carry out the command, but only transfer as much + * as we are allowed. */ + fsf->data_size_from_cmnd = fsf->data_size; + fsf->phase_error = 1; + } + } + fsf->residue = fsf->usb_amount_left = fsf->data_size; + + /* Conflicting data directions is a phase error */ + if (fsf->data_dir != data_dir && fsf->data_size_from_cmnd > 0) { + fsf->phase_error = 1; + return -EINVAL; + } + + /* Verify the length of the command itself */ + if (cmnd_size != fsf->cmnd_size) { + + /* Special case workaround: There are plenty of buggy SCSI + * implementations. Many have issues with cbw->Length + * field passing a wrong command size. For those cases we + * always try to work around the problem by using the length + * sent by the host side provided it is at least as large + * as the correct command length. + * Examples of such cases would be MS-Windows, which issues + * REQUEST SENSE with cbw->Length == 12 where it should + * be 6, and xbox360 issuing INQUIRY, TEST UNIT READY and + * REQUEST SENSE with cbw->Length == 10 where it should + * be 6 as well. + */ + if (cmnd_size <= fsf->cmnd_size) { + FDBG(fsf, "%s is buggy! Expected length %d " + "but we got %d\n", name, + cmnd_size, fsf->cmnd_size); + cmnd_size = fsf->cmnd_size; + } else { + fsf->phase_error = 1; + return -EINVAL; + } + } + + /* Check that the LUN values are consistent */ + if (transport_is_bbb(fsf)) { + if (fsf->common->lun != lun) + FDBG(fsf, "using LUN %d from CBW, " + "not LUN %d from CDB\n", + fsf->common->lun, lun); + } else + fsf->common->lun = lun; /* Use LUN from the command */ + + /* Check the LUN */ + if (fsf->common->lun >= 0 && fsf->common->lun < fsf->common->nluns) { + fsf->common->curlun = curlun = + &fsf->common->luns[fsf->common->lun]; + if (fsf->cmnd[0] != SC_REQUEST_SENSE) { + curlun->sense_data = SS_NO_SENSE; + curlun->sense_data_info = 0; + curlun->info_valid = 0; + } + } else { + fsf->common->curlun = curlun = NULL; + fsf->bad_lun_okay = 0; + + /* INQUIRY and REQUEST SENSE commands are explicitly allowed + * to use unsupported LUNs; all others may not. */ + if (fsf->cmnd[0] != SC_INQUIRY && + fsf->cmnd[0] != SC_REQUEST_SENSE) { + FDBG(fsf, "unsupported LUN %d\n", fsf->common->lun); + return -EINVAL; + } + } + + /* If a unit attention condition exists, only INQUIRY and + * REQUEST SENSE commands are allowed; anything else must fail. */ + if (curlun && curlun->unit_attention_data != SS_NO_SENSE && + fsf->cmnd[0] != SC_INQUIRY && + fsf->cmnd[0] != SC_REQUEST_SENSE) { + curlun->sense_data = curlun->unit_attention_data; + curlun->unit_attention_data = SS_NO_SENSE; + return -EINVAL; + } + + /* Check that only command bytes listed in the mask are non-zero */ + fsf->cmnd[1] &= 0x1f; /* Mask away the LUN */ + for (i = 1; i < cmnd_size; ++i) { + if (fsf->cmnd[i] && !(mask & (1 << i))) { + if (curlun) + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + } + + /* If the medium isn't mounted and the command needs to access + * it, return an error. */ + if (curlun && !fsf_lun_is_file_open(curlun) && needs_medium) { + curlun->sense_data = SS_MEDIUM_NOT_PRESENT; + return -EINVAL; + } + + return 0; +} + + +static int fsf_do_scsi_command(struct fsf *fsf) +{ + struct fsf_buffhd *bh; + int rc; + int reply = -EINVAL; + int i; + static char unknown[16]; + + dump_cdb(fsf); + + /* Wait for the next buffer to become available for data or status */ + bh = fsf->common->next_buffhd_to_drain = + fsf->common->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { + rc = fsf_sleep_thread(fsf); + if (rc) + return rc; + } + fsf->phase_error = 0; + fsf->short_packet_received = 0; + + /* We're using the backing file */ + down_read(&fsf->common->filesem); + switch (fsf->cmnd[0]) { + + case SC_INQUIRY: + fsf->data_size_from_cmnd = fsf->cmnd[4]; + reply = fsf_check_command(fsf, 6, DATA_DIR_TO_HOST, + (1<<4), 0, + "INQUIRY"); + if (reply == 0) + reply = fsf_do_inquiry(fsf, bh); + break; + + case SC_MODE_SELECT_6: + fsf->data_size_from_cmnd = fsf->cmnd[4]; + reply = fsf_check_command(fsf, 6, DATA_DIR_FROM_HOST, + (1<<1) | (1<<4), 0, + "MODE SELECT(6)"); + if (reply == 0) + reply = fsf_do_mode_select(fsf, bh); + break; + + case SC_MODE_SELECT_10: + fsf->data_size_from_cmnd = get_be16(&fsf->cmnd[7]); + reply = fsf_check_command(fsf, 10, DATA_DIR_FROM_HOST, + (1<<1) | (3<<7), 0, + "MODE SELECT(10)"); + if (reply == 0) + reply = fsf_do_mode_select(fsf, bh); + break; + + case SC_MODE_SENSE_6: + fsf->data_size_from_cmnd = fsf->cmnd[4]; + reply = fsf_check_command(fsf, 6, DATA_DIR_TO_HOST, + (1<<1) | (1<<2) | (1<<4), 0, + "MODE SENSE(6)"); + if (reply == 0) + reply = fsf_do_mode_sense(fsf, bh); + break; + + case SC_MODE_SENSE_10: + fsf->data_size_from_cmnd = get_be16(&fsf->cmnd[7]); + reply = fsf_check_command(fsf, 10, DATA_DIR_TO_HOST, + (1<<1) | (1<<2) | (3<<7), 0, + "MODE SENSE(10)"); + if (reply == 0) + reply = fsf_do_mode_sense(fsf, bh); + break; + +#ifndef CONFIG_USB_FSF_REMOVABLE_OFF + case SC_PREVENT_ALLOW_MEDIUM_REMOVAL: + fsf->data_size_from_cmnd = 0; + reply = fsf_check_command(fsf, 6, DATA_DIR_NONE, + (1<<4), 0, + "PREVENT-ALLOW MEDIUM REMOVAL"); + if (reply == 0) + reply = fsf_do_prevent_allow(fsf); + break; +#endif + + case SC_READ_6: + i = fsf->cmnd[4]; + fsf->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + reply = fsf_check_command(fsf, 6, DATA_DIR_TO_HOST, + (7<<1) | (1<<4), 1, + "READ(6)"); + if (reply == 0) + reply = fsf_do_read(fsf); + break; + + case SC_READ_10: + fsf->data_size_from_cmnd = get_be16(&fsf->cmnd[7]) << 9; + reply = fsf_check_command(fsf, 10, DATA_DIR_TO_HOST, + (1<<1) | (0xf<<2) | (3<<7), 1, + "READ(10)"); + if (reply == 0) + reply = fsf_do_read(fsf); + break; + + case SC_READ_12: + fsf->data_size_from_cmnd = get_be32(&fsf->cmnd[6]) << 9; + reply = fsf_check_command(fsf, 12, DATA_DIR_TO_HOST, + (1<<1) | (0xf<<2) | (0xf<<6), 1, + "READ(12)"); + if (reply == 0) + reply = fsf_do_read(fsf); + break; + + case SC_READ_CAPACITY: + fsf->data_size_from_cmnd = 8; + reply = fsf_check_command(fsf, 10, DATA_DIR_TO_HOST, + (0xf<<2) | (1<<8), 1, + "READ CAPACITY"); + if (reply == 0) + reply = fsf_do_read_capacity(fsf, bh); + break; + +#ifndef CONFIG_USB_FSF_CDROM_OFF + + case SC_READ_HEADER: + if (!fsf_lun_is_cdrom(fsf->common->curlun)) + goto unknown_cmnd; + fsf->data_size_from_cmnd = get_be16(&fsf->cmnd[7]); + reply = fsf_check_command(fsf, 10, DATA_DIR_TO_HOST, + (3<<7) | (0x1f<<1), 1, + "READ HEADER"); + if (reply == 0) + reply = fsf_do_read_header(fsf, bh); + break; + + case SC_READ_TOC: + if (!fsf_lun_is_cdrom(fsf->common->curlun)) + goto unknown_cmnd; + fsf->data_size_from_cmnd = get_be16(&fsf->cmnd[7]); + reply = fsf_check_command(fsf, 10, DATA_DIR_TO_HOST, + (7<<6) | (1<<1), 1, + "READ TOC"); + if (reply == 0) + reply = fsf_do_read_toc(fsf, bh); + break; + +#endif + + case SC_READ_FORMAT_CAPACITIES: + fsf->data_size_from_cmnd = get_be16(&fsf->cmnd[7]); + reply = fsf_check_command(fsf, 10, DATA_DIR_TO_HOST, + (3<<7), 1, + "READ FORMAT CAPACITIES"); + if (reply == 0) + reply = fsf_do_read_format_capacities(fsf, bh); + break; + + case SC_REQUEST_SENSE: + fsf->data_size_from_cmnd = fsf->cmnd[4]; + reply = fsf_check_command(fsf, 6, DATA_DIR_TO_HOST, + (1<<4), 0, + "REQUEST SENSE"); + if (reply == 0) + reply = fsf_do_request_sense(fsf, bh); + break; + +#ifndef CONFIG_USB_FSF_REMOVABLE_OFF + case SC_START_STOP_UNIT: + fsf->data_size_from_cmnd = 0; + reply = fsf_check_command(fsf, 6, DATA_DIR_NONE, + (1<<1) | (1<<4), 0, + "START-STOP UNIT"); + if (reply == 0) + reply = fsf_do_start_stop(fsf); + break; +#endif + + case SC_SYNCHRONIZE_CACHE: + fsf->data_size_from_cmnd = 0; + reply = fsf_check_command(fsf, 10, DATA_DIR_NONE, + (0xf<<2) | (3<<7), 1, + "SYNCHRONIZE CACHE"); + if (reply == 0 && fsf_fsync_sub(fsf->common->curlun)) + /* We ignore the requested LBA and write out + * all file's dirty data buffers. */ + fsf->common->curlun->sense_data = SS_WRITE_ERROR; + break; + + case SC_TEST_UNIT_READY: + fsf->data_size_from_cmnd = 0; + reply = fsf_check_command(fsf, 6, DATA_DIR_NONE, + 0, 1, + "TEST UNIT READY"); + break; + + /* Although optional, this command is used by MS-Windows. We + * support a minimal version: BytChk must be 0. */ + case SC_VERIFY: + fsf->data_size_from_cmnd = 0; + reply = fsf_check_command(fsf, 10, DATA_DIR_NONE, + (1<<1) | (0xf<<2) | (3<<7), 1, + "VERIFY"); + if (reply == 0) + reply = fsf_do_verify(fsf); + break; + + case SC_WRITE_6: + i = fsf->cmnd[4]; + fsf->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + reply = fsf_check_command(fsf, 6, DATA_DIR_FROM_HOST, + (7<<1) | (1<<4), 1, + "WRITE(6)"); + if (reply == 0) + reply = fsf_do_write(fsf); + break; + + case SC_WRITE_10: + fsf->data_size_from_cmnd = get_be16(&fsf->cmnd[7]) << 9; + reply = fsf_check_command(fsf, 10, DATA_DIR_FROM_HOST, + (1<<1) | (0xf<<2) | (3<<7), 1, + "WRITE(10)"); + if (reply == 0) + reply = fsf_do_write(fsf); + break; + + case SC_WRITE_12: + fsf->data_size_from_cmnd = get_be32(&fsf->cmnd[6]) << 9; + reply = fsf_check_command(fsf, 12, DATA_DIR_FROM_HOST, + (1<<1) | (0xf<<2) | (0xf<<6), 1, + "WRITE(12)"); + if (reply == 0) + reply = fsf_do_write(fsf); + 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: +#ifndef CONFIG_USB_FSF_CDROM_OFF /* prevents "unused lable" warning */ +unknown_cmnd: +#endif + fsf->data_size_from_cmnd = 0; + sprintf(unknown, "Unknown x%02x", fsf->cmnd[0]); + reply = fsf_check_command(fsf, fsf->cmnd_size, + DATA_DIR_UNKNOWN, 0xff, 0, unknown); + if (reply == 0) { + fsf->common->curlun->sense_data = SS_INVALID_COMMAND; + reply = -EINVAL; + } + break; + } + up_read(&fsf->common->filesem); + + if (reply == -EINTR || signal_pending(current)) + return -EINTR; + + /* Set up the single reply buffer for fsf_finish_reply() */ + if (reply == -EINVAL) + reply = 0; /* Error reply length */ + if (reply >= 0 && fsf->data_dir == DATA_DIR_TO_HOST) { + reply = min((u32) reply, fsf->data_size_from_cmnd); + bh->inreq->length = reply; + bh->state = BUF_STATE_FULL; + fsf->residue -= reply; + } /* Otherwise it's already set */ + + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +static int fsf_received_cbw(struct fsf *fsf, struct fsf_buffhd *bh) +{ + struct usb_request *req = bh->outreq; + struct bulk_cb_wrap *cbw = req->buf; + + /* Was this a real packet? Should it be ignored? */ + if (req->status || test_bit(IGNORE_BULK_OUT, &fsf->atomic_bitflags)) + return -EINVAL; + + /* Is the CBW valid? */ + if (req->actual != USB_BULK_CB_WRAP_LEN || + cbw->Signature != __constant_cpu_to_le32(USB_BULK_CB_SIG)) { + FDBG(fsf, "invalid CBW: len %u sig 0x%x\n", + req->actual, le32_to_cpu(cbw->Signature)); + + /* The Bulk-only spec says we MUST stall the IN endpoint + * (6.6.1), so it's unavoidable. It also says we must + * retain this state until the next reset, but there's + * no way to tell the controller driver it should ignore + * Clear-Feature(HALT) requests. + * + * We aren't required to halt the OUT endpoint; instead + * we can simply accept and discard any data received + * until the next reset. */ + fsf_wedge_bulk_in_endpoint(fsf); + set_bit(IGNORE_BULK_OUT, &fsf->atomic_bitflags); + return -EINVAL; + } + + /* Is the CBW meaningful? */ + if (cbw->Lun >= FSF_MAX_LUNS || cbw->Flags & ~USB_BULK_IN_FLAG || + cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) { + FDBG(fsf, "non-meaningful CBW: lun = %u, flags = 0x%x, " + "cmdlen %u\n", + cbw->Lun, cbw->Flags, cbw->Length); + + /* We can do anything we want here, so let's stall the + * bulk pipes if we are allowed to. */ + if (fsf_can_stall(fsf)) { + fsf_set_halt(fsf, fsf->bulk_out); + fsf_halt_bulk_in_endpoint(fsf); + } + return -EINVAL; + } + + /* Save the command for later */ + fsf->cmnd_size = cbw->Length; + memcpy(fsf->cmnd, cbw->CDB, fsf->cmnd_size); + if (cbw->Flags & USB_BULK_IN_FLAG) + fsf->data_dir = DATA_DIR_TO_HOST; + else + fsf->data_dir = DATA_DIR_FROM_HOST; + fsf->data_size = le32_to_cpu(cbw->DataTransferLength); + if (fsf->data_size == 0) + fsf->data_dir = DATA_DIR_NONE; + fsf->common->lun = cbw->Lun; + fsf->tag = cbw->Tag; + return 0; +} + + +static int fsf_get_next_command(struct fsf *fsf) +{ + struct fsf_buffhd *bh; + int rc = 0; + + if (transport_is_bbb(fsf)) { + /* Wait for the next buffer to become available */ + bh = fsf->common->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { + rc = fsf_sleep_thread(fsf); + if (rc) + return rc; + } + + /* Queue a request to read a Bulk-only CBW */ + set_bulk_out_req_length(fsf, bh, USB_BULK_CB_WRAP_LEN); + bh->outreq->short_not_ok = 1; + fsf_start_transfer(fsf, fsf->bulk_out, bh->outreq, + &bh->outreq_busy, &bh->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. */ + + /* Wait for the CBW to arrive */ + while (bh->state != BUF_STATE_FULL) { + rc = fsf_sleep_thread(fsf); + if (rc) + return rc; + } + smp_rmb(); + rc = fsf_received_cbw(fsf, bh); + bh->state = BUF_STATE_EMPTY; + +#ifdef CONFIG_USB_FSF_TEST + } else { /* US_PR_CB or US_PR_CBI */ + /* Wait for the next command to arrive */ + while (fsf->cbbuf_cmnd_size == 0) { + rc = fsf_sleep_thread(fsf); + if (rc) + return rc; + } + + /* Is the previous status interrupt request still busy? + * The host is allowed to skip reading the status, + * so we must cancel it. */ + if (fsf->intreq_busy) + usb_ep_dequeue(fsf->intr_in, fsf->intreq); + + /* Copy the command and mark the buffer empty */ + fsf->data_dir = DATA_DIR_UNKNOWN; + spin_lock_irq(&fsf->lock); + fsf->cmnd_size = fsf->cbbuf_cmnd_size; + memcpy(fsf->cmnd, fsf->cbbuf_cmnd, fsf->cmnd_size); + fsf->cbbuf_cmnd_size = 0; + spin_unlock_irq(&fsf->lock); +#endif + } + return rc; +} + + +/*-------------------------------------------------------------------------*/ + +static int fsf_enable_endpoint(struct fsf *fsf, struct usb_ep *ep, + const struct usb_endpoint_descriptor *d) +{ + int rc; + + ep->driver_data = fsf; + rc = usb_ep_enable(ep, d); + if (rc) + FERROR(fsf, "can't enable %s, result %d\n", ep->name, rc); + return rc; +} + +static int fsf_alloc_request(struct fsf *fsf, struct usb_ep *ep, + struct usb_request **preq) +{ + *preq = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (*preq) + return 0; + FERROR(fsf, "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 fsf_do_set_interface(struct fsf *fsf, int altsetting) +{ + struct usb_composite_dev *cdev = fsf->cdev; + const struct usb_endpoint_descriptor *d; + struct fsf_buffhd *bh; + struct fsf_lun *lun; + int i, rc = 0; + + if (fsf->running) + FDBG(fsf, "reset interface\n"); + +reset: + /* Deallocate the requests */ + FOREACH_BUFFHD(i, bh, fsf->common) { + if (bh->inreq) { + usb_ep_free_request(fsf->bulk_in, bh->inreq); + bh->inreq = NULL; + } + if (bh->outreq) { + usb_ep_free_request(fsf->bulk_out, bh->outreq); + bh->outreq = NULL; + } + } +#ifdef CONFIG_USB_FSF_TEST + if (fsf->intreq) { + usb_ep_free_request(fsf->intr_in, fsf->intreq); + fsf->intreq = NULL; + } +#endif + + /* Disable the endpoints */ + if (fsf->bulk_in_enabled) { + usb_ep_disable(fsf->bulk_in); + fsf->bulk_in_enabled = 0; + } + if (fsf->bulk_out_enabled) { + usb_ep_disable(fsf->bulk_out); + fsf->bulk_out_enabled = 0; + } +#ifdef CONFIG_USB_FSF_TEST + if (fsf->intr_in_enabled) { + usb_ep_disable(fsf->intr_in); + fsf->intr_in_enabled = 0; + } +#endif + + fsf->running = 0; + if (altsetting < 0 || rc != 0) + return rc; + + FDBG(fsf, "set interface %d\n", altsetting); + + /* Enable the endpoints */ + d = ep_desc(cdev->gadget, &fsf_fs_bulk_in_desc, &fsf_hs_bulk_in_desc); + rc = fsf_enable_endpoint(fsf, fsf->bulk_in, d); + if (rc != 0) + goto reset; + fsf->bulk_in_enabled = 1; + + d = ep_desc(cdev->gadget, &fsf_fs_bulk_out_desc, &fsf_hs_bulk_out_desc); + rc = fsf_enable_endpoint(fsf, fsf->bulk_out, d); + if (rc != 0) + goto reset; + fsf->bulk_out_enabled = 1; + fsf->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize); + clear_bit(IGNORE_BULK_OUT, &fsf->atomic_bitflags); + +#ifdef CONFIG_USB_FSF_TEST + if (transport_is_cbi(fsf)) { + d = ep_desc(cdev->gadget, + &fsf_fs_intr_in_desc, &fsf_hs_intr_in_desc); + rc = fsf_enable_endpoint(fsf, fsf->intr_in, d); + if (rc != 0) + goto reset; + fsf->intr_in_enabled = 1; + } +#endif + + /* Allocate the requests */ + FOREACH_BUFFHD(i, bh, fsf->common) { + rc = fsf_alloc_request(fsf, fsf->bulk_in, &bh->inreq); + if (rc != 0) + goto reset; + rc = fsf_alloc_request(fsf, fsf->bulk_out, &bh->outreq); + if (rc != 0) + goto reset; + bh->inreq->buf = bh->outreq->buf = bh->buf; + bh->inreq->context = bh->outreq->context = bh; + bh->inreq->complete = fsf_bulk_in_complete; + bh->outreq->complete = fsf_bulk_out_complete; + } +#ifdef CONFIG_USB_FSF_TEST + if (transport_is_cbi(fsf)) { + rc = fsf_alloc_request(fsf, fsf->intr_in, &fsf->intreq); + if (rc != 0) + goto reset; + fsf->intreq->complete = fsf_intr_in_complete; + } +#endif + + fsf->running = 1; + FOREACH_LUN(i, lun, fsf->common) + lun->unit_attention_data = SS_RESET_OCCURRED; + + return rc; +} + + +/* + * 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 fsf_do_set_config(struct fsf *fsf, u8 new_config) +{ + int rc = 0; + + /* Disable the single interface */ + if (fsf->config != 0) { + FDBG(fsf, "reset config\n"); + fsf->config = 0; + rc = fsf_do_set_interface(fsf, -1); + } + + /* Enable the interface */ + if (new_config != 0) { + fsf->config = new_config; + rc = fsf_do_set_interface(fsf, 0); + if (rc != 0) + fsf->config = 0; /* Reset on errors */ + else + FINFO(fsf, "config #%d\n", fsf->config); + } + + return rc; +} + + +/******************************* MAIN THREAD ******************************/ + + +static void fsf_handle_exception(struct fsf *fsf) +{ + unsigned int exception_req_tag; + enum fsf_state old_state; + struct fsf_buffhd *bh; + siginfo_t info; + u8 new_config; + int sig, i, rc; + + /* Clear the existing signals. Anything but SIGUSR1 is converted + * into a high-priority EXIT exception. */ + for (;;) { + sig = dequeue_signal_lock(current, ¤t->blocked, &info); + if (!sig) + break; + if (sig != SIGUSR1) { + if (fsf->state < FSF_STATE_EXIT) + FDBG(fsf, "Main thread exiting on signal\n"); + fsf_raise_exception(fsf, FSF_STATE_EXIT); + } + } + + /* Cancel all the pending transfers */ +#ifdef CONFIG_USB_FSF_TEST + if (fsf->intreq_busy) + usb_ep_dequeue(fsf->intr_in, fsf->intreq); +#endif + FOREACH_BUFFHD(i, bh, fsf->common) { + if (bh->inreq_busy) + usb_ep_dequeue(fsf->bulk_in, bh->inreq); + if (bh->outreq_busy) + usb_ep_dequeue(fsf->bulk_out, bh->outreq); + } + + /* Wait until everything is idle */ + for (;;) { +#ifdef CONFIG_USB_FSF_TEST + int num_active = fsf->intreq_busy; +#else + int num_active = 0; +#endif + FOREACH_BUFFHD(i, bh, fsf->common) + num_active += bh->inreq_busy + bh->outreq_busy; + if (num_active == 0) + break; + if (fsf_sleep_thread(fsf)) + return; + } + + /* Clear out the controller's fifos */ + if (fsf->bulk_in_enabled) + usb_ep_fifo_flush(fsf->bulk_in); + if (fsf->bulk_out_enabled) + usb_ep_fifo_flush(fsf->bulk_out); +#ifdef CONFIG_USB_FSF_TEST + if (fsf->intr_in_enabled) + usb_ep_fifo_flush(fsf->intr_in); +#endif + + /* Reset the I/O buffer states and pointers, the SCSI + * state, and the exception. Then invoke the handler. */ + spin_lock_irq(&fsf->lock); + + fsf->common->next_buffhd_to_fill = fsf->common->next_buffhd_to_drain = + fsf->common->buffhds; + FOREACH_BUFFHD(i, bh, fsf->common) + bh->state = BUF_STATE_EMPTY; + + exception_req_tag = fsf->exception_req_tag; + new_config = fsf->new_config; + old_state = fsf->state; + + if (old_state == FSF_STATE_ABORT_BULK_OUT) + fsf->state = FSF_STATE_STATUS_PHASE; + else { + struct fsf_lun *lun; + FOREACH_LUN(i, lun, fsf->common) { +#ifndef CONFIG_USB_FSF_REMOVABLE_OFF + lun->prevent_medium_removal = 0; +#endif + lun->sense_data = lun->unit_attention_data = + SS_NO_SENSE; + lun->sense_data_info = 0; + lun->info_valid = 0; + } + fsf->state = FSF_STATE_IDLE; + } + spin_unlock_irq(&fsf->lock); + + /* Carry out any extra actions required for the exception */ + switch (old_state) { + case FSF_STATE_ABORT_BULK_OUT: + fsf_send_status(fsf); + spin_lock_irq(&fsf->lock); + if (fsf->state == FSF_STATE_STATUS_PHASE) + fsf->state = FSF_STATE_IDLE; + spin_unlock_irq(&fsf->lock); + break; + + case FSF_STATE_RESET: + FINFO(fsf, "FSF_STATE_RESET"); + /* In case we were forced against our will to halt a + * bulk endpoint, clear the halt now. (The SuperH UDC + * requires this.) */ + if (test_and_clear_bit(IGNORE_BULK_OUT, &fsf->atomic_bitflags)) + usb_ep_clear_halt(fsf->bulk_in); + + if (transport_is_bbb(fsf)) { + if (fsf->ep0_req_tag == exception_req_tag) + /* Complete the status stage */ + fsf_ep0_queue(fsf); + + } else if (transport_is_cbi(fsf)) { + /* Status by interrupt pipe */ + fsf_send_status(fsf); + } + + /* Technically this should go here, but it would only be + * a waste of time. Ditto for the INTERFACE_CHANGE and + * CONFIG_CHANGE cases. */ + /* for (i = 0; i < fsf->common->nluns; ++i) */ + /*fsf->common->luns[i].unit_attention_data = + SS_RESET_OCCURRED; */ + break; + + case FSF_STATE_CONFIG_CHANGE: + rc = fsf_do_set_config(fsf, new_config); + if (fsf->ep0_req_tag != exception_req_tag) + break; + if (rc != 0) /* STALL on errors */ + fsf_set_halt(fsf, ep0(fsf)); + else /* Complete the status stage */ + fsf_ep0_queue(fsf); + break; + + case FSF_STATE_EXIT: + case FSF_STATE_TERMINATED: + fsf_do_set_config(fsf, 0); /* Free resources */ + spin_lock_irq(&fsf->lock); + fsf->state = FSF_STATE_TERMINATED; /* Stop the thread */ + spin_unlock_irq(&fsf->lock); + break; + + case FSF_STATE_COMMAND_PHASE: + case FSF_STATE_DATA_PHASE: + case FSF_STATE_STATUS_PHASE: + case FSF_STATE_IDLE: + break; + } +} + + +static int fsf_main_thread(void *fsf_) +{ + struct fsf *fsf = fsf_; + + /* Allow the thread to be killed by a signal, but set the signal mask + * to block everything but INT, TERM, KILL, and USR1. */ + allow_signal(SIGINT); + allow_signal(SIGTERM); + allow_signal(SIGKILL); + allow_signal(SIGUSR1); + + /* Allow the thread to be frozen */ + set_freezable(); + + /* Arrange for userspace references to be interpreted as kernel + * pointers. That way we can pass a kernel pointer to a routine + * that expects a __user pointer and it will work okay. */ + set_fs(get_ds()); + + /* The main loop */ + while (fsf->state != FSF_STATE_TERMINATED) { + if (fsf_exception_in_progress(fsf) || signal_pending(current)) { + fsf_handle_exception(fsf); + continue; + } + + if (!fsf->running) { + fsf_sleep_thread(fsf); + continue; + } + + if (fsf_get_next_command(fsf)) + continue; + + spin_lock_irq(&fsf->lock); + if (!fsf_exception_in_progress(fsf)) + fsf->state = FSF_STATE_DATA_PHASE; + spin_unlock_irq(&fsf->lock); + + if (fsf_do_scsi_command(fsf) || fsf_finish_reply(fsf)) + continue; + + spin_lock_irq(&fsf->lock); + if (!fsf_exception_in_progress(fsf)) + fsf->state = FSF_STATE_STATUS_PHASE; + spin_unlock_irq(&fsf->lock); + + if (fsf_send_status(fsf)) + continue; + + spin_lock_irq(&fsf->lock); + if (!fsf_exception_in_progress(fsf)) + fsf->state = FSF_STATE_IDLE; + spin_unlock_irq(&fsf->lock); + } + + spin_lock_irq(&fsf->lock); + fsf->thread_task = NULL; + spin_unlock_irq(&fsf->lock); + + clear_bit(REGISTERED, &fsf->atomic_bitflags); + /* XXX */ + + /* Let the unbind and cleanup routines know the thread has exited */ + complete_and_exit(&fsf->thread_notifier, 0); +} + + +/****************************** BACKING FILE ******************************/ + +/* If the next two routines are called while the gadget is registered, + * the caller must own fsf->common->filesem for writing. */ + +static int fsf_lun_open_file(struct fsf_lun *curlun, const char *filename) +{ + int ro; + struct file *filp = NULL; + int rc = -EINVAL; + struct inode *inode = NULL; + loff_t size; + loff_t num_sectors; + loff_t min_sectors; + + LINFO(curlun, "%s\n", __func__); + + /* R/W if we can, R/O if we must */ + ro = fsf_lun_is_readonly(curlun); + switch (ro) { + case 0: + filp = filp_open(filename, O_RDWR | O_LARGEFILE, 0); + if (-EROFS != PTR_ERR(filp)) + break; + ro = 1; + /* FALL THROUGH */ + default: + filp = filp_open(filename, O_RDONLY | O_LARGEFILE, 0); + } + if (IS_ERR(filp)) { + LINFO(curlun, "unable to open backing file: %s\n", filename); + return PTR_ERR(filp); + } + + if (!(filp->f_mode & FMODE_WRITE)) + ro = 1; + + if (filp->f_path.dentry) + inode = filp->f_path.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)) { + LINFO(curlun, "invalid file type: %s\n", filename); + goto out; + } + + /* If we can't read the file, it's no good. + * If we can't write the file, use it read-only. */ + if (!filp->f_op || !(filp->f_op->read || filp->f_op->aio_read)) { + LINFO(curlun, "file not readable: %s\n", filename); + 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) { + LINFO(curlun, "unable to find file size: %s\n", filename); + rc = (int) size; + goto out; + } + num_sectors = size >> 9; /* File size in 512-byte blocks */ + min_sectors = 1; + if (fsf_lun_is_cdrom(curlun)) { + num_sectors &= ~3; /* Reduce to a multiple of 2048 */ + min_sectors = 300*4; /* Smallest track is 300 frames */ + if (num_sectors >= 256*60*75*4) { + num_sectors = (256*60*75 - 1) * 4; + LINFO(curlun, "file too big: %s\n", filename); + LINFO(curlun, "using only first %d blocks\n", + (int) num_sectors); + } + } + if (num_sectors < min_sectors) { + LINFO(curlun, "file too small: %s\n", filename); + rc = -ETOOSMALL; + goto out; + } + + get_file(filp); +#ifndef CONFIG_USB_FSF_READONLY_ON + curlun->readonly = ro; +#endif + curlun->filp = filp; + curlun->file_length = size; + curlun->num_sectors = num_sectors; + LDBG(curlun, "open backing file: %s size: %lld num_sectors: %lld\n", + filename, size, num_sectors); + rc = 0; + +out: + filp_close(filp, current->files); + return rc; +} + + +static void fsf_lun_close_file(struct fsf_lun *lun) +{ + if (lun->filp) { + int rc; + + /* + * XXX: San: Ugly hack here added to ensure that + * our pages get synced to disk. + * Also drop caches here just to be extra-safe + */ + rc = vfs_fsync(lun->filp, lun->filp->f_path.dentry, 1); + if (rc < 0) + LERROR(lun, "Error syncing data (%d)\n", rc); + /* drop_pagecache and drop_slab are no longer available */ + /* drop_pagecache(); */ + /* drop_slab(); */ + + LDBG(lun, "close backing file\n"); + fput(lun->filp); + lun->filp = NULL; + } +} + + +/****************************** DEVICE ATTRIBS ******************************/ + + +static ssize_t fsf_show_ro(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", fsf_lun_is_readonly(fsf_lun_from_dev(dev))); +} + +static ssize_t fsf_show_file(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fsf_lun *curlun = fsf_lun_from_dev(dev); + struct fsf_common *common = dev_get_drvdata(dev); + char *p; + ssize_t rc; + + down_read(&common->filesem); + if (fsf_lun_is_file_open(curlun)) { /* Get the complete pathname */ + p = d_path(&curlun->filp->f_path, buf, PAGE_SIZE - 1); + if (IS_ERR(p)) + rc = PTR_ERR(p); + else { + rc = strlen(p); + memmove(buf, p, rc); + buf[rc] = '\n'; /* Add a newline */ + buf[++rc] = 0; + } + } else { /* No file, return 0 bytes */ + *buf = 0; + rc = 0; + } + up_read(&common->filesem); + return rc; +} + +#ifndef CONFIG_USB_FSF_READONLY_ON +static ssize_t fsf_store_ro(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fsf_lun *curlun = fsf_lun_from_dev(dev); + struct fsf_common *common = dev_get_drvdata(dev); + ssize_t rc = count; + int i; + + if (fsf_lun_is_cdrom(curlun)) + return -EPERM; + + if (sscanf(buf, "%d", &i) != 1) + return -EINVAL; + + /* Allow the write-enable status to change while the backing + file is closed. */ + down_read(&common->filesem); + if (fsf_lun_is_file_open(curlun)) { + LDBG(curlun, "read-only status change prevented\n"); + rc = -EBUSY; + } else { + curlun->readonly = !!i; + LDBG(curlun, "read-only status set to %d\n", + fsf_lun_is_readonly(curlun)); + } + up_read(&common->filesem); + return rc; +} +#endif + +#ifndef CONFIG_USB_FSF_REMOVABLE_OFF +static ssize_t fsf_store_file(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fsf_lun *curlun = fsf_lun_from_dev(dev); + struct fsf_common *common = dev_get_drvdata(dev); + int rc = 0; + + LDBG(curlun, "store_file: \"%s\"\n", buf); + + if (!fsf_lun_is_removable(curlun)) + return -EPERM; + + if (curlun->prevent_medium_removal && fsf_lun_is_file_open(curlun)) { + LDBG(curlun, "eject attempt prevented\n"); + return -EBUSY; /* "Door is locked" */ + } + + /* Remove a trailing newline */ + if (count > 0 && buf[count-1] == '\n') + ((char *) buf)[count-1] = 0; + + /* Eject current medium */ + down_write(&common->filesem); + if (fsf_lun_is_file_open(curlun)) { + fsf_lun_close_file(curlun); + curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT; + } + + /* Load new medium */ + if (count > 0 && buf[0]) { + rc = fsf_lun_open_file(curlun, buf); + if (rc == 0) + curlun->unit_attention_data = + SS_NOT_READY_TO_READY_TRANSITION; + } + up_write(&common->filesem); + return (rc < 0 ? rc : count); +} +#endif + +/* Write is checked per LUN in store_*() functions. */ +#ifdef CONFIG_USB_FSF_READONLY_ON +static DEVICE_ATTR(ro, 0444, fsf_show_ro, NULL); +#else +static DEVICE_ATTR(ro, 0644, fsf_show_ro, fsf_store_ro); +#endif + +#ifndef CONFIG_USB_FSF_REMOVABLE_OFF +static DEVICE_ATTR(file, 0644, fsf_show_file, fsf_store_file); +#else +static DEVICE_ATTR(file, 0444, fsf_show_file, NULL); +#endif + + +/****************************** BIND & UNBIND ******************************/ + + +static void fsf_release(struct fsf *fsf) +{ + file_storage_function_release_common(fsf->common); + kfree(fsf); +} + +static void fsf_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct fsf *fsf = fsf_from_func(f); + + FDBG(fsf, "unbind\n"); + clear_bit(REGISTERED, &fsf->atomic_bitflags); + + /* If the thread isn't already dead, tell it to exit now */ + if (fsf->state != FSF_STATE_TERMINATED) { + fsf_raise_exception(fsf, FSF_STATE_EXIT); + wait_for_completion(&fsf->thread_notifier); + + /* The cleanup routine waits for this completion also */ + complete(&fsf->thread_notifier); + } + + fsf_release(fsf); +} + + +int fsf_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct fsf *fsf = fsf_from_func(f); + struct usb_ep *ep; + int rc, i, id; + + +#ifdef CONFIG_USB_FSF_CAN_STALL_SUPPORT + /* Some peripheral controllers are known not to be able to + * halt bulk endpoints correctly. If one of them is present, + * disable stalls. */ + if (fsf->can_stall && + (gadget_is_sh(fsf->cdev->gadget) || + gadget_is_at91(fsf->cdev->gadget))) + fsf->can_stall = 0; +#endif + + /* Find all the endpoints we will use */ + id = usb_interface_id(c, f); + if (id < 0) + return id; + fsf_intf_desc.bInterfaceNumber = id; + + ep = usb_ep_autoconfig(fsf->cdev->gadget, &fsf_fs_bulk_in_desc); + if (!ep) + goto autoconf_fail; + ep->driver_data = fsf; /* claim the endpoint */ + fsf->bulk_in = ep; + + ep = usb_ep_autoconfig(fsf->cdev->gadget, &fsf_fs_bulk_out_desc); + if (!ep) + goto autoconf_fail; + ep->driver_data = fsf; /* claim the endpoint */ + fsf->bulk_out = ep; + +#ifdef CONFIG_USB_FSF_TEST + if (transport_is_cbi(fsf)) { + ep = usb_ep_autoconfig(fsf->cdev->gadget, &fsf_fs_intr_in_desc); + if (!ep) + goto autoconf_fail; + ep->driver_data = fsf; /* claim the endpoint */ + fsf->intr_in = ep; + } +#endif + + +#ifdef CONFIG_USB_FSF_TEST + i = (transport_is_cbi(fsf) ? 3 : 2); /* Number of endpoints */ + fsf_intf_desc.bNumEndpoints = i; + fsf_intf_desc.bInterfaceSubClass = fsf_protocol_type(fsf); + fsf_intf_desc.bInterfaceProtocol = fsf_transport_type(fsf); + fsf_fs_function[i + FSF_FS_FUNCTION_PRE_EP_ENTRIES] = NULL; +#endif + + if (gadget_is_dualspeed(fsf->cdev->gadget)) { + /* Assume endpoint addresses are the same for both speeds */ + /* It is tempting to remove those assigments and + initialize the fsf_hs_*.bEndpointAddress in the + "correct" (ie. the same fsf_fs_*.bEndpointAddress) + way. Don't get fooled however! + fsf_fs_*.bEndpointAddress is updated when + usb_ep_autoconfig() is called! */ + fsf_hs_bulk_in_desc.bEndpointAddress = + fsf_fs_bulk_in_desc.bEndpointAddress; + fsf_hs_bulk_out_desc.bEndpointAddress = + fsf_fs_bulk_out_desc.bEndpointAddress; +#ifdef CONFIG_USB_FSF_TEST + fsf_hs_intr_in_desc.bEndpointAddress = + fsf_fs_intr_in_desc.bEndpointAddress; + + fsf_hs_function[i + FSF_HS_FUNCTION_PRE_EP_ENTRIES] = NULL; +#endif + f->hs_descriptors = fsf_hs_function; + } + + + /* maybe allocate device-global string IDs, and patch descriptors */ + if (fsf_string_defs[FSF_STRING_INTERFACE_IDX].id == 0) { + i = usb_string_id(c->cdev); + if (i < 0) + return i; + fsf_string_defs[FSF_STRING_INTERFACE_IDX].id = i; + fsf_intf_desc.iInterface = i; + } + + + fsf->thread_task = kthread_create(fsf_main_thread, fsf, + "file-storage"); + if (IS_ERR(fsf->thread_task)) { + rc = PTR_ERR(fsf->thread_task); + FERROR(fsf, "kthread_create failed: %d\n", rc); + goto out; + } + + FINFO(fsf, DRIVER_DESC ", version: " DRIVER_VERSION + "; Number of LUNs: %d\n", fsf->common->nluns); + + FDBG(fsf, "transport=%s (x%02x)\n", + fsf_transport_name(fsf), fsf_transport_type(fsf)); + FDBG(fsf, "protocol=%s (x%02x)\n", + fsf_protocol_name(fsf), fsf_protocol_type(fsf)); + FDBG(fsf, "removable=%d, stall=%d, cdrom=%d, buflen=%u\n", + fsf_lun_is_removable(fsf->common->curlun), fsf_can_stall(fsf), + fsf_lun_is_cdrom(fsf->common->curlun), fsf_buflen); + FDBG(fsf, "I/O thread pid: %d\n", task_pid_nr(fsf->thread_task)); + + set_bit(REGISTERED, &fsf->atomic_bitflags); + + /* Tell the thread to start working */ + wake_up_process(fsf->thread_task); + return 0; + +autoconf_fail: + FERROR(fsf, "unable to autoconfigure all endpoints\n"); + rc = -ENOTSUPP; + +out: + fsf->state = FSF_STATE_TERMINATED; /* The thread is dead */ + fsf_unbind(c, f); + complete(&fsf->thread_notifier); + return rc; +} + + +/****************************** ALT CONFIGS ******************************/ + + +static int fsf_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct fsf *fsf = fsf_from_func(f); + fsf->new_config = 1; + fsf_raise_exception(fsf, FSF_STATE_CONFIG_CHANGE); + return 0; +} + +static void fsf_disable(struct usb_function *f) +{ + struct fsf *fsf = fsf_from_func(f); + fsf->new_config = 0; + fsf_raise_exception(fsf, FSF_STATE_CONFIG_CHANGE); +} + + +/****************************** FSF COMMON ******************************/ + +static void fsf_lun_release(struct device *dev) +{ + (void)dev; + /* nop */ +} + + +int file_storage_function_init_common(struct usb_composite_dev *cdev, + struct fsf_common_config *cfg, + struct fsf_common **fsf_common) +{ + struct fsf_common *common; + struct fsf_buffhd *bh; + struct fsf_lun *lun; + int nluns, rc, i; + + + nluns = cfg->nluns; + if (nluns < 1 || nluns > FSF_MAX_LUNS) { + ERROR(cdev, "invalid number of LUNs: %u\n", nluns); + return -EINVAL; + } + + + common = kmalloc(sizeof *common, GFP_KERNEL); + if (!common) + return -ENOMEM; + + + common->nluns = 0; /* so release won't touch nluns */ + /* Allocate the data buffers */ + FOREACH_BUFFHD(i, bh, common) { + /* Allocate for the bulk-in endpoint. We assume that + * the buffer will also work with the bulk-out (and + * interrupt-in) endpoint. */ + bh->buf = kmalloc(fsf_buflen, GFP_KERNEL); + if (!bh->buf) { + rc = -ENOMEM; + goto error_put; + } + bh->next = bh + 1; + } + common->buffhds[NUM_BUFFERS - 1].next = &common->buffhds[0]; + + + /* Create the LUNs, open their backing files, and register the + * LUN devices in sysfs. */ + common->luns = lun = kzalloc(nluns * sizeof *common->luns, GFP_KERNEL); + if (!lun) { + rc = -ENOMEM; + goto error_free_common; + } + + kref_init(&common->ref); + + for (i = 0; i < nluns; ++i, ++lun) { +#ifdef CONFIG_USB_FSF_CDROM_CONFIG + lun->cdrom = cfg->luns[i].cdrom; +#endif +#ifdef CONFIG_USB_FSF_REMOVABLE_CONFIG + lun->removable = lun->cdrom || cfg->luns[i].removable; +#endif +#ifndef CONFIG_USB_FSF_READONLY_ON + lun->readonly = lun->cdrom || cfg->luns[i].readonly; +#endif + lun->dev.release = fsf_lun_release; + lun->dev.parent = &cdev->gadget->dev; + dev_set_drvdata(&lun->dev, common); + dev_set_name(&lun->dev, "lun%d", i); + + if (!fsf_lun_is_removable(lun) && !cfg->luns[i].filename) { + ERROR(cdev, "no file given for LUN%d\n", i); + rc = -EINVAL; + common->nluns = lun - common->luns; + goto error_put; + } + + rc = device_register(&lun->dev); + if (rc != 0) { + LINFO(lun, "failed to register LUN%d: %d\n", i, rc); + common->nluns = lun - common->luns; + goto error_put; + } + lun->registered = 1; + + if ((rc = device_create_file(&lun->dev, &dev_attr_ro )) != 0 || + (rc = device_create_file(&lun->dev, &dev_attr_file)) != 0 || + (cfg->luns[i].filename && + (rc = fsf_lun_open_file(lun, cfg->luns[i].filename)))) { + common->nluns = lun - common->luns + 1; + goto error_put; + } + } + common->nluns = nluns; + common->lun = 0; + common->curlun = 0; + + + common->vendor = cfg->vendor; + common->product = cfg->product; + + if (cfg->release == 0xffff) { /* Parameter wasn't set */ + int gcnum; + /* The sa1100 controller is not supported */ + if (gadget_is_sa1100(cdev->gadget)) + gcnum = -1; + else + gcnum = usb_gadget_controller_number(cdev->gadget); + if (gcnum >= 0) + common->release = 0x0300 + gcnum; + else { + LWARN(lun, "controller '%s' not recognized\n", + cdev->gadget->name); + common->release = 0x0399; + } + } else { + common->release = cfg->release; + } + + + + init_rwsem(&common->filesem); + *fsf_common = common; + + return 0; + + + error_put: + /* Here, common->nluns may differ from cfg->nluns as it is set + to the number of registered LUNs prior to going to this + label. */ + file_storage_function_release_common(common); + return rc; + + error_free_common: + kfree(common); + return rc; +} + + + +static void fsf_common_release(struct kref *kref) +{ + struct fsf_common *common = container_of(kref, struct fsf_common, ref); + struct fsf_buffhd *bh; + struct fsf_lun *lun; + int i; + + /* nluns may be zero when called from + file_storage_function_init_common() */ + FOREACH_LUN(i, lun, common) { + if (lun->registered) { + device_remove_file(&lun->dev, &dev_attr_ro); + device_remove_file(&lun->dev, &dev_attr_file); + fsf_lun_close_file(lun); + device_unregister(&lun->dev); + lun->registered = 0; + } + } + + /* Free the data buffers */ + FOREACH_BUFFHD(i, bh, common) + kfree(bh->buf); + + kfree(common->luns); + kfree(common); +} + +int file_storage_function_release_common(struct fsf_common *fsf_common) +{ + if (fsf_common) + kref_put(&fsf_common->ref, fsf_common_release); + return 0; +} + + + +/****************************** ADD FUNCTION ******************************/ + + +int file_storage_function_add(struct usb_composite_dev *cdev, + struct usb_configuration *c, + struct fsf_config *cfg, + struct fsf_common *common) +{ + struct fsf *fsf; + int rc; + +#ifdef CONFIG_USB_FSF_TEST + int trans_type, proto_type; + const char *trans_name, *proto_name; +#endif + +#ifdef CONFIG_USB_FSF_TEST + /* Default values */ + trans_type = US_PR_BULK; + trans_name = "Bulk-only"; + proto_type = US_SC_SCSI; + proto_name = "Transparent SCSI"; + + if (cfg->transport) { + if (strnicmp(cfg->transport, "BBB", 10) == 0) { + ; /* Use default setting */ + } else if (strnicmp(cfg->transport, "CB", 10) == 0) { + trans_type = US_PR_CB; + trans_name = "Control-Bulk"; + } else if (strnicmp(cfg->transport, "CBI", 10) == 0) { + trans_type = US_PR_CBI; + trans_name = "Control-Bulk-Interrupt"; + } else { + ERROR(cdev, "invalid transport: %s\n", + cfg->transport); + return -EINVAL; + } + } + + if (cfg->protocol) { + unsigned long prot; + if (strict_strtoul(cfg->protocol, 0, &prot)) + prot = 0xffffffff; + + if (strnicmp(cfg->protocol, "SCSI", 10) == 0 || + prot == US_SC_SCSI) { + /* Use default setting */ + } else if (strnicmp(cfg->protocol, "RBC", 10) == 0 || + prot == US_SC_RBC) { + proto_type = US_SC_RBC; + proto_name = "RBC"; + } else if (strnicmp(cfg->protocol, "8020", 4) == 0 || + strnicmp(cfg->protocol, "ATAPI", 10) == 0 || + prot == US_SC_8020) { + proto_type = US_SC_8020; + proto_name = "8020i (ATAPI)"; + } else if (strnicmp(cfg->protocol, "QIC", 3) == 0 || + prot == US_SC_QIC) { + proto_type = US_SC_QIC; + proto_name = "QIC-157"; + } else if (strnicmp(cfg->protocol, "UFI", 10) == 0 || + prot == US_SC_UFI) { + proto_type = US_SC_UFI; + proto_name = "UFI"; + } else if (strnicmp(cfg->protocol, "8070", 4) == 0 || + prot == US_SC_8070) { + proto_type = US_SC_8070; + proto_name = "8070i"; + } else { + ERROR(cdev, "invalid protocol: %s\n", cfg->protocol); + return -EINVAL; + } + } + + if (_fsf_buflen <= 0) { + ERROR(cdev, "invalid buflen: %d\n", _fsf_buflen); + return -ETOOSMALL; + } + _fsf_buflen &= PAGE_CACHE_MASK; +#endif + + fsf = kzalloc(sizeof *fsf, GFP_KERNEL); + if (unlikely(!fsf)) + return -ENOMEM; + + spin_lock_init(&fsf->lock); + init_completion(&fsf->thread_notifier); + + fsf->cdev = cdev; + fsf->function.name = DRIVER_DESC; + fsf->function.strings = fsf_strings; + fsf->function.descriptors = fsf_fs_function; + fsf->function.bind = fsf_bind; + fsf->function.unbind = fsf_unbind; + fsf->function.setup = fsf_setup; + fsf->function.set_alt = fsf_set_alt; + fsf->function.disable = fsf_disable; + + fsf->common = common; + kref_get(&common->ref); +#ifdef CONFIG_USB_FSF_CAN_STALL_SUPPORT + fsf->can_stall = fsf_can_stall; +#endif + +#ifdef CONFIG_USB_FSF_TEST + fsf->transport_type = trans_type; + fsf->transport_name = trans_name; + fsf->protocol_type = proto_type; + fsf->protocol_name = proto_name; +#endif + + rc = usb_add_function(c, &fsf->function); + + if (unlikely(rc != 0)) + fsf_release(fsf); + + return rc; +} diff --git a/drivers/usb/gadget/f_file_storage.h b/drivers/usb/gadget/f_file_storage.h new file mode 100644 index 0000000..ad56528 --- /dev/null +++ b/drivers/usb/gadget/f_file_storage.h @@ -0,0 +1,356 @@ +/* + * f_file_storage.h -- File-backed USB Storage Function, for USB development + * + * Copyright (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz <m.nazarewicz@xxxxxxxxxxx> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* + * Overview + * + * The File-backed Storage Function (or FSF) acts as a USB Mass + * Storage device, appearing to the host as a disk drive or as + * a CD-ROM drive. In addition to providing an example of a genuinely + * useful function driver for a USB device, it also illustrates + * a technique of double-buffering for increased throughput. Last but + * not least, it gives an easy way to probe the behavior of the Mass + * Storage drivers in a USB host. + * + * Backing storage is provided by a regular file or a block device. + * Access can be limited to read-only. Moreover, the function can be + * configured to indicate that it has removable media. (For CD-ROM + * emulation, access is always read-only and function reports having + * removable media.) + * + * 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 also supports the following protocols: RBC (0x01), ATAPI or + * SFF-8020i (0x02), QIC-157 (0c03), UFI (0x04), SFF-8070i (0x05), and + * transparent SCSI (0x06), selected by the optional "protocol" module + * parameter. In addition, the default Vendor ID, Product ID, and + * release number can be overridden. + * + * There is support for multiple logical units (LUNs), each of which + * has its own backing file as well as access, CD-ROM simulation and + * removable media mode configuration. The number of LUNs can be set + * when gadget configures the FSF. + * + * 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. + * + * Note that the driver is slightly non-portable in that it assumes a + * single memory/DMA buffer will be useable for bulk-in, bulk-out, and + * interrupt-in endpoints. With most device controllers this isn't an + * issue, but there may be some with hardware restrictions that prevent + * a buffer from being used by more than one endpoint. + * + * This gadget driver is heavily based on "Gadget Zero" by David Brownell. + * The driver's SCSI command interface was based on the "Information + * technology - Small Computer System Interface - 2" document from + * X3T9.2 Project 375D, Revision 10L, 7-SEP-93, available at + * <http://www.t10.org/ftp/t10/drafts/s2/s2-r10l.pdf>. The single exception + * is opcode 0x23 (READ FORMAT CAPACITIES), which was based on the + * "Universal Serial Bus Mass Storage Class UFI Command Specification" + * document, Revision 1.0, December 14, 1998, available at + * <http://www.usb.org/developers/devclass_docs/usbmass-ufi10.pdf>. + */ + + +/* + * Configuration & Usage + * + * Prior to adding FSF to USB configuration a struct fsf_common needs + * to be created. This is done using + * file_storage_function_init_common() function. As one of its + * arguments it takes pointer to fsf_common_config structure. This + * structure lets you configure various settings of the function: + * + * nluns Specifies number of LUNs the function will use. Each + * LUN should have acompaning entry in luns array. + * This can be any integer from 1 to FSF_MAX_LUNS + * (which is 8). + * luns Array of nluns elements with configuration for each + * LUN. + * + * vendor Specifies what vendor FSF will report to USB host. + * May be set to NULL. Only present if + * CONFIG_USB_FSF_TEST is set. + * product Specifies the name of product FSF will report to USB + * host. May be set to NULL. Only present if + * CONFIG_USB_FSF_TEST is set. + * release Specifies the release number FSF will report to USB + * host. Set to 0xfff to let FSF guess it + * automatically. + * + * The luns array specifies configuration for each LUN. It's an array + * of fsf_lun_config structures. Each of them lets you configure the + * following: + * + * filename Specifies path to LUN's backing file. May be NULL + * unless LUN is not removable. + * readonly Specifies whether LUN is readonly. Option is not + * present if CONFIG_USB_FSF_READONLY_ON is set in + * which case it is assumed true. + * removable Specifies whether LUN is removable media. Option is + * present only if CONFIG_USB_FSF_REMOVABLE_CONFIG + * is set. If CONFIG_USB_FSF_REMOVABLE_ON is set + * this flag is assumed true and if + * CONFIG_USB_FSF_REMOVABLE_OFF is set this flag is + * assumed false. If it is false, filename must be + * specified. + * cdrom Specifies whether LUN simulates a CD-ROM. Option is + * present only if CONFIG_USB_FSF_CDROM_CONFIG is + * set. If CONFIG_USB_FSF_CDROM_ON is set this flag + * is assumed true and if CONFIG_USB_FSF_CDROM_OFF + * is set this flag is assumed false. If it is + * true, removable and readonly is implied. + * + * + * After creating a fsf_common object by invoking + * file_storage_function_init_common() you may add FSF to USB + * configuration. To do so, you call file_storage_function_add(). As + * one of it's arguments it takes a fsf_config structure which lets + * you configure further options (but only if CONFIG_USB_FSF_TEST is + * set): + * + * transport Specifies transport name (CB, CBI, or BBB), default + * BBB. + * protocol Specifies protocol name (RBC, 8020 or ATAPI, QIC, + * UFI, 8070, or SCSI; also 1 - 6), default SCSI + * + * Because some of the structure's fields are present only if some + * macros are defined you can use file_storage_cfg_set_* functions. + * + * When you finish adding File-backing Storage Function to various (or + * one) USB Configuration(s) you shall call + * file_storage_function_release_common() function. There's no need + * to wait in calling this function when gadget is removed, you can + * (and should so you won't forget about it ;) ) do it right away. + * + * + * In addition to configuration from gadget with the structures + * described above. The function defines the following two module + * parameters: + * + * stall Default determined according to the type of USB + * device controller (usually true), boolean to + * permit the driver to halt bulk endpoints. Only + * present if CONFIG_USB_FSF_CAN_STALL_SUPPORT is + * defined. + * buflen=N Default N=16384, buffer size used (will be rounded + * down to a multiple of PAGE_CACHE_SIZE). Only + * present if CONFIG_USB_FSF_TEST is set. + * + * Gadget module may add its own module parameters which may be mapped + * to the configuration structures desribed above. You should consult + * the gadget's documentation. On the other hand, if you are gadget + * developer you may look into f_file_storage_params.h file as it adds + * all the parameters one may want. + * + * + * The pathnames of the backing files and the ro settings are available in + * the attribute files "file" and "ro" in the lun<n> subdirectory of the + * gadget's sysfs directory. If the "removable" option is set, writing to + * these files will simulate ejecting/loading the medium (writing an empty + * line means eject) and adjusting a write-enable tab. Changes to the ro + * setting are not allowed when the medium is loaded or if CD-ROM emulation + * is being used. + * + */ + + +/* + * Driver Design + * + * The FSF driver is fairly straightforward. There is a main kernel + * thread that handles most of the work. Interrupt routines field + * callbacks from the controller driver: bulk- and interrupt-request + * completion notifications, endpoint-0 events, and disconnect events. + * Completion events are passed to the main thread by wakeup calls. Many + * ep0 requests are handled at interrupt time, but SetInterface, + * SetConfiguration, and device reset requests are forwarded to the + * thread in the form of "exceptions" using SIGUSR1 signals (since they + * should interrupt any ongoing file I/O operations). + * + * The thread's main routine implements the standard command/data/status + * parts of a SCSI interaction. It and its subroutines are full of tests + * for pending signals/exceptions -- all this polling is necessary since + * the kernel has no setjmp/longjmp equivalents. (Maybe this is an + * indication that the driver really wants to be running in userspace.) + * An important point is that so long as the thread is alive it keeps an + * open reference to the backing file. This will prevent unmounting + * the backing file's underlying filesystem and could cause problems + * during system shutdown, for example. To prevent such problems, the + * thread catches INT, TERM, and KILL signals and converts them into + * an EXIT exception. + * + * In normal operation the main thread is started during the gadget's + * fsf_bind() callback and stopped during fsf_unbind(). But it can also + * exit when it receives a signal, and there's no point leaving the + * gadget running when the thread is dead. So just before the thread + * exits, it deregisters the gadget driver. This makes things a little + * tricky: The driver is deregistered at two places, and the exiting + * thread can indirectly call fsf_unbind() which in turn can tell the + * thread to exit. The first problem is resolved through the use of the + * REGISTERED atomic bitflag; the driver will only be deregistered once. + * The second problem is resolved by having fsf_unbind() check + * fsf->state; it won't try to stop the thread if the state is already + * FSF_STATE_TERMINATED. + * + * To provide maximum throughput, the driver uses a circular pipeline of + * buffer heads (struct fsf_buffhd). In principle the pipeline can be + * arbitrarily long; in practice the benefits don't justify having more + * than 2 stages (i.e., double buffering). But it helps to think of the + * pipeline as being a long one. Each buffer head contains a bulk-in and + * a bulk-out request pointer (since the buffer can be used for both + * output and input -- directions always are given from the host's + * point of view) as well as a pointer to the buffer and various state + * variables. + * + * Use of the pipeline follows a simple protocol. There is a variable + * (fsf->next_buffhd_to_fill) that points to the next buffer head to use. + * At any time that buffer head may still be in use from an earlier + * request, so each buffer head has a state variable indicating whether + * it is EMPTY, FULL, or BUSY. Typical use involves waiting for the + * buffer head to be EMPTY, filling the buffer either by file I/O or by + * USB I/O (during which the buffer head is BUSY), and marking the buffer + * head FULL when the I/O is complete. Then the buffer will be emptied + * (again possibly by USB I/O, during which it is marked BUSY) and + * finally marked EMPTY again (possibly by a completion routine). + * + * A module parameter tells the driver to avoid stalling the bulk + * endpoints wherever the transport specification allows. This is + * necessary for some UDCs like the SuperH, which cannot reliably clear a + * halt on a bulk endpoint. However, under certain circumstances the + * Bulk-only specification requires a stall. In such cases the driver + * will halt the endpoint and set a flag indicating that it should clear + * the halt in software during the next device reset. Hopefully this + * will permit everything to work correctly. Furthermore, although the + * specification allows the bulk-out endpoint to halt when the host sends + * too much data, implementing this would cause an unavoidable race. + * The driver will always use the "no-stall" approach for OUT transfers. + * + * One subtle point concerns sending status-stage responses for ep0 + * requests. Some of these requests, such as device reset, can involve + * interrupting an ongoing file I/O operation, which might take an + * arbitrarily long time. During that delay the host might give up on + * the original ep0 request and issue a new one. When that happens the + * driver should not notify the host about completion of the original + * request, as the host will no longer be waiting for it. So the driver + * assigns to each ep0 request a unique tag, and it keeps track of the + * tag value of the request associated with a long-running exception + * (device-reset, interface-change, or configuration-change). When the + * exception handler is finished, the status-stage response is submitted + * only if the current ep0 request tag is equal to the exception request + * tag. Thus only the most recently received ep0 request will get a + * status-stage response. + * + * Warning: This driver source file is too long. It ought to be split up + * into a header file plus about 3 separate .c files, to handle the details + * of the Gadget, USB Mass Storage, and SCSI protocols. + */ + + +#ifndef __KERNEL_F_FILE_STORAGE_H +#define __KERNEL_F_FILE_STORAGE_H + + +#define FSF_MAX_LUNS 8 + + +struct usb_composite_dev; +struct usb_configuration; +struct fsf_common; + + +struct fsf_lun_config { + char *filename; + +#ifndef CONFIG_USB_FSF_READONLY_ON + unsigned int readonly:1; +#endif +#ifdef CONFIG_USB_FSF_REMOVABLE_CONFIG + unsigned int removable:1; +#endif +#ifdef CONFIG_USB_FSF_CDROM_CONFIG + unsigned int cdrom:1; +#endif +}; + +struct fsf_common_config { + unsigned nluns; + struct fsf_lun_config *luns; + + const char *vendor; + const char *product; + + u16 release; +}; + + +struct fsf_config { +#ifdef CONFIG_USB_FSF_TEST + const char *protocol; + const char *transport; +#endif +}; + + +int file_storage_function_init_common(struct usb_composite_dev *cdev, + struct fsf_common_config *cfg, + struct fsf_common **fsf_common); +int file_storage_function_release_common(struct fsf_common *fsf_common); + +int file_storage_function_add(struct usb_composite_dev *cdev, + struct usb_configuration *c, + struct fsf_config *cfg, + struct fsf_common *fsf_common); + + +#ifdef CONFIG_USB_FSF_TEST +# define file_storage_cfg_set_proto(cfg, proto, trans) \ + (((cfg)->protocol = (proto)), ((cfg)->transport = (trans))) +#else +# define file_storage_cfg_set_proto(cfg, proto, trans) do { } while (0) +#endif + +#ifndef CONFIG_USB_FSF_READONLY_ON +# define file_storage_cfg_set_readonly(lun, value) \ + ((lun)->readonly = !!(value)) +#else +# define file_storage_cfg_set_readonly(lun, value) do { } while (0) +#endif + +#ifdef CONFIG_USB_FSF_REMOVABLE_CONFIG +# define file_storage_cfg_set_removable(lun, value) \ + ((lun)->removable = !!(value)) +#else +# define file_storage_cfg_set_removable(lun, value) do { } while (0) +#endif + +#ifdef CONFIG_USB_FSF_CDROM_CONFIG +# define file_storage_cfg_set_cdrom(lun, value) \ + ((lun)->cdrom = !!(value)) +#else +# define file_storage_cfg_set_cdrom(lun, value) do { } while (0) +#endif + +#endif diff --git a/drivers/usb/gadget/f_file_storage_params.c b/drivers/usb/gadget/f_file_storage_params.c new file mode 100644 index 0000000..db0b2fe --- /dev/null +++ b/drivers/usb/gadget/f_file_storage_params.c @@ -0,0 +1,182 @@ +/* + * f_file_storage_params.c -- File-backed USB Storage Function + * + * Copyright (C) 2003-2008 Alan Stern + * All rights reserved. + * + * Copyright (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz <m.nazarewicz@xxxxxxxxxxx> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* See comments in f_file_storage_params.h file. */ + +#include <linux/fs.h> + +#include "f_file_storage_params.h" + + +static struct { + char *file[FSF_MAX_LUNS]; + unsigned int file_count; + +#ifndef CONFIG_USB_FSF_READONLY_ON + int ro[FSF_MAX_LUNS]; + unsigned int ro_count; +#endif + +#ifdef CONFIG_USB_FSF_REMOVABLE_CONFIG + int removable[FSF_MAX_LUNS]; + unsigned int removable_count; +#endif + +#ifdef CONFIG_USB_FSF_CDROM_CONFIG + int cdrom[FSF_MAX_LUNS]; + unsigned int cdrom_count; +#endif + + unsigned int nluns; + +#ifdef CONFIG_USB_FSF_CAN_STALL_SUPPORT + int can_stall; +#endif + +#ifdef CONFIG_USB_FSF_TEST + char *transport_parm; + char *protocol_parm; + unsigned short release; +#endif + +} fsf_module_params = { /* Default values */ + .nluns = 1, + + .file_count = 0, + .ro_count = 0, + .removable_count = 0, + .cdrom_count = 0, + +#ifdef CONFIG_USB_FSF_CAN_STALL_SUPPORT + .can_stall = 1, +#endif + +#ifdef CONFIG_USB_FSF_TEST + .transport_parm = 0, + .protocol_parm = 0, + .release = 0xffff, /* Use controller chip type */ +#endif + }; + + +#define P(name, type, desc) \ + module_param_array_named(name, fsf_module_params.name, type, \ + &fsf_module_params.name ##_count, S_IRUGO); \ + MODULE_PARM_DESC(name, desc) + +P(file, charp, "names of backing files or devices"); +#ifndef CONFIG_USB_FSF_READONLY_ON +P(ro, bool, "true to force read-only"); +#endif +#ifdef CONFIG_USB_FSF_REMOVABLE_CONFIG +P(removable, bool, "true to simulate removable"); +#endif +#ifdef CONFIG_USB_FSF_CDROM_CONFIG +P(cdrom, bool, "true to emulate CD-ROM instead of disk"); +#endif + +#undef P + +module_param_named(luns, fsf_module_params.nluns, uint, S_IRUGO); +MODULE_PARM_DESC(luns, "number of LUNs"); + + +/* In the non-TEST version, only the module parameters listed above + * are available. */ +#ifdef CONFIG_USB_FSF_TEST + +module_param_named(transport, fsf_module_params.transport_parm, charp, S_IRUGO); +MODULE_PARM_DESC(transport, "type of transport (BBB, CBI, or CB)"); + +module_param_named(protocol, fsf_module_params.protocol_parm, charp, S_IRUGO); +MODULE_PARM_DESC(protocol, "type of protocol (RBC, 8020, QIC, UFI, " + "8070, or SCSI)"); + +module_param_named(release, fsf_module_params.release, ushort, S_IRUGO); +MODULE_PARM_DESC(release, "USB release number"); + +#endif /* CONFIG_USB_FSF_TEST */ + + + +struct fsf_common *__file_storage_function_singleton; + + +int file_storage_function_setup(struct usb_composite_dev *cdev) +{ + struct fsf_common_config cfg; + struct fsf_lun_config *lcfg; + unsigned i = 0; + int rc; + + if (fsf_module_params.nluns < 1 || + fsf_module_params.nluns > FSF_MAX_LUNS) { + dev_err(&cdev->gadget->dev, "invalid number of LUNs: %u\n", + fsf_module_params.nluns); + return -EINVAL; + } + i = cfg.nluns = fsf_module_params.nluns; + + cfg.luns = lcfg = kmalloc(i * sizeof *cfg.luns, GFP_KERNEL); + if (!lcfg) + return -ENOMEM; + + +#define V(name, def) \ + (fsf_module_params.name ##_count > i ? fsf_module_params.name[i] : def) + + do { + lcfg->filename = V(file, 0); + file_storage_cfg_set_readonly(lcfg, V(ro, 0)); + file_storage_cfg_set_removable(lcfg, V(removable, 1)); + file_storage_cfg_set_cdrom(lcfg, V(cdrom, 0)); + ++lcfg; + } while (--i); +#undef V + + + cfg.vendor = "Linux"; + cfg.product = "File-Stor Gadget"; +#ifdef CONFIG_USB_FSF_TEST + cfg.release = fsf_module_params.release; +#else + cfg.release = 0xffff; +#endif + + rc = file_storage_function_init_common(cdev, &cfg, &__file_storage_function_singleton); + kfree(cfg.luns); + + return rc; +} + + + +#ifdef CONFIG_USB_FSF_TEST +void file_storage_cfg_from_params(struct fsf_config *cfg) +{ + file_storage_cfg_set_proto(cfg, fsf_module_params.protocol_parm, + fsf_module_params.transport_parm); +} +#endif diff --git a/drivers/usb/gadget/f_file_storage_params.h b/drivers/usb/gadget/f_file_storage_params.h new file mode 100644 index 0000000..fa1e347 --- /dev/null +++ b/drivers/usb/gadget/f_file_storage_params.h @@ -0,0 +1,102 @@ +/* + * f_file_storage_params.h -- File-backed USB Storage Function + * + * Copyright (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz <m.nazarewicz@xxxxxxxxxxx> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* + * This file (with corresponding .c file) defines several module + * parameters which are mapped to proper File-backed Storage Function + * (or FSF) configuration structures. It also provides some functions + * which move burden of creating FSF's configuration from gadget + * developer. When using this file the module will accept following + * parameters: + * + * file=filename[,filename...] + * Required if "removable" is not set, names of + * the files or block devices used for + * backing storage + * ro=b[,b...] Default false, booleans for read-only access + * removable=b[,b...] Default true, boolean for removable media + * cdrom=b[,b...] Default false, boolean for whether to emulate + * a CD-ROM drive + * luns=N Default N = number of filenames, number of + * LUNs to support + * transport=XXX Default BBB, transport name (CB, CBI, or BBB) + * protocol=YYY Default SCSI, protocol name (RBC, 8020 or + * ATAPI, QIC, UFI, 8070, or SCSI; + * also 1 - 6) + * + * The "ro" is present only if CONFIG_USB_FSF_READONLY_ON is not set, + * "removable" is present only if CONFIG_USB_FSF_REMOVABLE_CONFIG is + * set, "cdrom" is present only if CONFIG_USB_FSF_CDROM_CONFIG is set + * and "transport" as well as "protocol" are only set if + * CONFIG_USB_FSF_TEST is set. + * + * The File-backed Storage Function defines some parameters of its own + * so you should consult documentation in f_file_storage.h as well. + * + * + * As gadget developer, you need to invoke + * file_storage_function_setup() prior to adding FSF to ony USB + * configurations. Then, when adding FSF to USB configurations use + * file_storage_function_singleton() to access fsf_common object. And + * finally, when all configurations are set up, call + * file_storage_function_cleanup(). + */ + + + +#ifndef __KERNEL_F_FILE_STORAGE_PARAMS_H +#define __KERNEL_F_FILE_STORAGE_PARAMS_H + + +#include "f_file_storage.h" + + +extern struct fsf_common *__file_storage_function_singleton; + +static inline struct fsf_common *file_storage_function_singleton(void) +{ + return __file_storage_function_singleton; +} + +static inline int file_storage_function_setup(struct usb_composite_dev *cdev); + +static inline int file_storage_function_cleanup(void) +{ + int ret = 0; + if (__file_storage_function_singleton) { + ret = file_storage_function_release_common(__file_storage_function_singleton); + __file_storage_function_singleton = 0; + } + return ret; +} + + + +#ifdef CONFIG_USB_FSF_TEST +void file_storage_cfg_from_params(struct fsf_config *cfg); +#else +# define file_storage_cfg_from_params(cfg) \ + file_storage_cfg_set_proto(cfg, 0, 0) +#endif + + +#endif -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html