/*
* file_storage.c -- File-backed USB Storage Gadget, for USB development
*
- * Copyright (C) 2003-2005 Alan Stern
+ * Copyright (C) 2003-2008 Alan Stern
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
/*
* The File-backed Storage Gadget acts as a USB Mass Storage device,
- * appearing to the host as a disk drive. In addition to providing an
- * example of a genuinely useful gadget driver for a USB device, it also
- * illustrates a technique of double-buffering for increased throughput.
- * Last but not least, it gives an easy way to probe the behavior of the
- * Mass Storage drivers in a USB host.
+ * appearing to the host as a disk drive or as a CD-ROM drive. In addition
+ * to providing an example of a genuinely useful gadget driver for a USB
+ * device, it also illustrates a technique of double-buffering for increased
+ * throughput. Last but not least, it gives an easy way to probe the
+ * behavior of the Mass Storage drivers in a USB host.
*
* Backing storage is provided by a regular file or a block device, specified
* by the "file" module parameter. Access can be limited to read-only by
- * setting the optional "ro" module parameter. The gadget will indicate that
- * it has removable media if the optional "removable" module parameter is set.
+ * setting the optional "ro" module parameter. (For CD-ROM emulation,
+ * access is always read-only.) The gadget will indicate that it has
+ * removable media if the optional "removable" module parameter is set.
*
* The gadget supports the Control-Bulk (CB), Control-Bulk-Interrupt (CBI),
* and Bulk-Only (also known as Bulk-Bulk-Bulk or BBB) transports, selected
* The default number of LUNs is taken from the number of "file" elements;
* it is 1 if "file" is not given. If "removable" is not set then a backing
* file must be specified for each LUN. If it is set, then an unspecified
- * or empty backing filename means the LUN's medium is not loaded.
+ * or empty backing filename means the LUN's medium is not loaded. Ideally
+ * each LUN would be settable independently as a disk drive or a CD-ROM
+ * drive, but currently all LUNs have to be the same type. The CD-ROM
+ * emulation includes a single data track and no audio tracks; hence there
+ * need be only one backing file per LUN. Note also that the CD-ROM block
+ * length is set to 512 rather than the more common value 2048.
*
* 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.
+ *
* Module options:
*
* file=filename[,filename...]
* removable Default false, boolean for removable media
* luns=N Default N = number of filenames, number of
* LUNs to support
+ * stall Default determined according to the type of
+ * USB device controller (usually true),
+ * boolean to permit the driver to halt
+ * bulk endpoints
+ * cdrom Default false, boolean for whether to emulate
+ * a CD-ROM drive
* 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;
* buflen=N Default N=16384, buffer size used (will be
* rounded down to a multiple of
* PAGE_CACHE_SIZE)
- * stall Default determined according to the type of
- * USB device controller (usually true),
- * boolean to permit the driver to halt
- * bulk endpoints
*
* If CONFIG_USB_FILE_STORAGE_TEST is not set, only the "file", "ro",
- * "removable", and "luns" options are available; default values are used
- * for everything else.
+ * "removable", "luns", "stall", and "cdrom" options are available; default
+ * values are used for everything else.
*
* 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.
+ * setting are not allowed when the medium is loaded or if CD-ROM emulation
+ * is being used.
*
* 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>.
*/
*/
-#undef DEBUG
-#undef VERBOSE
-#undef DUMP_MSGS
-
-#include <linux/config.h>
+/* #define VERBOSE_DEBUG */
+/* #define DUMP_MSGS */
-#include <asm/system.h>
-#include <asm/uaccess.h>
-#include <linux/bitops.h>
#include <linux/blkdev.h>
-#include <linux/compiler.h>
#include <linux/completion.h>
#include <linux/dcache.h>
#include <linux/delay.h>
#include <linux/fcntl.h>
#include <linux/file.h>
#include <linux/fs.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
+#include <linux/kref.h>
+#include <linux/kthread.h>
#include <linux/limits.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/pagemap.h>
#include <linux/rwsem.h>
-#include <linux/sched.h>
-#include <linux/signal.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string.h>
-#include <linux/suspend.h>
+#include <linux/freezer.h>
#include <linux/utsname.h>
-#include <linux/wait.h>
-#include <linux/usb_ch9.h>
-#include <linux/usb_gadget.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
#include "gadget_chips.h"
+
+/*
+ * Kbuild is not very cooperative with respect to linking separately
+ * compiled library objects into one module. So for now we won't use
+ * separate compilation ... ensuring init/exit sections work to shrink
+ * the runtime footprint, and giving us at least some parts of what
+ * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
+ */
+#include "usbstring.c"
+#include "config.c"
+#include "epautoconf.c"
+
/*-------------------------------------------------------------------------*/
#define DRIVER_DESC "File-backed Storage Gadget"
#define DRIVER_NAME "g_file_storage"
-#define DRIVER_VERSION "20 October 2004"
+#define DRIVER_VERSION "20 November 2008"
static const char longname[] = DRIVER_DESC;
static const char shortname[] = DRIVER_NAME;
/*-------------------------------------------------------------------------*/
-#define xprintk(f,level,fmt,args...) \
- dev_printk(level , &(f)->gadget->dev , fmt , ## args)
-#define yprintk(l,level,fmt,args...) \
- dev_printk(level , &(l)->dev , fmt , ## args)
-
-#ifdef DEBUG
-#define DBG(fsg,fmt,args...) \
- xprintk(fsg , KERN_DEBUG , fmt , ## args)
#define LDBG(lun,fmt,args...) \
- yprintk(lun , KERN_DEBUG , fmt , ## args)
-#define MDBG(fmt,args...) \
- printk(KERN_DEBUG DRIVER_NAME ": " fmt , ## args)
-#else
-#define DBG(fsg,fmt,args...) \
- do { } while (0)
-#define LDBG(lun,fmt,args...) \
- do { } while (0)
+ dev_dbg(&(lun)->dev , fmt , ## args)
#define MDBG(fmt,args...) \
- do { } while (0)
-#undef VERBOSE
+ pr_debug(DRIVER_NAME ": " fmt , ## args)
+
+#ifndef DEBUG
+#undef VERBOSE_DEBUG
#undef DUMP_MSGS
-#endif /* DEBUG */
+#endif /* !DEBUG */
-#ifdef VERBOSE
-#define VDBG DBG
+#ifdef VERBOSE_DEBUG
#define VLDBG LDBG
#else
-#define VDBG(fsg,fmt,args...) \
- do { } while (0)
#define VLDBG(lun,fmt,args...) \
do { } while (0)
-#endif /* VERBOSE */
+#endif /* VERBOSE_DEBUG */
-#define ERROR(fsg,fmt,args...) \
- xprintk(fsg , KERN_ERR , fmt , ## args)
#define LERROR(lun,fmt,args...) \
- yprintk(lun , KERN_ERR , fmt , ## args)
-
-#define WARN(fsg,fmt,args...) \
- xprintk(fsg , KERN_WARNING , fmt , ## args)
+ dev_err(&(lun)->dev , fmt , ## args)
#define LWARN(lun,fmt,args...) \
- yprintk(lun , KERN_WARNING , fmt , ## args)
-
-#define INFO(fsg,fmt,args...) \
- xprintk(fsg , KERN_INFO , fmt , ## args)
+ dev_warn(&(lun)->dev , fmt , ## args)
#define LINFO(lun,fmt,args...) \
- yprintk(lun , KERN_INFO , fmt , ## args)
+ dev_info(&(lun)->dev , fmt , ## args)
#define MINFO(fmt,args...) \
- printk(KERN_INFO DRIVER_NAME ": " fmt , ## args)
+ pr_info(DRIVER_NAME ": " fmt , ## args)
+
+#define DBG(d, fmt, args...) \
+ dev_dbg(&(d)->gadget->dev , fmt , ## args)
+#define VDBG(d, fmt, args...) \
+ dev_vdbg(&(d)->gadget->dev , fmt , ## args)
+#define ERROR(d, fmt, args...) \
+ dev_err(&(d)->gadget->dev , fmt , ## args)
+#define WARNING(d, fmt, args...) \
+ dev_warn(&(d)->gadget->dev , fmt , ## args)
+#define INFO(d, fmt, args...) \
+ dev_info(&(d)->gadget->dev , fmt , ## args)
/*-------------------------------------------------------------------------*/
#define MAX_LUNS 8
- /* Arggh! There should be a module_param_array_named macro! */
-static char *file[MAX_LUNS] = {NULL, };
-static int ro[MAX_LUNS] = {0, };
-
static struct {
- int num_filenames;
- int num_ros;
+ char *file[MAX_LUNS];
+ int ro[MAX_LUNS];
+ unsigned int num_filenames;
+ unsigned int num_ros;
unsigned int nluns;
+ int removable;
+ int can_stall;
+ int cdrom;
+
char *transport_parm;
char *protocol_parm;
- int removable;
unsigned short vendor;
unsigned short product;
unsigned short release;
unsigned int buflen;
- int can_stall;
int transport_type;
char *transport_name;
.transport_parm = "BBB",
.protocol_parm = "SCSI",
.removable = 0,
+ .can_stall = 1,
+ .cdrom = 0,
.vendor = DRIVER_VENDOR_ID,
.product = DRIVER_PRODUCT_ID,
.release = 0xffff, // Use controller chip type
.buflen = 16384,
- .can_stall = 1,
};
-module_param_array(file, charp, &mod_data.num_filenames, S_IRUGO);
+module_param_array_named(file, mod_data.file, charp, &mod_data.num_filenames,
+ S_IRUGO);
MODULE_PARM_DESC(file, "names of backing files or devices");
-module_param_array(ro, bool, &mod_data.num_ros, S_IRUGO);
+module_param_array_named(ro, mod_data.ro, bool, &mod_data.num_ros, S_IRUGO);
MODULE_PARM_DESC(ro, "true to force read-only");
module_param_named(luns, mod_data.nluns, uint, S_IRUGO);
module_param_named(removable, mod_data.removable, bool, S_IRUGO);
MODULE_PARM_DESC(removable, "true to simulate removable media");
+module_param_named(stall, mod_data.can_stall, bool, S_IRUGO);
+MODULE_PARM_DESC(stall, "false to prevent bulk stalls");
+
+module_param_named(cdrom, mod_data.cdrom, bool, S_IRUGO);
+MODULE_PARM_DESC(cdrom, "true to emulate cdrom instead of disk");
+
/* In the non-TEST version, only the module parameters listed above
* are available. */
module_param_named(buflen, mod_data.buflen, uint, S_IRUGO);
MODULE_PARM_DESC(buflen, "I/O buffer size");
-module_param_named(stall, mod_data.can_stall, bool, S_IRUGO);
-MODULE_PARM_DESC(stall, "false to prevent bulk stalls");
-
#endif /* CONFIG_USB_FILE_STORAGE_TEST */
/*-------------------------------------------------------------------------*/
+/* SCSI device types */
+#define TYPE_DISK 0x00
+#define TYPE_CDROM 0x05
+
/* USB protocol value = the transport method */
#define USB_PR_CBI 0x00 // Control/Bulk/Interrupt
#define USB_PR_CB 0x01 // Control/Bulk w/o interrupt
#define SC_READ_12 0xa8
#define SC_READ_CAPACITY 0x25
#define SC_READ_FORMAT_CAPACITIES 0x23
+#define SC_READ_HEADER 0x44
+#define SC_READ_TOC 0x43
#define SC_RELEASE 0x17
#define SC_REQUEST_SENSE 0x03
#define SC_RESERVE 0x16
unsigned int ro : 1;
unsigned int prevent_medium_removal : 1;
unsigned int registered : 1;
+ unsigned int info_valid : 1;
u32 sense_data;
u32 sense_data_info;
#define backing_file_is_open(curlun) ((curlun)->filp != NULL)
-static inline struct lun *dev_to_lun(struct device *dev)
+static struct lun *dev_to_lun(struct device *dev)
{
return container_of(dev, struct lun, dev);
}
struct fsg_buffhd {
void *buf;
- dma_addr_t dma;
- volatile enum fsg_buffer_state state;
+ enum fsg_buffer_state state;
struct fsg_buffhd *next;
/* The NetChip 2280 is faster, and handles some protocol faults
unsigned int bulk_out_intended_length;
struct usb_request *inreq;
- volatile int inreq_busy;
+ int inreq_busy;
struct usb_request *outreq;
- volatile int outreq_busy;
+ int outreq_busy;
};
enum fsg_state {
/* filesem protects: backing files in use */
struct rw_semaphore filesem;
+ /* reference counting: wait until all LUNs are released */
+ struct kref ref;
+
struct usb_ep *ep0; // Handy copy of gadget->ep0
struct usb_request *ep0req; // For control responses
- volatile unsigned int ep0_req_tag;
+ unsigned int ep0_req_tag;
const char *ep0req_name;
struct usb_request *intreq; // For interrupt responses
- volatile int intreq_busy;
+ int intreq_busy;
struct fsg_buffhd *intr_buffhd;
unsigned int bulk_out_maxpacket;
unsigned long atomic_bitflags;
#define REGISTERED 0
-#define CLEAR_BULK_HALTS 1
+#define IGNORE_BULK_OUT 1
#define SUSPENDED 2
struct usb_ep *bulk_in;
struct fsg_buffhd *next_buffhd_to_drain;
struct fsg_buffhd buffhds[NUM_BUFFERS];
- wait_queue_head_t thread_wqh;
int thread_wakeup_needed;
struct completion thread_notifier;
- int thread_pid;
struct task_struct *thread_task;
- sigset_t thread_signal_mask;
int cmnd_size;
u8 cmnd[MAX_COMMAND_SIZE];
unsigned int nluns;
struct lun *luns;
struct lun *curlun;
- struct completion lun_released;
};
typedef void (*fsg_routine_t)(struct fsg_dev *);
-static int inline exception_in_progress(struct fsg_dev *fsg)
+static int exception_in_progress(struct fsg_dev *fsg)
{
return (fsg->state > FSG_STATE_IDLE);
}
/* Make bulk-out requests be divisible by the maxpacket size */
-static void inline set_bulk_out_req_length(struct fsg_dev *fsg,
+static void set_bulk_out_req_length(struct fsg_dev *fsg,
struct fsg_buffhd *bh, unsigned int length)
{
unsigned int rem;
static void dump_msg(struct fsg_dev *fsg, const char *label,
const u8 *buf, unsigned int length)
{
- unsigned int start, num, i;
- char line[52], *p;
-
- if (length >= 512)
- return;
- DBG(fsg, "%s, length %u:\n", label, length);
-
- start = 0;
- while (length > 0) {
- num = min(length, 16u);
- p = line;
- for (i = 0; i < num; ++i) {
- if (i == 8)
- *p++ = ' ';
- sprintf(p, " %02x", buf[i]);
- p += 3;
- }
- *p = 0;
- printk(KERN_DEBUG "%6x: %s\n", start, line);
- buf += num;
- start += num;
- length -= num;
+ if (length < 512) {
+ DBG(fsg, "%s, length %u:\n", label, length);
+ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET,
+ 16, 1, buf, length, 0);
}
}
-static void inline dump_cdb(struct fsg_dev *fsg)
+static void dump_cdb(struct fsg_dev *fsg)
{}
#else
-static void inline dump_msg(struct fsg_dev *fsg, const char *label,
+static void dump_msg(struct fsg_dev *fsg, const char *label,
const u8 *buf, unsigned int length)
{}
-static void inline dump_cdb(struct fsg_dev *fsg)
-{
- int i;
- char cmdbuf[3*MAX_COMMAND_SIZE + 1];
+#ifdef VERBOSE_DEBUG
- for (i = 0; i < fsg->cmnd_size; ++i)
- sprintf(cmdbuf + i*3, " %02x", fsg->cmnd[i]);
- VDBG(fsg, "SCSI CDB: %s\n", cmdbuf);
+static void dump_cdb(struct fsg_dev *fsg)
+{
+ print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE,
+ 16, 1, fsg->cmnd, fsg->cmnd_size, 0);
}
+#else
+
+static void dump_cdb(struct fsg_dev *fsg)
+{}
+
+#endif /* VERBOSE_DEBUG */
#endif /* DUMP_MSGS */
/* Routines for unaligned data access */
-static u16 inline get_be16(u8 *buf)
+static u16 get_be16(u8 *buf)
{
return ((u16) buf[0] << 8) | ((u16) buf[1]);
}
-static u32 inline get_be32(u8 *buf)
+static u32 get_be32(u8 *buf)
{
return ((u32) buf[0] << 24) | ((u32) buf[1] << 16) |
((u32) buf[2] << 8) | ((u32) buf[3]);
}
-static void inline put_be16(u8 *buf, u16 val)
+static void put_be16(u8 *buf, u16 val)
{
buf[0] = val >> 8;
buf[1] = val;
}
-static void inline put_be32(u8 *buf, u32 val)
+static void put_be32(u8 *buf, u32 val)
{
buf[0] = val >> 24;
buf[1] = val >> 16;
buf[2] = val >> 8;
- buf[3] = val;
+ buf[3] = val & 0xff;
}
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
- .bcdUSB = __constant_cpu_to_le16(0x0200),
+ .bcdUSB = cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_PER_INTERFACE,
/* The next three values can be overridden by module parameters */
- .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_ID),
- .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_ID),
- .bcdDevice = __constant_cpu_to_le16(0xffff),
+ .idVendor = cpu_to_le16(DRIVER_VENDOR_ID),
+ .idProduct = cpu_to_le16(DRIVER_PRODUCT_ID),
+ .bcdDevice = cpu_to_le16(0xffff),
.iManufacturer = STRING_MANUFACTURER,
.iProduct = STRING_PRODUCT,
.bConfigurationValue = CONFIG_VALUE,
.iConfiguration = STRING_CONFIG,
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
- .bMaxPower = 1, // self-powered
+ .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2,
};
static struct usb_otg_descriptor
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = __constant_cpu_to_le16(2),
+ .wMaxPacketSize = cpu_to_le16(2),
.bInterval = 32, // frames -> 32 ms
};
#define FS_FUNCTION_PRE_EP_ENTRIES 2
-#ifdef CONFIG_USB_GADGET_DUALSPEED
-
/*
* USB 2.0 devices need to expose both high speed and full speed
* descriptors, unless they only run at full speed.
.bLength = sizeof dev_qualifier,
.bDescriptorType = USB_DT_DEVICE_QUALIFIER,
- .bcdUSB = __constant_cpu_to_le16(0x0200),
+ .bcdUSB = cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_PER_INTERFACE,
.bNumConfigurations = 1,
/* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */
.bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = __constant_cpu_to_le16(512),
+ .wMaxPacketSize = cpu_to_le16(512),
};
static struct usb_endpoint_descriptor
/* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */
.bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = __constant_cpu_to_le16(512),
+ .wMaxPacketSize = cpu_to_le16(512),
.bInterval = 1, // NAK every 1 uframe
};
/* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */
.bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = __constant_cpu_to_le16(2),
+ .wMaxPacketSize = cpu_to_le16(2),
.bInterval = 9, // 2**(9-1) = 256 uframes -> 32 ms
};
#define HS_FUNCTION_PRE_EP_ENTRIES 2
/* Maxpacket and other transfer characteristics vary by speed. */
-#define ep_desc(g,fs,hs) (((g)->speed==USB_SPEED_HIGH) ? (hs) : (fs))
-
-#else
-
-/* If there's no high speed support, always use the full-speed descriptor. */
-#define ep_desc(g,fs,hs) fs
-
-#endif /* !CONFIG_USB_GADGET_DUALSPEED */
+static struct usb_endpoint_descriptor *
+ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs,
+ struct usb_endpoint_descriptor *hs)
+{
+ if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+ return hs;
+ return fs;
+}
/* The CBI specification limits the serial string to 12 uppercase hexadecimal
static int populate_config_buf(struct usb_gadget *gadget,
u8 *buf, u8 type, unsigned index)
{
-#ifdef CONFIG_USB_GADGET_DUALSPEED
enum usb_device_speed speed = gadget->speed;
-#endif
int len;
const struct usb_descriptor_header **function;
if (index > 0)
return -EINVAL;
-#ifdef CONFIG_USB_GADGET_DUALSPEED
- if (type == USB_DT_OTHER_SPEED_CONFIG)
+ if (gadget_is_dualspeed(gadget) && type == USB_DT_OTHER_SPEED_CONFIG)
speed = (USB_SPEED_FULL + USB_SPEED_HIGH) - speed;
- if (speed == USB_SPEED_HIGH)
+ if (gadget_is_dualspeed(gadget) && speed == USB_SPEED_HIGH)
function = hs_function;
else
-#endif
function = fs_function;
/* for now, don't advertise srp-only devices */
- if (!gadget->is_otg)
+ if (!gadget_is_otg(gadget))
function++;
len = usb_gadget_config_buf(&config_desc, buf, EP0_BUFSIZE, function);
/* These routines may be called in process context or in_irq */
+/* Caller must hold fsg->lock */
static void wakeup_thread(struct fsg_dev *fsg)
{
/* Tell the main thread that something has happened */
fsg->thread_wakeup_needed = 1;
- wake_up_all(&fsg->thread_wqh);
+ if (fsg->thread_task)
+ wake_up_process(fsg->thread_task);
}
static void raise_exception(struct fsg_dev *fsg, enum fsg_state new_state)
{
unsigned long flags;
- struct task_struct *thread_task;
/* Do nothing if a higher-priority exception is already in progress.
* If a lower-or-equal priority exception is in progress, preempt it
if (fsg->state <= new_state) {
fsg->exception_req_tag = fsg->ep0_req_tag;
fsg->state = new_state;
- thread_task = fsg->thread_task;
- if (thread_task)
- send_sig_info(SIGUSR1, SEND_SIG_FORCED, thread_task);
+ if (fsg->thread_task)
+ send_sig_info(SIGUSR1, SEND_SIG_FORCED,
+ fsg->thread_task);
}
spin_unlock_irqrestore(&fsg->lock, flags);
}
if (rc != 0 && rc != -ESHUTDOWN) {
/* We can't do much more than wait for a reset */
- WARN(fsg, "error in submission: %s --> %d\n",
+ WARNING(fsg, "error in submission: %s --> %d\n",
fsg->ep0->name, rc);
}
return rc;
static void ep0_complete(struct usb_ep *ep, struct usb_request *req)
{
- struct fsg_dev *fsg = (struct fsg_dev *) ep->driver_data;
+ struct fsg_dev *fsg = ep->driver_data;
if (req->actual > 0)
dump_msg(fsg, fsg->ep0req_name, req->buf, req->actual);
if (req->status || req->actual != req->length)
- DBG(fsg, "%s --> %d, %u/%u\n", __FUNCTION__,
+ DBG(fsg, "%s --> %d, %u/%u\n", __func__,
req->status, req->actual, req->length);
if (req->status == -ECONNRESET) // Request was cancelled
usb_ep_fifo_flush(ep);
static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req)
{
- struct fsg_dev *fsg = (struct fsg_dev *) ep->driver_data;
- struct fsg_buffhd *bh = (struct fsg_buffhd *) req->context;
+ struct fsg_dev *fsg = ep->driver_data;
+ struct fsg_buffhd *bh = req->context;
if (req->status || req->actual != req->length)
- DBG(fsg, "%s --> %d, %u/%u\n", __FUNCTION__,
+ DBG(fsg, "%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(&fsg->lock);
bh->inreq_busy = 0;
bh->state = BUF_STATE_EMPTY;
- spin_unlock(&fsg->lock);
wakeup_thread(fsg);
+ spin_unlock(&fsg->lock);
}
static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req)
{
- struct fsg_dev *fsg = (struct fsg_dev *) ep->driver_data;
- struct fsg_buffhd *bh = (struct fsg_buffhd *) req->context;
+ struct fsg_dev *fsg = ep->driver_data;
+ struct fsg_buffhd *bh = req->context;
dump_msg(fsg, "bulk-out", req->buf, req->actual);
if (req->status || req->actual != bh->bulk_out_intended_length)
- DBG(fsg, "%s --> %d, %u/%u\n", __FUNCTION__,
+ DBG(fsg, "%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(&fsg->lock);
bh->outreq_busy = 0;
bh->state = BUF_STATE_FULL;
- spin_unlock(&fsg->lock);
wakeup_thread(fsg);
+ spin_unlock(&fsg->lock);
}
#ifdef CONFIG_USB_FILE_STORAGE_TEST
static void intr_in_complete(struct usb_ep *ep, struct usb_request *req)
{
- struct fsg_dev *fsg = (struct fsg_dev *) ep->driver_data;
- struct fsg_buffhd *bh = (struct fsg_buffhd *) req->context;
+ struct fsg_dev *fsg = ep->driver_data;
+ struct fsg_buffhd *bh = req->context;
if (req->status || req->actual != req->length)
- DBG(fsg, "%s --> %d, %u/%u\n", __FUNCTION__,
+ DBG(fsg, "%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(&fsg->lock);
fsg->intreq_busy = 0;
bh->state = BUF_STATE_EMPTY;
- spin_unlock(&fsg->lock);
wakeup_thread(fsg);
+ spin_unlock(&fsg->lock);
}
#else
/* Save the command for later */
if (fsg->cbbuf_cmnd_size)
- WARN(fsg, "CB[I] overwriting previous command\n");
+ WARNING(fsg, "CB[I] overwriting previous command\n");
fsg->cbbuf_cmnd_size = req->actual;
memcpy(fsg->cbbuf_cmnd, req->buf, fsg->cbbuf_cmnd_size);
- spin_unlock(&fsg->lock);
wakeup_thread(fsg);
+ spin_unlock(&fsg->lock);
}
#else
{
struct usb_request *req = fsg->ep0req;
int value = -EOPNOTSUPP;
- u16 w_index = ctrl->wIndex;
- u16 w_length = ctrl->wLength;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
if (!fsg->config)
return value;
if (ctrl->bRequestType != (USB_DIR_OUT |
USB_TYPE_CLASS | USB_RECIP_INTERFACE))
break;
- if (w_index != 0) {
+ if (w_index != 0 || w_value != 0) {
value = -EDOM;
break;
}
if (ctrl->bRequestType != (USB_DIR_IN |
USB_TYPE_CLASS | USB_RECIP_INTERFACE))
break;
- if (w_index != 0) {
+ if (w_index != 0 || w_value != 0) {
value = -EDOM;
break;
}
VDBG(fsg, "get max LUN\n");
*(u8 *) req->buf = fsg->nluns - 1;
- value = min(w_length, (u16) 1);
+ value = 1;
break;
}
}
if (ctrl->bRequestType != (USB_DIR_OUT |
USB_TYPE_CLASS | USB_RECIP_INTERFACE))
break;
- if (w_index != 0) {
+ if (w_index != 0 || w_value != 0) {
value = -EDOM;
break;
}
"unknown class-specific control req "
"%02x.%02x v%04x i%04x l%u\n",
ctrl->bRequestType, ctrl->bRequest,
- ctrl->wValue, w_index, w_length);
+ le16_to_cpu(ctrl->wValue), w_index, w_length);
return value;
}
{
struct usb_request *req = fsg->ep0req;
int value = -EOPNOTSUPP;
- u16 w_index = ctrl->wIndex;
- u16 w_value = ctrl->wValue;
- u16 w_length = ctrl->wLength;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
/* Usually this just stores reply data in the pre-allocated ep0 buffer,
* but config change events will also reconfigure hardware. */
case USB_DT_DEVICE:
VDBG(fsg, "get device descriptor\n");
- value = min(w_length, (u16) sizeof device_desc);
+ value = sizeof device_desc;
memcpy(req->buf, &device_desc, value);
break;
-#ifdef CONFIG_USB_GADGET_DUALSPEED
case USB_DT_DEVICE_QUALIFIER:
VDBG(fsg, "get device qualifier\n");
- if (!fsg->gadget->is_dualspeed)
+ if (!gadget_is_dualspeed(fsg->gadget))
break;
- value = min(w_length, (u16) sizeof dev_qualifier);
+ value = sizeof dev_qualifier;
memcpy(req->buf, &dev_qualifier, value);
break;
case USB_DT_OTHER_SPEED_CONFIG:
VDBG(fsg, "get other-speed config descriptor\n");
- if (!fsg->gadget->is_dualspeed)
+ if (!gadget_is_dualspeed(fsg->gadget))
break;
goto get_config;
-#endif
case USB_DT_CONFIG:
VDBG(fsg, "get configuration descriptor\n");
-#ifdef CONFIG_USB_GADGET_DUALSPEED
- get_config:
-#endif
+get_config:
value = populate_config_buf(fsg->gadget,
req->buf,
w_value >> 8,
w_value & 0xff);
- if (value >= 0)
- value = min(w_length, (u16) value);
break;
case USB_DT_STRING:
/* wIndex == language code */
value = usb_gadget_get_string(&stringtab,
w_value & 0xff, req->buf);
- if (value >= 0)
- value = min(w_length, (u16) value);
break;
}
break;
break;
VDBG(fsg, "get configuration\n");
*(u8 *) req->buf = fsg->config;
- value = min(w_length, (u16) 1);
+ value = 1;
break;
case USB_REQ_SET_INTERFACE:
}
VDBG(fsg, "get interface\n");
*(u8 *) req->buf = 0;
- value = min(w_length, (u16) 1);
+ value = 1;
break;
default:
VDBG(fsg,
"unknown control req %02x.%02x v%04x i%04x l%u\n",
ctrl->bRequestType, ctrl->bRequest,
- w_value, w_index, w_length);
+ w_value, w_index, le16_to_cpu(ctrl->wLength));
}
return value;
{
struct fsg_dev *fsg = get_gadget_data(gadget);
int rc;
+ int w_length = le16_to_cpu(ctrl->wLength);
++fsg->ep0_req_tag; // Record arrival of a new request
fsg->ep0req->context = NULL;
/* Respond with data/status or defer until later? */
if (rc >= 0 && rc != DELAYED_STATUS) {
+ rc = min(rc, w_length);
fsg->ep0req->length = rc;
- fsg->ep0req->zero = (rc < ctrl->wLength &&
- (rc % gadget->ep0->maxpacket) == 0);
+ fsg->ep0req->zero = rc < w_length;
fsg->ep0req_name = (ctrl->bRequestType & USB_DIR_IN ?
"ep0-in" : "ep0-out");
rc = ep0_queue(fsg);
/* Use this for bulk or interrupt transfers, not ep0 */
static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep,
- struct usb_request *req, volatile int *pbusy,
- volatile enum fsg_buffer_state *state)
+ struct usb_request *req, int *pbusy,
+ enum fsg_buffer_state *state)
{
int rc;
dump_msg(fsg, "bulk-in", req->buf, req->length);
else if (ep == fsg->intr_in)
dump_msg(fsg, "intr-in", req->buf, req->length);
+
+ spin_lock_irq(&fsg->lock);
*pbusy = 1;
*state = BUF_STATE_BUSY;
+ spin_unlock_irq(&fsg->lock);
rc = usb_ep_queue(ep, req, GFP_KERNEL);
if (rc != 0) {
*pbusy = 0;
* submissions if DMA is enabled. */
if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP &&
req->length == 0))
- WARN(fsg, "error in submission: %s --> %d\n",
+ WARNING(fsg, "error in submission: %s --> %d\n",
ep->name, rc);
}
}
static int sleep_thread(struct fsg_dev *fsg)
{
- int rc;
+ int rc = 0;
/* Wait until a signal arrives or we are woken up */
- rc = wait_event_interruptible(fsg->thread_wqh,
- fsg->thread_wakeup_needed);
+ for (;;) {
+ try_to_freeze();
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (signal_pending(current)) {
+ rc = -EINTR;
+ break;
+ }
+ if (fsg->thread_wakeup_needed)
+ break;
+ schedule();
+ }
+ __set_current_state(TASK_RUNNING);
fsg->thread_wakeup_needed = 0;
- if (current->flags & PF_FREEZE)
- refrigerator(PF_FREEZE);
- return (rc ? -EINTR : 0);
+ return rc;
}
/* Wait for the next buffer to become available */
bh = fsg->next_buffhd_to_fill;
while (bh->state != BUF_STATE_EMPTY) {
- if ((rc = sleep_thread(fsg)) != 0)
+ rc = sleep_thread(fsg);
+ if (rc)
return rc;
}
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;
if (nread < amount) {
curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
curlun->sense_data_info = file_offset >> 9;
+ curlun->info_valid = 1;
break;
}
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 */
curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
return -EINVAL;
}
- if (fsg->cmnd[1] & 0x08) // FUA
+ if (fsg->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;
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);
* the bulk-out maxpacket size */
bh->outreq->length = bh->bulk_out_intended_length =
amount;
+ bh->outreq->short_not_ok = 1;
start_transfer(fsg, fsg->bulk_out, bh->outreq,
&bh->outreq_busy, &bh->state);
fsg->next_buffhd_to_fill = bh->next;
if (bh->state == BUF_STATE_EMPTY && !get_some_more)
break; // We stopped early
if (bh->state == BUF_STATE_FULL) {
+ smp_rmb();
fsg->next_buffhd_to_drain = bh->next;
bh->state = BUF_STATE_EMPTY;
if (bh->outreq->status != 0) {
curlun->sense_data = SS_COMMUNICATION_FAILURE;
curlun->sense_data_info = file_offset >> 9;
+ curlun->info_valid = 1;
break;
}
if (nwritten < amount) {
curlun->sense_data = SS_WRITE_ERROR;
curlun->sense_data_info = file_offset >> 9;
+ curlun->info_valid = 1;
break;
}
}
/* Wait for something to happen */
- if ((rc = sleep_thread(fsg)) != 0)
+ rc = sleep_thread(fsg);
+ if (rc)
return rc;
}
static int fsync_sub(struct lun *curlun)
{
struct file *filp = curlun->filp;
- struct inode *inode;
- int rc, err;
if (curlun->ro || !filp)
return 0;
- if (!filp->f_op->fsync)
- return -EINVAL;
-
- inode = filp->f_dentry->d_inode;
- down(&inode->i_sem);
- current->flags |= PF_SYNCWRITE;
- rc = filemap_fdatawrite(inode->i_mapping);
- err = filp->f_op->fsync(filp, filp->f_dentry, 1);
- if (!rc)
- rc = err;
- err = filemap_fdatawait(inode->i_mapping);
- if (!rc)
- rc = err;
- current->flags &= ~PF_SYNCWRITE;
- up(&inode->i_sem);
- VLDBG(curlun, "fdatasync -> %d\n", rc);
- return rc;
+ return vfs_fsync(filp, filp->f_path.dentry, 1);
}
static void fsync_all(struct fsg_dev *fsg)
static void invalidate_sub(struct lun *curlun)
{
struct file *filp = curlun->filp;
- struct inode *inode = filp->f_dentry->d_inode;
+ struct inode *inode = filp->f_path.dentry->d_inode;
unsigned long rc;
- rc = invalidate_inode_pages(inode->i_mapping);
+ rc = invalidate_mapping_pages(inode->i_mapping, 0, -1);
VLDBG(curlun, "invalidate_inode_pages -> %ld\n", rc);
}
curlun->sense_data =
SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
curlun->sense_data_info = file_offset >> 9;
+ curlun->info_valid = 1;
break;
}
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;
u8 *buf = (u8 *) bh->buf;
static char vendor_id[] = "Linux ";
- static char product_id[] = "File-Stor Gadget";
+ static char product_disk_id[] = "File-Stor Gadget";
+ static char product_cdrom_id[] = "File-CD Gadget ";
if (!fsg->curlun) { // Unsupported LUNs are okay
fsg->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
+ memset(buf, 0, 8);
+ buf[0] = (mod_data.cdrom ? TYPE_CDROM : TYPE_DISK);
if (mod_data.removable)
buf[1] = 0x80;
buf[2] = 2; // ANSI SCSI level 2
buf[3] = 2; // SCSI-2 INQUIRY data format
buf[4] = 31; // Additional length
// No special options
- sprintf(buf + 8, "%-8s%-16s%04x", vendor_id, product_id,
+ sprintf(buf + 8, "%-8s%-16s%04x", vendor_id,
+ (mod_data.cdrom ? product_cdrom_id :
+ product_disk_id),
mod_data.release);
return 36;
}
struct lun *curlun = fsg->curlun;
u8 *buf = (u8 *) bh->buf;
u32 sd, sdinfo;
+ int valid;
/*
* From the SCSI-2 spec., section 7.9 (Unit attention condition):
fsg->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] = 0x80 | 0x70; // Valid, current error
+ 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
}
+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 do_read_header(struct fsg_dev *fsg, struct fsg_buffhd *bh)
+{
+ struct lun *curlun = fsg->curlun;
+ int msf = fsg->cmnd[1] & 0x02;
+ u32 lba = get_be32(&fsg->cmnd[2]);
+ u8 *buf = (u8 *) bh->buf;
+
+ if ((fsg->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 do_read_toc(struct fsg_dev *fsg, struct fsg_buffhd *bh)
+{
+ struct lun *curlun = fsg->curlun;
+ int msf = fsg->cmnd[1] & 0x02;
+ int start_track = fsg->cmnd[6];
+ u8 *buf = (u8 *) bh->buf;
+
+ if ((fsg->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */
+ start_track > 1) {
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+
+ memset(buf, 0, 20);
+ buf[1] = (20-2); /* TOC data length */
+ buf[2] = 1; /* First track number */
+ buf[3] = 1; /* Last track number */
+ buf[5] = 0x16; /* Data track, copying allowed */
+ buf[6] = 0x01; /* Only track is number 1 */
+ store_cdrom_address(&buf[8], msf, 0);
+
+ buf[13] = 0x16; /* Lead-out track is data */
+ buf[14] = 0xAA; /* Lead-out track number */
+ store_cdrom_address(&buf[16], msf, curlun->num_sectors);
+ return 20;
+}
+
+
static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
{
struct lun *curlun = fsg->curlun;
VDBG(fsg, "delayed bulk-in endpoint halt\n");
while (rc != 0) {
if (rc != -EAGAIN) {
- WARN(fsg, "usb_ep_set_halt -> %d\n", rc);
+ WARNING(fsg, "usb_ep_set_halt -> %d\n", rc);
rc = 0;
break;
}
return rc;
}
+static int wedge_bulk_in_endpoint(struct fsg_dev *fsg)
+{
+ int rc;
+
+ DBG(fsg, "bulk-in set wedge\n");
+ rc = usb_ep_set_wedge(fsg->bulk_in);
+ if (rc == -EAGAIN)
+ VDBG(fsg, "delayed bulk-in endpoint wedge\n");
+ while (rc != 0) {
+ if (rc != -EAGAIN) {
+ WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc);
+ rc = 0;
+ break;
+ }
+
+ /* Wait for a short time and then try again */
+ if (msleep_interruptible(100) != 0)
+ return -EINTR;
+ rc = usb_ep_set_wedge(fsg->bulk_in);
+ }
+ return rc;
+}
+
static int pad_with_zeros(struct fsg_dev *fsg)
{
struct fsg_buffhd *bh = fsg->next_buffhd_to_fill;
/* Wait for the next buffer to be free */
while (bh->state != BUF_STATE_EMPTY) {
- if ((rc = sleep_thread(fsg)) != 0)
+ rc = sleep_thread(fsg);
+ if (rc)
return rc;
}
/* Throw away the data in a filled buffer */
if (bh->state == BUF_STATE_FULL) {
+ smp_rmb();
bh->state = BUF_STATE_EMPTY;
fsg->next_buffhd_to_drain = bh->next;
* the bulk-out maxpacket size */
bh->outreq->length = bh->bulk_out_intended_length =
amount;
+ bh->outreq->short_not_ok = 1;
start_transfer(fsg, fsg->bulk_out, bh->outreq,
&bh->outreq_busy, &bh->state);
fsg->next_buffhd_to_fill = bh->next;
}
/* Otherwise wait for something to happen */
- if ((rc = sleep_thread(fsg)) != 0)
+ rc = sleep_thread(fsg);
+ if (rc)
return rc;
}
return 0;
/* Wait for the next buffer to become available */
bh = fsg->next_buffhd_to_fill;
while (bh->state != BUF_STATE_EMPTY) {
- if ((rc = sleep_thread(fsg)) != 0)
+ rc = sleep_thread(fsg);
+ if (rc)
return rc;
}
}
if (transport_is_bbb()) {
- struct bulk_cs_wrap *csw = (struct bulk_cs_wrap *) bh->buf;
+ struct bulk_cs_wrap *csw = bh->buf;
/* Store and send the Bulk-only CSW */
- csw->Signature = __constant_cpu_to_le32(USB_BULK_CS_SIG);
+ csw->Signature = cpu_to_le32(USB_BULK_CS_SIG);
csw->Tag = fsg->tag;
csw->Residue = cpu_to_le32(fsg->residue);
csw->Status = status;
return 0;
} else { // USB_PR_CBI
- struct interrupt_data *buf = (struct interrupt_data *)
- bh->buf;
+ 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
fsg->intr_buffhd = bh; // Point to the right buffhd
fsg->intreq->buf = bh->inreq->buf;
- fsg->intreq->dma = bh->inreq->dma;
fsg->intreq->context = bh;
start_transfer(fsg, fsg->intr_in, fsg->intreq,
&fsg->intreq_busy, &bh->state);
/* Verify the length of the command itself */
if (cmnd_size != fsg->cmnd_size) {
- /* Special case workaround: MS-Windows issues REQUEST SENSE
- * with cbw->Length == 12 (it should be 6). */
- if (fsg->cmnd[0] == SC_REQUEST_SENSE && fsg->cmnd_size == 12)
+ /* 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 <= fsg->cmnd_size) {
+ DBG(fsg, "%s is buggy! Expected length %d "
+ "but we got %d\n", name,
+ cmnd_size, fsg->cmnd_size);
cmnd_size = fsg->cmnd_size;
- else {
+ } else {
fsg->phase_error = 1;
return -EINVAL;
}
}
- /* Check that the LUN values are oonsistent */
+ /* Check that the LUN values are consistent */
if (transport_is_bbb()) {
if (fsg->lun != lun)
DBG(fsg, "using LUN %d from CBW, "
if (fsg->cmnd[0] != SC_REQUEST_SENSE) {
curlun->sense_data = SS_NO_SENSE;
curlun->sense_data_info = 0;
+ curlun->info_valid = 0;
}
} else {
fsg->curlun = curlun = NULL;
/* Wait for the next buffer to become available for data or status */
bh = fsg->next_buffhd_to_drain = fsg->next_buffhd_to_fill;
while (bh->state != BUF_STATE_EMPTY) {
- if ((rc = sleep_thread(fsg)) != 0)
+ rc = sleep_thread(fsg);
+ if (rc)
return rc;
- }
+ }
fsg->phase_error = 0;
fsg->short_packet_received = 0;
reply = do_read_capacity(fsg, bh);
break;
+ case SC_READ_HEADER:
+ if (!mod_data.cdrom)
+ goto unknown_cmnd;
+ fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]);
+ if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
+ (3<<7) | (0x1f<<1), 1,
+ "READ HEADER")) == 0)
+ reply = do_read_header(fsg, bh);
+ break;
+
+ case SC_READ_TOC:
+ if (!mod_data.cdrom)
+ goto unknown_cmnd;
+ fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]);
+ if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
+ (7<<6) | (1<<1), 1,
+ "READ TOC")) == 0)
+ reply = do_read_toc(fsg, bh);
+ break;
+
case SC_READ_FORMAT_CAPACITIES:
fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]);
if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
// Fall through
default:
+ unknown_cmnd:
fsg->data_size_from_cmnd = 0;
sprintf(unknown, "Unknown x%02x", fsg->cmnd[0]);
if ((reply = check_command(fsg, fsg->cmnd_size,
static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
{
struct usb_request *req = bh->outreq;
- struct bulk_cb_wrap *cbw = (struct bulk_cb_wrap *) req->buf;
+ struct bulk_cb_wrap *cbw = req->buf;
- /* Was this a real packet? */
- if (req->status)
+ /* Was this a real packet? Should it be ignored? */
+ if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags))
return -EINVAL;
/* Is the CBW valid? */
if (req->actual != USB_BULK_CB_WRAP_LEN ||
- cbw->Signature != __constant_cpu_to_le32(
+ cbw->Signature != cpu_to_le32(
USB_BULK_CB_SIG)) {
DBG(fsg, "invalid CBW: len %u sig 0x%x\n",
req->actual,
le32_to_cpu(cbw->Signature));
- /* The Bulk-only spec says we MUST stall the bulk pipes!
- * If we want to avoid stalls, set a flag so that we will
- * clear the endpoint halts at the next reset. */
- if (!mod_data.can_stall)
- set_bit(CLEAR_BULK_HALTS, &fsg->atomic_bitflags);
- fsg_set_halt(fsg, fsg->bulk_out);
- halt_bulk_in_endpoint(fsg);
+ /* The Bulk-only spec says we MUST stall the IN endpoint
+ * (6.6.1), so it's unavoidable. It also says we must
+ * retain this state until the next reset, but there's
+ * no way to tell the controller driver it should ignore
+ * Clear-Feature(HALT) requests.
+ *
+ * We aren't required to halt the OUT endpoint; instead
+ * we can simply accept and discard any data received
+ * until the next reset. */
+ wedge_bulk_in_endpoint(fsg);
+ set_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
return -EINVAL;
}
/* Is the CBW meaningful? */
if (cbw->Lun >= MAX_LUNS || cbw->Flags & ~USB_BULK_IN_FLAG ||
- cbw->Length < 6 || cbw->Length > MAX_COMMAND_SIZE) {
+ cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) {
DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, "
"cmdlen %u\n",
cbw->Lun, cbw->Flags, cbw->Length);
/* Wait for the next buffer to become available */
bh = fsg->next_buffhd_to_fill;
while (bh->state != BUF_STATE_EMPTY) {
- if ((rc = sleep_thread(fsg)) != 0)
+ rc = sleep_thread(fsg);
+ if (rc)
return rc;
- }
+ }
/* Queue a request to read a Bulk-only CBW */
set_bulk_out_req_length(fsg, bh, USB_BULK_CB_WRAP_LEN);
+ bh->outreq->short_not_ok = 1;
start_transfer(fsg, fsg->bulk_out, bh->outreq,
&bh->outreq_busy, &bh->state);
/* Wait for the CBW to arrive */
while (bh->state != BUF_STATE_FULL) {
- if ((rc = sleep_thread(fsg)) != 0)
+ rc = sleep_thread(fsg);
+ if (rc)
return rc;
- }
+ }
+ smp_rmb();
rc = received_cbw(fsg, bh);
bh->state = BUF_STATE_EMPTY;
/* Wait for the next command to arrive */
while (fsg->cbbuf_cmnd_size == 0) {
- if ((rc = sleep_thread(fsg)) != 0)
+ rc = sleep_thread(fsg);
+ if (rc)
return rc;
- }
+ }
/* Is the previous status interrupt request still busy?
* The host is allowed to skip reading the status,
goto reset;
fsg->bulk_out_enabled = 1;
fsg->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize);
+ clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
if (transport_is_cbi()) {
d = ep_desc(fsg->gadget, &fs_intr_in_desc, &hs_intr_in_desc);
if ((rc = alloc_request(fsg, fsg->bulk_out, &bh->outreq)) != 0)
goto reset;
bh->inreq->buf = bh->outreq->buf = bh->buf;
- bh->inreq->dma = bh->outreq->dma = bh->dma;
bh->inreq->context = bh->outreq->context = bh;
bh->inreq->complete = bulk_in_complete;
bh->outreq->complete = bulk_out_complete;
/* Clear the existing signals. Anything but SIGUSR1 is converted
* into a high-priority EXIT exception. */
for (;;) {
- sig = dequeue_signal_lock(current, &fsg->thread_signal_mask,
- &info);
+ sig = dequeue_signal_lock(current, ¤t->blocked, &info);
if (!sig)
break;
if (sig != SIGUSR1) {
curlun->sense_data = curlun->unit_attention_data =
SS_NO_SENSE;
curlun->sense_data_info = 0;
+ curlun->info_valid = 0;
}
fsg->state = FSG_STATE_IDLE;
}
/* 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(CLEAR_BULK_HALTS,
- &fsg->atomic_bitflags)) {
+ if (test_and_clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags))
usb_ep_clear_halt(fsg->bulk_in);
- usb_ep_clear_halt(fsg->bulk_out);
- }
if (transport_is_bbb()) {
if (fsg->ep0_req_tag == exception_req_tag)
static int fsg_main_thread(void *fsg_)
{
- struct fsg_dev *fsg = (struct fsg_dev *) fsg_;
-
- fsg->thread_task = current;
-
- /* Release all our userspace resources */
- daemonize("file-storage-gadget");
+ struct fsg_dev *fsg = fsg_;
/* Allow the thread to be killed by a signal, but set the signal mask
* to block everything but INT, TERM, KILL, and USR1. */
- siginitsetinv(&fsg->thread_signal_mask, sigmask(SIGINT) |
- sigmask(SIGTERM) | sigmask(SIGKILL) |
- sigmask(SIGUSR1));
- sigprocmask(SIG_SETMASK, &fsg->thread_signal_mask, NULL);
+ 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());
- /* Wait for the gadget registration to finish up */
- wait_for_completion(&fsg->thread_notifier);
-
/* The main loop */
while (fsg->state != FSG_STATE_TERMINATED) {
if (exception_in_progress(fsg) || signal_pending(current)) {
spin_unlock_irq(&fsg->lock);
}
+ spin_lock_irq(&fsg->lock);
fsg->thread_task = NULL;
- flush_signals(current);
+ spin_unlock_irq(&fsg->lock);
/* In case we are exiting because of a signal, unregister the
* gadget driver and close the backing file. */
struct inode *inode = NULL;
loff_t size;
loff_t num_sectors;
+ loff_t min_sectors;
/* R/W if we can, R/O if we must */
ro = curlun->ro;
if (!(filp->f_mode & FMODE_WRITE))
ro = 1;
- if (filp->f_dentry)
- inode = filp->f_dentry->d_inode;
+ 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;
rc = (int) size;
goto out;
}
- num_sectors = size >> 9; // File size in 512-byte sectors
- if (num_sectors == 0) {
+ num_sectors = size >> 9; // File size in 512-byte blocks
+ min_sectors = 1;
+ if (mod_data.cdrom) {
+ 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;
return sprintf(buf, "%d\n", curlun->ro);
}
-static ssize_t show_file(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t show_file(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct lun *curlun = dev_to_lun(dev);
- struct fsg_dev *fsg = (struct fsg_dev *) dev_get_drvdata(dev);
+ struct fsg_dev *fsg = dev_get_drvdata(dev);
char *p;
ssize_t rc;
down_read(&fsg->filesem);
if (backing_file_is_open(curlun)) { // Get the complete pathname
- p = d_path(curlun->filp->f_dentry, curlun->filp->f_vfsmnt,
- buf, PAGE_SIZE - 1);
+ p = d_path(&curlun->filp->f_path, buf, PAGE_SIZE - 1);
if (IS_ERR(p))
rc = PTR_ERR(p);
else {
}
-static ssize_t store_ro(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+static ssize_t store_ro(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
ssize_t rc = count;
struct lun *curlun = dev_to_lun(dev);
- struct fsg_dev *fsg = (struct fsg_dev *) dev_get_drvdata(dev);
+ struct fsg_dev *fsg = dev_get_drvdata(dev);
int i;
if (sscanf(buf, "%d", &i) != 1)
return rc;
}
-static ssize_t store_file(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+static ssize_t store_file(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct lun *curlun = dev_to_lun(dev);
- struct fsg_dev *fsg = (struct fsg_dev *) dev_get_drvdata(dev);
+ struct fsg_dev *fsg = dev_get_drvdata(dev);
int rc = 0;
if (curlun->prevent_medium_removal && backing_file_is_open(curlun)) {
/*-------------------------------------------------------------------------*/
+static void fsg_release(struct kref *ref)
+{
+ struct fsg_dev *fsg = container_of(ref, struct fsg_dev, ref);
+
+ kfree(fsg->luns);
+ kfree(fsg);
+}
+
static void lun_release(struct device *dev)
{
- struct fsg_dev *fsg = (struct fsg_dev *) dev_get_drvdata(dev);
+ struct fsg_dev *fsg = dev_get_drvdata(dev);
- complete(&fsg->lun_released);
+ kref_put(&fsg->ref, fsg_release);
}
-static void fsg_unbind(struct usb_gadget *gadget)
+static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget)
{
struct fsg_dev *fsg = get_gadget_data(gadget);
int i;
clear_bit(REGISTERED, &fsg->atomic_bitflags);
/* Unregister the sysfs attribute files and the LUNs */
- init_completion(&fsg->lun_released);
for (i = 0; i < fsg->nluns; ++i) {
curlun = &fsg->luns[i];
if (curlun->registered) {
device_remove_file(&curlun->dev, &dev_attr_ro);
device_remove_file(&curlun->dev, &dev_attr_file);
device_unregister(&curlun->dev);
- wait_for_completion(&fsg->lun_released);
curlun->registered = 0;
}
}
}
/* Free the data buffers */
- for (i = 0; i < NUM_BUFFERS; ++i) {
- struct fsg_buffhd *bh = &fsg->buffhds[i];
-
- if (bh->buf)
- usb_ep_free_buffer(fsg->bulk_in, bh->buf, bh->dma,
- mod_data.buflen);
- }
+ for (i = 0; i < NUM_BUFFERS; ++i)
+ kfree(fsg->buffhds[i].buf);
/* Free the request and buffer for endpoint 0 */
if (req) {
- if (req->buf)
- usb_ep_free_buffer(fsg->ep0, req->buf,
- req->dma, EP0_BUFSIZE);
+ kfree(req->buf);
usb_ep_free_request(fsg->ep0, req);
}
static int __init check_parameters(struct fsg_dev *fsg)
{
int prot;
+ int gcnum;
/* Store the default values */
mod_data.transport_type = USB_PR_BULK;
mod_data.protocol_type = USB_SC_SCSI;
mod_data.protocol_name = "Transparent SCSI";
- if (gadget_is_sh(fsg->gadget))
+ /* Some peripheral controllers are known not to be able to
+ * halt bulk endpoints correctly. If one of them is present,
+ * disable stalls.
+ */
+ if (gadget_is_sh(fsg->gadget) || gadget_is_at91(fsg->gadget))
mod_data.can_stall = 0;
if (mod_data.release == 0xffff) { // Parameter wasn't set
- if (gadget_is_net2280(fsg->gadget))
- mod_data.release = 0x0301;
- else if (gadget_is_dummy(fsg->gadget))
- mod_data.release = 0x0302;
- else if (gadget_is_pxa(fsg->gadget))
- mod_data.release = 0x0303;
- else if (gadget_is_sh(fsg->gadget))
- mod_data.release = 0x0304;
-
/* The sa1100 controller is not supported */
-
- else if (gadget_is_goku(fsg->gadget))
- mod_data.release = 0x0306;
- else if (gadget_is_mq11xx(fsg->gadget))
- mod_data.release = 0x0307;
- else if (gadget_is_omap(fsg->gadget))
- mod_data.release = 0x0308;
- else if (gadget_is_lh7a40x(fsg->gadget))
- mod_data.release = 0x0309;
- else if (gadget_is_n9604(fsg->gadget))
- mod_data.release = 0x0310;
- else if (gadget_is_pxa27x(fsg->gadget))
- mod_data.release = 0x0311;
- else if (gadget_is_s3c2410(gadget))
- mod_data.release = 0x0312;
- else if (gadget_is_at91(fsg->gadget))
- mod_data.release = 0x0313;
+ if (gadget_is_sa1100(fsg->gadget))
+ gcnum = -1;
+ else
+ gcnum = usb_gadget_controller_number(fsg->gadget);
+ if (gcnum >= 0)
+ mod_data.release = 0x0300 + gcnum;
else {
- WARN(fsg, "controller '%s' not recognized\n",
+ WARNING(fsg, "controller '%s' not recognized\n",
fsg->gadget->name);
mod_data.release = 0x0399;
}
goto out;
if (mod_data.removable) { // Enable the store_xxx attributes
- dev_attr_ro.attr.mode = dev_attr_file.attr.mode = 0644;
- dev_attr_ro.store = store_ro;
+ dev_attr_file.attr.mode = 0644;
dev_attr_file.store = store_file;
+ if (!mod_data.cdrom) {
+ dev_attr_ro.attr.mode = 0644;
+ dev_attr_ro.store = store_ro;
+ }
}
/* Find out how many LUNs there should be */
i = mod_data.nluns;
if (i == 0)
- i = max(mod_data.num_filenames, 1);
+ i = max(mod_data.num_filenames, 1u);
if (i > MAX_LUNS) {
ERROR(fsg, "invalid number of LUNs: %d\n", i);
rc = -EINVAL;
/* Create the LUNs, open their backing files, and register the
* LUN devices in sysfs. */
- fsg->luns = kmalloc(i * sizeof(struct lun), GFP_KERNEL);
+ fsg->luns = kzalloc(i * sizeof(struct lun), GFP_KERNEL);
if (!fsg->luns) {
rc = -ENOMEM;
goto out;
}
- memset(fsg->luns, 0, i * sizeof(struct lun));
fsg->nluns = i;
for (i = 0; i < fsg->nluns; ++i) {
curlun = &fsg->luns[i];
- curlun->ro = ro[i];
+ curlun->ro = mod_data.ro[i];
+ if (mod_data.cdrom)
+ curlun->ro = 1;
+ curlun->dev.release = lun_release;
curlun->dev.parent = &gadget->dev;
curlun->dev.driver = &fsg_driver.driver;
dev_set_drvdata(&curlun->dev, fsg);
- snprintf(curlun->dev.bus_id, BUS_ID_SIZE,
- "%s-lun%d", gadget->dev.bus_id, i);
+ dev_set_name(&curlun->dev,"%s-lun%d",
+ dev_name(&gadget->dev), i);
- if ((rc = device_register(&curlun->dev)) != 0)
+ if ((rc = device_register(&curlun->dev)) != 0) {
INFO(fsg, "failed to register LUN%d: %d\n", i, rc);
- else {
- curlun->registered = 1;
- curlun->dev.release = lun_release;
- device_create_file(&curlun->dev, &dev_attr_ro);
- device_create_file(&curlun->dev, &dev_attr_file);
+ goto out;
}
+ if ((rc = device_create_file(&curlun->dev,
+ &dev_attr_ro)) != 0 ||
+ (rc = device_create_file(&curlun->dev,
+ &dev_attr_file)) != 0) {
+ device_unregister(&curlun->dev);
+ goto out;
+ }
+ curlun->registered = 1;
+ kref_get(&fsg->ref);
- if (file[i] && *file[i]) {
- if ((rc = open_backing_file(curlun, file[i])) != 0)
+ if (mod_data.file[i] && *mod_data.file[i]) {
+ if ((rc = open_backing_file(curlun,
+ mod_data.file[i])) != 0)
goto out;
} else if (!mod_data.removable) {
ERROR(fsg, "no file given for LUN%d\n", i);
intf_desc.bInterfaceProtocol = mod_data.transport_type;
fs_function[i + FS_FUNCTION_PRE_EP_ENTRIES] = NULL;
-#ifdef CONFIG_USB_GADGET_DUALSPEED
- hs_function[i + HS_FUNCTION_PRE_EP_ENTRIES] = NULL;
+ if (gadget_is_dualspeed(gadget)) {
+ hs_function[i + HS_FUNCTION_PRE_EP_ENTRIES] = NULL;
- /* Assume ep0 uses the same maxpacket value for both speeds */
- dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket;
+ /* Assume ep0 uses the same maxpacket value for both speeds */
+ dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket;
- /* Assume that all endpoint addresses are the same for both speeds */
- hs_bulk_in_desc.bEndpointAddress = fs_bulk_in_desc.bEndpointAddress;
- hs_bulk_out_desc.bEndpointAddress = fs_bulk_out_desc.bEndpointAddress;
- hs_intr_in_desc.bEndpointAddress = fs_intr_in_desc.bEndpointAddress;
-#endif
-
- if (gadget->is_otg) {
- otg_desc.bmAttributes |= USB_OTG_HNP,
- config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ /* Assume endpoint addresses are the same for both speeds */
+ hs_bulk_in_desc.bEndpointAddress =
+ fs_bulk_in_desc.bEndpointAddress;
+ hs_bulk_out_desc.bEndpointAddress =
+ fs_bulk_out_desc.bEndpointAddress;
+ hs_intr_in_desc.bEndpointAddress =
+ fs_intr_in_desc.bEndpointAddress;
}
+ if (gadget_is_otg(gadget))
+ otg_desc.bmAttributes |= USB_OTG_HNP;
+
rc = -ENOMEM;
/* Allocate the request and buffer for endpoint 0 */
fsg->ep0req = req = usb_ep_alloc_request(fsg->ep0, GFP_KERNEL);
if (!req)
goto out;
- req->buf = usb_ep_alloc_buffer(fsg->ep0, EP0_BUFSIZE,
- &req->dma, GFP_KERNEL);
+ req->buf = kmalloc(EP0_BUFSIZE, GFP_KERNEL);
if (!req->buf)
goto out;
req->complete = ep0_complete;
for (i = 0; i < NUM_BUFFERS; ++i) {
struct fsg_buffhd *bh = &fsg->buffhds[i];
- bh->buf = usb_ep_alloc_buffer(fsg->bulk_in, mod_data.buflen,
- &bh->dma, GFP_KERNEL);
+ /* 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(mod_data.buflen, GFP_KERNEL);
if (!bh->buf)
goto out;
bh->next = bh + 1;
usb_gadget_set_selfpowered(gadget);
snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
- system_utsname.sysname, system_utsname.release,
+ init_utsname()->sysname, init_utsname()->release,
gadget->name);
/* On a real device, serial[] would be loaded from permanent
sprintf(&serial[i], "%02X", c);
}
- if ((rc = kernel_thread(fsg_main_thread, fsg, (CLONE_VM | CLONE_FS |
- CLONE_FILES))) < 0)
+ fsg->thread_task = kthread_create(fsg_main_thread, fsg,
+ "file-storage-gadget");
+ if (IS_ERR(fsg->thread_task)) {
+ rc = PTR_ERR(fsg->thread_task);
goto out;
- fsg->thread_pid = rc;
+ }
INFO(fsg, DRIVER_DESC ", version: " DRIVER_VERSION "\n");
INFO(fsg, "Number of LUNs=%d\n", fsg->nluns);
if (backing_file_is_open(curlun)) {
p = NULL;
if (pathbuf) {
- p = d_path(curlun->filp->f_dentry,
- curlun->filp->f_vfsmnt,
- pathbuf, PATH_MAX);
+ p = d_path(&curlun->filp->f_path,
+ pathbuf, PATH_MAX);
if (IS_ERR(p))
p = NULL;
}
mod_data.protocol_name, mod_data.protocol_type);
DBG(fsg, "VendorID=x%04x, ProductID=x%04x, Release=x%04x\n",
mod_data.vendor, mod_data.product, mod_data.release);
- DBG(fsg, "removable=%d, stall=%d, buflen=%u\n",
+ DBG(fsg, "removable=%d, stall=%d, cdrom=%d, buflen=%u\n",
mod_data.removable, mod_data.can_stall,
- mod_data.buflen);
- DBG(fsg, "I/O thread pid: %d\n", fsg->thread_pid);
+ mod_data.cdrom, mod_data.buflen);
+ DBG(fsg, "I/O thread pid: %d\n", task_pid_nr(fsg->thread_task));
+
+ set_bit(REGISTERED, &fsg->atomic_bitflags);
+
+ /* Tell the thread to start working */
+ wake_up_process(fsg->thread_task);
return 0;
autoconf_fail:
fsg->state = FSG_STATE_TERMINATED; // The thread is dead
fsg_unbind(gadget);
close_all_backing_files(fsg);
+ complete(&fsg->thread_notifier);
return rc;
}
.driver = {
.name = (char *) shortname,
+ .owner = THIS_MODULE,
// .release = ...
// .suspend = ...
// .resume = ...
{
struct fsg_dev *fsg;
- fsg = kmalloc(sizeof *fsg, GFP_KERNEL);
+ fsg = kzalloc(sizeof *fsg, GFP_KERNEL);
if (!fsg)
return -ENOMEM;
- memset(fsg, 0, sizeof *fsg);
spin_lock_init(&fsg->lock);
init_rwsem(&fsg->filesem);
- init_waitqueue_head(&fsg->thread_wqh);
+ kref_init(&fsg->ref);
init_completion(&fsg->thread_notifier);
the_fsg = fsg;
}
-static void fsg_free(struct fsg_dev *fsg)
-{
- kfree(fsg->luns);
- kfree(fsg);
-}
-
-
static int __init fsg_init(void)
{
int rc;
if ((rc = fsg_alloc()) != 0)
return rc;
fsg = the_fsg;
- if ((rc = usb_gadget_register_driver(&fsg_driver)) != 0) {
- fsg_free(fsg);
- return rc;
- }
- set_bit(REGISTERED, &fsg->atomic_bitflags);
-
- /* Tell the thread to start working */
- complete(&fsg->thread_notifier);
- return 0;
+ if ((rc = usb_gadget_register_driver(&fsg_driver)) != 0)
+ kref_put(&fsg->ref, fsg_release);
+ return rc;
}
module_init(fsg_init);
wait_for_completion(&fsg->thread_notifier);
close_all_backing_files(fsg);
- fsg_free(fsg);
+ kref_put(&fsg->ref, fsg_release);
}
module_exit(fsg_cleanup);