The f_mass_storage.c has been changed into a composite function. mass_storage.c file has been introduced which defines a g_mass_storage gadget based on composite framework. This patch is a beta stage, the mass storage function and gadget needs more work and clean ups. Signed-off-by: Michal Nazarewicz <mnazarewicz@xxxxxxxxxxx> --- drivers/usb/gadget/Kconfig | 18 + drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/f_mass_storage.c | 1026 ++++++++++++----------------------- drivers/usb/gadget/file_storage.c | 2 +- drivers/usb/gadget/mass_storage.c | 235 ++++++++ drivers/usb/gadget/storage_common.c | 13 +- 6 files changed, 609 insertions(+), 687 deletions(-) create mode 100644 drivers/usb/gadget/mass_storage.c diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index cb0bb84..e36a71e 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -668,6 +668,24 @@ config USB_FILE_STORAGE_TEST behavior of USB Mass Storage hosts. Not needed for normal operation. +config USB_MASS_STORAGE + tristate "Mass Storage Gadget" + depends on BLOCK + help + The Mass Storage Gadget acts as a USB Mass Storage disk drive. + As its storage repository it can use a regular file or a block + device (in much the same way as the "loop" device driver), + specified as a module parameter or sysfs option. + + This is heavily based on File-backed Storage Gadget and in most + cases you will want to use FSG instead. This gadget is mostly + here to test the functionality of the Mass Storage Function + which may be used with composite framework. + + Say "y" to link the driver statically, or "m" to build + a dynamically linked module called "g_file_storage". If unsure, + consider File-backed Storage Gadget. + config USB_G_SERIAL tristate "Serial Gadget (with CDC ACM and CDC OBEX support)" help diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index b653183..93c9615 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -32,6 +32,7 @@ g_serial-objs := serial.o g_midi-objs := gmidi.o gadgetfs-objs := inode.o g_file_storage-objs := file_storage.o +g_mass_storage-objs := mass_storage.o g_printer-objs := printer.o g_cdc-objs := cdc2.o @@ -39,6 +40,7 @@ obj-$(CONFIG_USB_ZERO) += g_zero.o obj-$(CONFIG_USB_ETH) += g_ether.o obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o +obj-$(CONFIG_USB_MASS_STORAGE) += g_mass_storage.o obj-$(CONFIG_USB_G_SERIAL) += g_serial.o obj-$(CONFIG_USB_G_PRINTER) += g_printer.o obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index c245ce9..d9cb0fb 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -242,46 +242,10 @@ #include "gadget_chips.h" - -/*-------------------------------------------------------------------------*/ - -#define DRIVER_DESC "File-backed Storage Gadget" -#define DRIVER_NAME "g_file_storage" -#define DRIVER_VERSION "20 November 2008" - -static char stor_string_manufacturer[64]; -static const char stor_string_product[] = DRIVER_DESC; -static char stor_string_serial[13]; -static char stor_string_config[] = "Self Powered"; static const char stor_string_interface[] = "Mass Storage"; -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_AUTHOR("Alan Stern"); -MODULE_LICENSE("Dual BSD/GPL"); - -/* - * 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. - */ - - -/*-------------------------------------------------------------------------*/ - - -/* - * 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 STOR_DESCRIPTORS_DEVICE_STRINGS 1 #define STOR_BUFFHD_STATIC_BUFFER 1 +#define STOR_DESCRIPTORS_NO_OTG 1 #define STORAGE_BUFLEN ((u32)16384) #include "storage_common.c" @@ -331,6 +295,8 @@ MODULE_PARM_DESC(cdrom, "true to emulate cdrom instead of disk"); /* Data shared by all the MSF instances. */ struct msf_common { + struct usb_gadget *gadget; /* Copy of cdev->gadget */ + /* filesem protects: backing files in use */ struct rw_semaphore filesem; @@ -345,21 +311,24 @@ struct msf_common { unsigned int lun; struct storage_lun *luns; struct storage_lun *curlun; + + struct kref ref; }; struct msf { + struct usb_function function; + struct usb_composite_dev*cdev; + struct usb_gadget *gadget; /* Copy of cdev->gadget */ struct msf_common *common; + u16 interfaceNumber; + /* lock protects: state, all the req_busy's */ spinlock_t lock; - struct usb_gadget *gadget; - /* 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 + struct usb_ep *ep0; /* Copy of gadget->ep0 */ + struct usb_request *ep0req; /* Copy of cdev->req */ unsigned int ep0_req_tag; const char *ep0req_name; @@ -400,9 +369,15 @@ typedef struct msf storage_instance; #include "storage_common2.c" +static inline struct msf *msf_from_func(struct usb_function *f) +{ + return container_of(f, struct msf, function); +} + + typedef void (*msf_routine_t)(struct msf *); -static int msf_exception_in_progress(struct msf *msf) +static inline int msf_exception_in_progress(struct msf *msf) { return msf->state > STOR_STATE_IDLE; } @@ -420,9 +395,6 @@ static void msf_set_bulk_out_req_length(struct msf *msf, bh->outreq->length = length; } -static struct msf *the_msf; -static struct usb_gadget_driver msf_driver; - /*-------------------------------------------------------------------------*/ @@ -452,97 +424,6 @@ static int msf_set_halt(struct msf *msf, struct usb_ep *ep) /*-------------------------------------------------------------------------*/ -/* - * DESCRIPTORS ... most are static, but strings and (full) configuration - * descriptors are built on demand. Also the (static) config and interface - * descriptors are adjusted during msf_bind(). - */ - -/* Some of the descriptors are defined in storage_common.c file. */ - -/* There is only one configuration. */ -#define CONFIG_VALUE 1 - -static struct usb_device_descriptor -device_desc = { - .bLength = sizeof device_desc, - .bDescriptorType = USB_DT_DEVICE, - - .bcdUSB = cpu_to_le16(0x0200), - .bDeviceClass = USB_CLASS_PER_INTERFACE, - - /* The next three values can be overridden by module parameters */ - .idVendor = cpu_to_le16(STORAGE_VENDOR_ID), - .idProduct = cpu_to_le16(STORAGE_PRODUCT_ID), - .bcdDevice = cpu_to_le16(0xffff), - - .iManufacturer = STOR_STRING_MANUFACTURER, - .iProduct = STOR_STRING_PRODUCT, - .iSerialNumber = STOR_STRING_SERIAL, - .bNumConfigurations = 1, -}; - -static struct usb_config_descriptor -config_desc = { - .bLength = sizeof config_desc, - .bDescriptorType = USB_DT_CONFIG, - - /* wTotalLength computed by usb_gadget_config_buf() */ - .bNumInterfaces = 1, - .bConfigurationValue = CONFIG_VALUE, - .iConfiguration = STOR_STRING_CONFIG, - .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2, -}; - - -static struct usb_qualifier_descriptor -dev_qualifier = { - .bLength = sizeof dev_qualifier, - .bDescriptorType = USB_DT_DEVICE_QUALIFIER, - - .bcdUSB = cpu_to_le16(0x0200), - .bDeviceClass = USB_CLASS_PER_INTERFACE, - - .bNumConfigurations = 1, -}; - - - -/* - * Config descriptors must agree with the code that sets configurations - * and with code managing interfaces and their altsettings. They must - * also handle different speeds and other-speed requests. - */ -static int msf_populate_config_buf(struct usb_gadget *gadget, - u8 *buf, u8 type, unsigned index) -{ - enum usb_device_speed speed = gadget->speed; - int len; - const struct usb_descriptor_header **function; - - if (index > 0) - return -EINVAL; - - if (gadget_is_dualspeed(gadget) && type == USB_DT_OTHER_SPEED_CONFIG) - speed = (USB_SPEED_FULL + USB_SPEED_HIGH) - speed; - if (gadget_is_dualspeed(gadget) && speed == USB_SPEED_HIGH) - function = stor_hs_function; - else - function = stor_fs_function; - - /* for now, don't advertise srp-only devices */ - if (!gadget_is_otg(gadget)) - function++; - - len = usb_gadget_config_buf(&config_desc, buf, EP0_BUFSIZE, function); - ((struct usb_config_descriptor *) buf)->bDescriptorType = type; - return len; -} - - -/*-------------------------------------------------------------------------*/ - /* These routines may be called in process context or in_irq */ /* Caller must hold msf->lock */ @@ -550,7 +431,7 @@ static void stor_wakeup_thread(struct msf *msf) { /* Tell the main thread that something has happened */ msf->thread_wakeup_needed = 1; - if (msf->thread_task) + if (likely(msf->thread_task)) wake_up_process(msf->thread_task); } @@ -566,9 +447,9 @@ static void msf_raise_exception(struct msf *msf, enum stor_state new_state) if (msf->state <= new_state) { msf->exception_req_tag = msf->ep0_req_tag; msf->state = new_state; - if (msf->thread_task) + if (likely(msf->thread_task)) send_sig_info(SIGUSR1, SEND_SIG_FORCED, - msf->thread_task); + msf->thread_task); } spin_unlock_irqrestore(&msf->lock, flags); } @@ -576,63 +457,30 @@ static void msf_raise_exception(struct msf *msf, enum stor_state new_state) /*-------------------------------------------------------------------------*/ -/* The disconnect callback and ep0 routines. These always run in_irq, - * except that ep0_queue() is called in the main thread to acknowledge - * completion of various requests: set config, set interface, and - * Bulk-only device reset. */ - -static void msf_disconnect(struct usb_gadget *gadget) -{ - struct msf *msf = get_gadget_data(gadget); - - SDBG(msf, "disconnect or port reset\n"); - msf_raise_exception(msf, STOR_STATE_DISCONNECT); -} - - static int msf_ep0_queue(struct msf *msf) { - int rc; - - rc = usb_ep_queue(msf->ep0, msf->ep0req, GFP_ATOMIC); - if (rc != 0 && rc != -ESHUTDOWN) { - + int rc = usb_ep_queue(msf->ep0, msf->ep0req, GFP_ATOMIC); + msf->ep0->driver_data = msf; + if (unlikely(rc != 0 && rc != -ESHUTDOWN)) /* We can't do much more than wait for a reset */ SWARN(msf, "error in submission: %s --> %d\n", msf->ep0->name, rc); - } return rc; } -static void msf_ep0_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct msf *msf = ep->driver_data; - - if (req->actual > 0) - dump_msg(msf, msf->ep0req_name, req->buf, req->actual); - if (req->status || req->actual != req->length) - SDBG(msf, "%s --> %d, %u/%u\n", __func__, - req->status, req->actual, req->length); - if (req->status == -ECONNRESET) // Request was cancelled - usb_ep_fifo_flush(ep); - - if (req->status == 0 && req->context) - ((msf_routine_t) (req->context))(msf); -} - - /*-------------------------------------------------------------------------*/ /* Ep0 class-specific handlers. These always run in_irq. */ -static int msf_class_setup_req(struct msf *msf, - const struct usb_ctrlrequest *ctrl) +static int msf_setup(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) { + struct msf *msf = msf_from_func(f); struct usb_request *req = msf->ep0req; int value; - u16 w_index = le16_to_cpu(ctrl->wIndex); - u16 w_value = le16_to_cpu(ctrl->wValue); - u16 w_length = le16_to_cpu(ctrl->wLength); + u16 wIndex = le16_to_cpu(ctrl->wIndex); + u16 wValue = le16_to_cpu(ctrl->wValue); + u16 wLength = le16_to_cpu(ctrl->wLength); if (!msf->config) return -EOPNOTSUPP; @@ -644,7 +492,7 @@ static int msf_class_setup_req(struct msf *msf, if (ctrl->bRequestType != (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) goto eopnotsupp; - if (w_index != 0 || w_value != 0) { + if (wIndex != msf->interfaceNumber || wValue != 0) { value = -EDOM; break; } @@ -660,7 +508,7 @@ static int msf_class_setup_req(struct msf *msf, if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) goto eopnotsupp; - if (w_index != 0 || w_value != 0) { + if (wIndex != msf->interfaceNumber || wValue != 0) { value = -EDOM; break; } @@ -675,7 +523,7 @@ eopnotsupp: "unknown class-specific control req " "%02x.%02x v%04x i%04x l%u\n", ctrl->bRequestType, ctrl->bRequest, - le16_to_cpu(ctrl->wValue), w_index, w_length); + le16_to_cpu(ctrl->wValue), wIndex, wLength); value = -EOPNOTSUPP; } @@ -685,160 +533,6 @@ eopnotsupp: /*-------------------------------------------------------------------------*/ -/* Ep0 standard request handlers. These always run in_irq. */ - -static int msf_standard_setup_req(struct msf *msf, - const struct usb_ctrlrequest *ctrl) -{ - struct usb_request *req = msf->ep0req; - int value = -EOPNOTSUPP; - 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. */ - switch (ctrl->bRequest) { - - case USB_REQ_GET_DESCRIPTOR: - if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | - USB_RECIP_DEVICE)) - break; - switch (w_value >> 8) { - - case USB_DT_DEVICE: - VSDBG(msf, "get device descriptor\n"); - value = sizeof device_desc; - memcpy(req->buf, &device_desc, value); - break; - case USB_DT_DEVICE_QUALIFIER: - VSDBG(msf, "get device qualifier\n"); - if (!gadget_is_dualspeed(msf->gadget)) - break; - value = sizeof dev_qualifier; - memcpy(req->buf, &dev_qualifier, value); - break; - - case USB_DT_OTHER_SPEED_CONFIG: - VSDBG(msf, "get other-speed config descriptor\n"); - if (!gadget_is_dualspeed(msf->gadget)) - break; - goto get_config; - case USB_DT_CONFIG: - VSDBG(msf, "get configuration descriptor\n"); -get_config: - value = msf_populate_config_buf(msf->gadget, - req->buf, - w_value >> 8, - w_value & 0xff); - break; - - case USB_DT_STRING: - VSDBG(msf, "get string descriptor\n"); - - /* wIndex == language code */ - value = usb_gadget_get_string(&stor_stringtab, - w_value & 0xff, req->buf); - break; - } - break; - - /* One config, two speeds */ - case USB_REQ_SET_CONFIGURATION: - if (ctrl->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD | - USB_RECIP_DEVICE)) - break; - VSDBG(msf, "set configuration\n"); - if (w_value == CONFIG_VALUE || w_value == 0) { - msf->new_config = w_value; - - /* Raise an exception to wipe out previous transaction - * state (queued bufs, etc) and set the new config. */ - msf_raise_exception(msf, STOR_STATE_CONFIG_CHANGE); - value = DELAYED_STATUS; - } - break; - case USB_REQ_GET_CONFIGURATION: - if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | - USB_RECIP_DEVICE)) - break; - VSDBG(msf, "get configuration\n"); - *(u8 *) req->buf = msf->config; - value = 1; - break; - - case USB_REQ_SET_INTERFACE: - if (ctrl->bRequestType != (USB_DIR_OUT| USB_TYPE_STANDARD | - USB_RECIP_INTERFACE)) - break; - if (msf->config && w_index == 0) { - - /* Raise an exception to wipe out previous transaction - * state (queued bufs, etc) and install the new - * interface altsetting. */ - msf_raise_exception(msf, STOR_STATE_INTERFACE_CHANGE); - value = DELAYED_STATUS; - } - break; - case USB_REQ_GET_INTERFACE: - if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | - USB_RECIP_INTERFACE)) - break; - if (!msf->config) - break; - if (w_index != 0) { - value = -EDOM; - break; - } - VSDBG(msf, "get interface\n"); - *(u8 *) req->buf = 0; - value = 1; - break; - - default: - VSDBG(msf, - "unknown control req %02x.%02x v%04x i%04x l%u\n", - ctrl->bRequestType, ctrl->bRequest, - w_value, w_index, le16_to_cpu(ctrl->wLength)); - } - - return value; -} - - -static int msf_setup(struct usb_gadget *gadget, - const struct usb_ctrlrequest *ctrl) -{ - struct msf *msf = get_gadget_data(gadget); - int rc; - int w_length = le16_to_cpu(ctrl->wLength); - - ++msf->ep0_req_tag; // Record arrival of a new request - msf->ep0req->context = NULL; - msf->ep0req->length = 0; - dump_msg(msf, "ep0-setup", (u8 *) ctrl, sizeof(*ctrl)); - - if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) - rc = msf_class_setup_req(msf, ctrl); - else - rc = msf_standard_setup_req(msf, ctrl); - - /* Respond with data/status or defer until later? */ - if (rc >= 0 && rc != DELAYED_STATUS) { - rc = min(rc, w_length); - msf->ep0req->length = rc; - msf->ep0req->zero = rc < w_length; - msf->ep0req_name = (ctrl->bRequestType & USB_DIR_IN ? - "ep0-in" : "ep0-out"); - rc = msf_ep0_queue(msf); - } - - /* Device either stalls (rc < 0) or reports success */ - return rc; -} - - -/*-------------------------------------------------------------------------*/ - /* All the following routines run in process context */ @@ -1013,7 +707,7 @@ static int msf_do_read(struct msf *msf) /* Send this buffer and go read some more */ bh->inreq->zero = 0; msf_start_transfer(msf, msf->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); + &bh->inreq_busy, &bh->state); msf->common->next_buffhd_to_fill = bh->next; } @@ -1025,7 +719,7 @@ static int msf_do_read(struct msf *msf) static int msf_do_write(struct msf *msf) { - struct storage_lun *curlun = msf->common->curlun; + struct storage_lun *curlun = msf->common->curlun; u32 lba; struct stor_buffhd *bh; int get_some_more; @@ -1704,7 +1398,7 @@ static int msf_pad_with_zeros(struct msf *msf) bh->inreq->length = nsend; bh->inreq->zero = 0; msf_start_transfer(msf, msf->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); + &bh->inreq_busy, &bh->state); bh = msf->common->next_buffhd_to_fill = bh->next; msf->usb_amount_left -= nsend; nkeep = 0; @@ -1780,6 +1474,7 @@ static int msf_finish_reply(struct msf *msf) msf_set_halt(msf, msf->bulk_out); rc = msf_halt_bulk_in_endpoint(msf); } + rc = -EINVAL; break; /* All but the last buffer of data must have already been sent */ @@ -1791,7 +1486,7 @@ static int msf_finish_reply(struct msf *msf) else if (msf->residue == 0) { bh->inreq->zero = 0; msf_start_transfer(msf, msf->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); + &bh->inreq_busy, &bh->state); msf->common->next_buffhd_to_fill = bh->next; /* There is a residue. For CB and CBI, simply mark the end @@ -1807,7 +1502,7 @@ static int msf_finish_reply(struct msf *msf) } else if (mod_data.can_stall) { bh->inreq->zero = 1; msf_start_transfer(msf, msf->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); + &bh->inreq_busy, &bh->state); msf->common->next_buffhd_to_fill = bh->next; rc = msf_halt_bulk_in_endpoint(msf); } else { @@ -1833,13 +1528,11 @@ static int msf_finish_reply(struct msf *msf) * 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. */ -#if 0 else if (mod_data.can_stall) { msf_set_halt(msf, msf->bulk_out); msf_raise_exception(msf, STOR_STATE_ABORT_BULK_OUT); rc = -EINTR; } -#endif /* We can't stall. Read in the excess data and throw it * all away. */ @@ -1899,7 +1592,7 @@ static int msf_send_status(struct msf *msf) bh->inreq->length = USB_BULK_CS_WRAP_LEN; bh->inreq->zero = 0; msf_start_transfer(msf, msf->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); + &bh->inreq_busy, &bh->state); msf->common->next_buffhd_to_fill = bh->next; return 0; @@ -2292,8 +1985,7 @@ static int msf_received_cbw(struct msf *msf, struct stor_buffhd *bh) /* Is the CBW valid? */ if (req->actual != USB_BULK_CB_WRAP_LEN || - cbw->Signature != cpu_to_le32( - USB_BULK_CB_SIG)) { + cbw->Signature != cpu_to_le32(USB_BULK_CB_SIG)) { SDBG(msf, "invalid CBW: len %u sig 0x%x\n", req->actual, le32_to_cpu(cbw->Signature)); @@ -2314,7 +2006,7 @@ static int msf_received_cbw(struct msf *msf, struct stor_buffhd *bh) /* Is the CBW meaningful? */ if (cbw->Lun >= STORAGE_MAX_LUNS || cbw->Flags & ~USB_BULK_IN_FLAG || - cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) { + cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) { SDBG(msf, "non-meaningful CBW: lun = %u, flags = 0x%x, " "cmdlen %u\n", cbw->Lun, cbw->Flags, cbw->Length); @@ -2451,12 +2143,13 @@ reset: SDBG(msf, "set interface %d\n", altsetting); /* Enable the endpoints */ - d = ep_desc(msf->gadget, &stor_fs_bulk_in_desc, &stor_hs_bulk_in_desc); + d = ep_desc(msf->cdev->gadget, + &stor_fs_bulk_in_desc, &stor_hs_bulk_in_desc); if ((rc = msf_enable_endpoint(msf, msf->bulk_in, d)) != 0) goto reset; msf->bulk_in_enabled = 1; - d = ep_desc(msf->gadget, + d = ep_desc(msf->cdev->gadget, &stor_fs_bulk_out_desc, &stor_hs_bulk_out_desc); if ((rc = msf_enable_endpoint(msf, msf->bulk_out, d)) != 0) goto reset; @@ -2507,24 +2200,34 @@ static int msf_do_set_config(struct msf *msf, u8 new_config) /* Enable the interface */ if (new_config != 0) { msf->config = new_config; - if ((rc = msf_do_set_interface(msf, 0)) != 0) - msf->config = 0; // Reset on errors - else { - char *speed; - - switch (msf->gadget->speed) { - case USB_SPEED_LOW: speed = "low"; break; - case USB_SPEED_FULL: speed = "full"; break; - case USB_SPEED_HIGH: speed = "high"; break; - default: speed = "?"; break; - } - SINFO(msf, "%s speed config #%d\n", speed, msf->config); - } + rc = msf_do_set_interface(msf, 0); + if (rc) + msf->config = 0; /* Reset on errors */ + else + SDBG(msf, "config #%d\n", msf->config); } return rc; } +/****************************** ALT CONFIGS ******************************/ + + +static int msf_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct msf *msf = msf_from_func(f); + msf->new_config = 1; + msf_raise_exception(msf, STOR_STATE_CONFIG_CHANGE); + return 0; +} + +static void msf_disable(struct usb_function *f) +{ + struct msf *msf = msf_from_func(f); + msf->new_config = 0; + msf_raise_exception(msf, STOR_STATE_CONFIG_CHANGE); +} + /*-------------------------------------------------------------------------*/ static void msf_handle_exception(struct msf *msf) @@ -2612,9 +2315,6 @@ static void msf_handle_exception(struct msf *msf) /* Carry out any extra actions required for the exception */ switch (old_state) { - default: - break; - case STOR_STATE_ABORT_BULK_OUT: msf_send_status(msf); spin_lock_irq(&msf->lock); @@ -2640,16 +2340,6 @@ static void msf_handle_exception(struct msf *msf) // msf->common->luns[i].unit_attention_data = SS_RESET_OCCURRED; break; - case STOR_STATE_INTERFACE_CHANGE: - rc = msf_do_set_interface(msf, 0); - if (msf->ep0_req_tag != exception_req_tag) - break; - if (rc != 0) // STALL on errors - msf_set_halt(msf, msf->ep0); - else // Complete the status stage - msf_ep0_queue(msf); - break; - case STOR_STATE_CONFIG_CHANGE: rc = msf_do_set_config(msf, new_config); if (msf->ep0_req_tag != exception_req_tag) @@ -2660,12 +2350,6 @@ static void msf_handle_exception(struct msf *msf) msf_ep0_queue(msf); break; - case STOR_STATE_DISCONNECT: - for (i = 0; i < msf->common->nluns; ++i) - stor_lun_fsync_sub(&msf->common->luns[i]); - msf_do_set_config(msf, 0); // Unconfigured state - break; - case STOR_STATE_EXIT: case STOR_STATE_TERMINATED: msf_do_set_config(msf, 0); // Free resources @@ -2673,6 +2357,14 @@ static void msf_handle_exception(struct msf *msf) msf->state = STOR_STATE_TERMINATED; // Stop the thread spin_unlock_irq(&msf->lock); break; + + case STOR_STATE_INTERFACE_CHANGE: + case STOR_STATE_DISCONNECT: + case STOR_STATE_COMMAND_PHASE: + case STOR_STATE_DATA_PHASE: + case STOR_STATE_STATUS_PHASE: + case STOR_STATE_IDLE: + break; } } @@ -2733,7 +2425,7 @@ static int msf_main_thread(void *msf_) if (!msf_exception_in_progress(msf)) msf->state = STOR_STATE_IDLE; spin_unlock_irq(&msf->lock); - } + } spin_lock_irq(&msf->lock); msf->thread_task = NULL; @@ -2741,8 +2433,9 @@ static int msf_main_thread(void *msf_) /* If we are exiting because of a signal, unregister the * gadget driver. */ - if (test_and_clear_bit(REGISTERED, &msf->atomic_bitflags)) - usb_gadget_unregister_driver(&msf_driver); + /* XXX */ + /* if (test_and_clear_bit(REGISTERED, &msf->atomic_bitflags)) */ + /* usb_gadget_unregister_driver(&msf_driver); */ /* Let the unbind and cleanup routines know the thread has exited */ complete_and_exit(&msf->thread_notifier, 0); @@ -2751,55 +2444,59 @@ static int msf_main_thread(void *msf_) /*-------------------------------------------------------------------------*/ -static ssize_t msf_show_ro(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t msf_show_ro(struct device *dev, + struct device_attribute *attr, char *buf) { struct storage_lun *curlun = stor_lun_from_dev(dev); return sprintf(buf, "%d\n", curlun->ro); } -static ssize_t msf_show_file(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t msf_show_file(struct device *dev, + struct device_attribute *attr, char *buf) { struct storage_lun *curlun = stor_lun_from_dev(dev); - struct msf *msf = dev_get_drvdata(dev); - char *p; - ssize_t rc; + struct msf_common *common = dev_get_drvdata(dev); + char *p; + ssize_t rc; - down_read(&msf->common->filesem); - if (stor_lun_is_open(curlun)) { // Get the complete pathname + down_read(&common->filesem); + if (stor_lun_is_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] = '\n'; /* Add a newline */ buf[++rc] = 0; } - } else { // No file, return 0 bytes + } else { /* No file, return 0 bytes */ *buf = 0; rc = 0; } - up_read(&msf->common->filesem); + up_read(&common->filesem); return rc; } static ssize_t msf_store_ro(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { - ssize_t rc = count; + ssize_t rc = count; struct storage_lun *curlun = stor_lun_from_dev(dev); - struct msf *msf = dev_get_drvdata(dev); - int i; + struct msf_common *common = dev_get_drvdata(dev); + int i; + + if (curlun->cdrom) + return -EPERM; if (sscanf(buf, "%d", &i) != 1) return -EINVAL; - /* Allow the write-enable status to change only while the backing file - * is closed. */ - down_read(&msf->common->filesem); + /* Allow the write-enable status to change only while the + * backing file is closed. */ + down_read(&common->filesem); if (stor_lun_is_open(curlun)) { LDBG(curlun, "read-only status change prevented\n"); rc = -EBUSY; @@ -2807,7 +2504,7 @@ static ssize_t msf_store_ro(struct device *dev, struct device_attribute *attr, curlun->ro = !!i; LDBG(curlun, "read-only status set to %d\n", curlun->ro); } - up_read(&msf->common->filesem); + up_read(&common->filesem); return rc; } @@ -2815,295 +2512,298 @@ static ssize_t msf_store_file(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct storage_lun *curlun = stor_lun_from_dev(dev); - struct msf *msf = dev_get_drvdata(dev); - int rc = 0; + struct msf_common *common = dev_get_drvdata(dev); + int rc = 0; + + if (!curlun->removable) + return -EPERM; if (curlun->prevent_medium_removal && stor_lun_is_open(curlun)) { LDBG(curlun, "eject attempt prevented\n"); - return -EBUSY; // "Door is locked" + return -EBUSY; /* "Door is locked" */ } - /* Remove a trailing newline */ - if (count > 0 && buf[count-1] == '\n') - ((char *) buf)[count-1] = 0; // Ugh! - /* Eject current medium */ - down_write(&msf->common->filesem); + down_write(&common->filesem); if (stor_lun_is_open(curlun)) { stor_lun_close(curlun); curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT; } /* Load new medium */ - if (count > 0 && buf[0]) { - rc = stor_lun_open(curlun, buf); - if (rc == 0) - curlun->unit_attention_data = - SS_NOT_READY_TO_READY_TRANSITION; + if (count == 0) + goto skip_open; + + if (buf[count-1] == '\n') { + if (count == 1) + goto skip_open; + ((char *) buf)[count - 1] = 0; /* Ugh! */ } - up_write(&msf->common->filesem); + + rc = stor_lun_open(curlun, buf); + if (rc == 0) + curlun->unit_attention_data = + SS_NOT_READY_TO_READY_TRANSITION; + +skip_open: + up_write(&common->filesem); return (rc < 0 ? rc : count); } +/* Write permission is checked per LUN in store_*() functions. */ +static DEVICE_ATTR(ro, 0644, msf_show_ro, msf_store_ro); +static DEVICE_ATTR(file, 0644, msf_show_file, msf_store_file); -/* The write permissions and msf_store_xxx pointers are set in msf_bind() */ -static DEVICE_ATTR(ro, 0444, msf_show_ro, NULL); -static DEVICE_ATTR(file, 0444, msf_show_file, NULL); +/****************************** FSF COMMON ******************************/ -/*-------------------------------------------------------------------------*/ -static void msf_release(struct kref *ref) -{ - struct msf *msf = container_of(ref, struct msf, ref); +static void msf_common_release(struct kref *ref); +static void stor_lun_release(struct device *dev); - kfree(msf->common->luns); - kfree(msf); -} +static struct msf_common *msf_common_init(struct usb_composite_dev *cdev); -static void stor_lun_release(struct device *dev) +static inline void msf_common_get(struct msf_common *common) { - struct msf *msf = dev_get_drvdata(dev); - - kref_put(&msf->ref, msf_release); + kref_get(&common->ref); } -static void /* __init_or_exit */ msf_unbind(struct usb_gadget *gadget) +static inline void msf_common_put(struct msf_common *common) { - struct msf *msf = get_gadget_data(gadget); - int i; - struct storage_lun *curlun; - struct usb_request *req = msf->ep0req; + kref_put(&common->ref, msf_common_release); +} - SDBG(msf, "unbind\n"); - clear_bit(REGISTERED, &msf->atomic_bitflags); - /* Unregister the sysfs attribute files and the LUNs */ - for (i = 0; i < msf->common->nluns; ++i) { - curlun = &msf->common->luns[i]; - if (curlun->registered) { - device_remove_file(&curlun->dev, &dev_attr_ro); - device_remove_file(&curlun->dev, &dev_attr_file); - stor_lun_close(curlun); - device_unregister(&curlun->dev); - curlun->registered = 0; - } +static struct msf_common *msf_common_init(struct usb_composite_dev *cdev) +{ + struct msf_common *common; + struct stor_buffhd *bh; + struct storage_lun *lun; + int nluns, rc, i; + + /* Find out how many LUNs there should be */ + nluns = mod_data.nluns; + if (nluns == 0) + nluns = max(mod_data.num_filenames, 1u); + if (nluns < 1 || nluns > STORAGE_MAX_LUNS) { + ERROR(cdev, "invalid number of LUNs: %u\n", nluns); + return ERR_PTR(-EINVAL); } - /* If the thread isn't already dead, tell it to exit now */ - if (msf->state != STOR_STATE_TERMINATED) { - msf_raise_exception(msf, STOR_STATE_EXIT); - wait_for_completion(&msf->thread_notifier); - /* The cleanup routine waits for this completion also */ - complete(&msf->thread_notifier); - } + common = kzalloc(sizeof *common, GFP_KERNEL); + if (!common) + return ERR_PTR(-ENOMEM); + common->gadget = cdev->gadget; - /* Free the request and buffer for endpoint 0 */ - if (req) { - kfree(req->buf); - usb_ep_free_request(msf->ep0, req); - } + /* 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; + } + + for (i = 0; i < nluns; ++i, ++lun) { + lun->cdrom = mod_data.cdrom; + lun->removable = mod_data.cdrom || mod_data.removable; + lun->ro = mod_data.cdrom || mod_data.ro[i]; + lun->dev.release = stor_lun_release; + lun->dev.parent = &cdev->gadget->dev; + dev_set_drvdata(&lun->dev, common); + dev_set_name(&lun->dev, "lun%d", i); + + rc = device_register(&lun->dev); + if (rc) { + SINFO(common, "failed to register LUN%d: %d\n", i, rc); + common->nluns = i; + goto error_release_common; + } - set_gadget_data(gadget, NULL); -} + rc = device_create_file(&lun->dev, &dev_attr_ro); + if (rc) + goto lun_error; + rc = device_create_file(&lun->dev, &dev_attr_file); + if (rc) + goto lun_error; -static int __init msf_bind(struct usb_gadget *gadget) -{ - struct msf *msf = the_msf; - int rc; - int i; - struct storage_lun *curlun; - struct usb_ep *ep; - struct usb_request *req; - char *pathbuf, *p; + if (mod_data.file[i] && *mod_data.file[i]) { + rc = stor_lun_open(lun, mod_data.file[i]); + if (rc) + goto lun_error; + } else if (!lun->removable) { + SERROR(common, "no file given for LUN%d\n", i); + rc = -EINVAL; +lun_error: + common->nluns = i + 1; + goto error_release_common; + } + } + common->nluns = nluns; - msf->gadget = gadget; - set_gadget_data(gadget, msf); - msf->ep0 = gadget->ep0; - msf->ep0->driver_data = msf; - if (gadget_is_sh(msf->gadget) || gadget_is_at91(msf->gadget)) - mod_data.can_stall = 0; + /* Data buffers cyclic list */ + /* Buffers in buffhds are static -- no need for additional + * allocation. */ + bh = common->buffhds; + i = STORAGE_NUM_BUFFERS - 1; + do { + bh->next = bh + 1; + } while (++bh, --i); + bh->next = common->buffhds; + if (mod_data.release == 0xffff) { // Parameter wasn't set int gcnum; /* The sa1100 controller is not supported */ - if (gadget_is_sa1100(msf->gadget)) + if (gadget_is_sa1100(gadget)) gcnum = -1; else - gcnum = usb_gadget_controller_number(msf->gadget); + gcnum = usb_gadget_controller_number(cdev->gadget); if (gcnum >= 0) mod_data.release = 0x0300 + gcnum; else { - SWARN(msf, "controller '%s' not recognized\n", - msf->gadget->name); + SWARN(common, "controller '%s' not recognized\n", + cdev->gadget->name); mod_data.release = 0x0399; } } - if (mod_data.removable) { // Enable the msf_store_xxx attributes - dev_attr_file.attr.mode = 0644; - dev_attr_file.store = msf_store_file; - if (!mod_data.cdrom) { - dev_attr_ro.attr.mode = 0644; - dev_attr_ro.store = msf_store_ro; - } - } + init_rwsem(&common->filesem); + kref_init(&common->ref); + return common; - /* Find out how many LUNs there should be */ - i = mod_data.nluns; - if (i == 0) - i = max(mod_data.num_filenames, 1u); - if (i > STORAGE_MAX_LUNS) { - SERROR(msf, "invalid number of LUNs: %d\n", i); - rc = -EINVAL; - goto out; - } - /* Create the LUNs, open their backing files, and register the - * LUN devices in sysfs. */ - msf->common->luns = kzalloc(i * sizeof(struct storage_lun), GFP_KERNEL); - if (!msf->common->luns) { - rc = -ENOMEM; - goto out; +error_release_common: + /* Here, common->nluns may differ from cfg->nluns as it is set + * to the number of registered LUNs prior to going to this + * label. */ + /* We call msf_common_release directly since kref is not + * initialised. */ + msf_common_release(&common->ref); + return ERR_PTR(rc); + +error_free_common: + kfree(common); + return ERR_PTR(rc); +} + + + +static void msf_common_release(struct kref *ref) +{ + struct msf_common *common = + container_of(ref, struct msf_common, ref); + unsigned i = common->nluns; + struct storage_lun *lun = common->luns; + + /* Beware tempting for -> do-while optimization: when in error + * recovery nluns may be zero. */ + + for (; i; --i, ++lun) { + device_remove_file(&lun->dev, &dev_attr_ro); + device_remove_file(&lun->dev, &dev_attr_file); + stor_lun_close(lun); + device_unregister(&lun->dev); } - msf->common->nluns = i; - - for (i = 0; i < msf->common->nluns; ++i) { - curlun = &msf->common->luns[i]; - curlun->cdrom = mod_data.cdrom; - curlun->ro = mod_data.cdrom || mod_data.ro[i]; - curlun->removable = mod_data.removable; - curlun->dev.release = stor_lun_release; - curlun->dev.parent = &gadget->dev; - curlun->dev.driver = &msf_driver.driver; - dev_set_drvdata(&curlun->dev, msf); - dev_set_name(&curlun->dev,"%s-lun%d", - dev_name(&gadget->dev), i); - - if ((rc = device_register(&curlun->dev)) != 0) { - SINFO(msf, "failed to register LUN%d: %d\n", i, rc); - 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(&msf->ref); + kfree(common->luns); - if (mod_data.file[i] && *mod_data.file[i]) { - if ((rc = stor_lun_open(curlun, - mod_data.file[i])) != 0) - goto out; - } else if (!mod_data.removable) { - SERROR(msf, "no file given for LUN%d\n", i); - rc = -EINVAL; - goto out; - } + kfree(common); +} + + +static void stor_lun_release(struct device *dev) +{ + (void)dev; + /* nop */ +} + + +/*-------------------------------------------------------------------------*/ + +static void msf_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct msf *msf = msf_from_func(f); + + SDBG(msf, "unbind\n"); + clear_bit(REGISTERED, &msf->atomic_bitflags); + + /* If the thread isn't already dead, tell it to exit now */ + if (msf->state != STOR_STATE_TERMINATED) { + msf_raise_exception(msf, STOR_STATE_EXIT); + wait_for_completion(&msf->thread_notifier); + + /* The cleanup routine waits for this completion also */ + complete(&msf->thread_notifier); } + msf_common_put(msf->common); + kfree(msf); +} + + +static int msf_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct msf *msf = msf_from_func(f); + struct usb_gadget *gadget = c->cdev->gadget; + struct usb_ep *ep; + int rc, i, id; + + msf->gadget = gadget; + msf->ep0 = gadget->ep0; + msf->ep0req = c->cdev->req; + + if (mod_data.can_stall && + (gadget_is_sh(gadget) || gadget_is_at91(gadget))) + mod_data.can_stall = 0; + /* Find all the endpoints we will use */ - usb_ep_autoconfig_reset(gadget); + id = usb_interface_id(c, f); + if (id < 0) + return id; + stor_intf_desc.bInterfaceNumber = id; + msf->interfaceNumber = id; + ep = usb_ep_autoconfig(gadget, &stor_fs_bulk_in_desc); if (!ep) goto autoconf_fail; - ep->driver_data = msf; // claim the endpoint + ep->driver_data = msf; /* claim the endpoint */ msf->bulk_in = ep; ep = usb_ep_autoconfig(gadget, &stor_fs_bulk_out_desc); if (!ep) goto autoconf_fail; - ep->driver_data = msf; // claim the endpoint + ep->driver_data = msf; /* claim the endpoint */ msf->bulk_out = ep; - /* Fix up the descriptors */ - device_desc.bMaxPacketSize0 = msf->ep0->maxpacket; - /* device_desc.idVendor = cpu_to_le16(mod_data.vendor); */ - /* device_desc.idProduct = cpu_to_le16(mod_data.product); */ - device_desc.bcdDevice = cpu_to_le16(mod_data.release); - if (gadget_is_dualspeed(gadget)) { - /* Assume ep0 uses the same maxpacket value for both speeds */ - dev_qualifier.bMaxPacketSize0 = msf->ep0->maxpacket; - /* Assume endpoint addresses are the same for both speeds */ stor_hs_bulk_in_desc.bEndpointAddress = stor_fs_bulk_in_desc.bEndpointAddress; stor_hs_bulk_out_desc.bEndpointAddress = stor_fs_bulk_out_desc.bEndpointAddress; + f->hs_descriptors = stor_hs_function; } - if (gadget_is_otg(gadget)) - stor_otg_desc.bmAttributes |= USB_OTG_HNP; - - rc = -ENOMEM; - - /* Allocate the request and buffer for endpoint 0 */ - msf->ep0req = req = usb_ep_alloc_request(msf->ep0, GFP_KERNEL); - if (!req) - goto out; - req->buf = kmalloc(EP0_BUFSIZE, GFP_KERNEL); - if (!req->buf) - goto out; - req->complete = msf_ep0_complete; - - /* This should reflect the actual gadget power source */ - usb_gadget_set_selfpowered(gadget); - - snprintf(stor_string_manufacturer, sizeof stor_string_manufacturer, - "%s %s with %s", - init_utsname()->sysname, init_utsname()->release, - gadget->name); - - /* On a real device, serial[] would be loaded from permanent - * storage. We just encode it from the driver version string. */ - for (i = 0; i < sizeof(stor_string_serial) - 2; i += 2) { - unsigned char c = DRIVER_VERSION[i / 2]; - - if (!c) - break; - sprintf(&stor_string_serial[i], "%02X", c); + /* maybe allocate device-global string IDs, and patch descriptors */ + if (stor_strings[STOR_STRING_INTERFACE].id == 0) { + i = usb_string_id(c->cdev); + if (i < 0) + return i; + stor_strings[STOR_STRING_INTERFACE].id = i; + stor_intf_desc.iInterface = i; } msf->thread_task = kthread_create(msf_main_thread, msf, - "file-storage-gadget"); + "file-storage"); if (IS_ERR(msf->thread_task)) { rc = PTR_ERR(msf->thread_task); + SERROR(msf, "kthread_create failed: %d\n", rc); goto out; } - SINFO(msf, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); - SINFO(msf, "Number of LUNs=%d\n", msf->common->nluns); - - pathbuf = kmalloc(PATH_MAX, GFP_KERNEL); - for (i = 0; i < msf->common->nluns; ++i) { - curlun = &msf->common->luns[i]; - if (stor_lun_is_open(curlun)) { - p = NULL; - if (pathbuf) { - p = d_path(&curlun->filp->f_path, - pathbuf, PATH_MAX); - if (IS_ERR(p)) - p = NULL; - } - LINFO(curlun, "ro=%d, file: %s\n", - curlun->ro, (p ? p : "(error)")); - } - } - kfree(pathbuf); - - /* SDBG(msf, "VendorID=x%04x, ProductID=x%04x, Release=x%04x\n", */ - /* mod_data.vendor, mod_data.product, mod_data.release); */ - SDBG(msf, "removable=%d, stall=%d, cdrom=%d, buflen=%u\n", - mod_data.removable, mod_data.can_stall, - mod_data.cdrom, STORAGE_BUFLEN); SDBG(msf, "I/O thread pid: %d\n", task_pid_nr(msf->thread_task)); set_bit(REGISTERED, &msf->atomic_bitflags); @@ -3117,96 +2817,54 @@ autoconf_fail: rc = -ENOTSUPP; out: - msf->state = STOR_STATE_TERMINATED; // The thread is dead - msf_unbind(gadget); + msf->state = STOR_STATE_TERMINATED; /* The thread is dead */ + msf_unbind(c, f); complete(&msf->thread_notifier); return rc; } -/*-------------------------------------------------------------------------*/ -static struct usb_gadget_driver msf_driver = { -#ifdef CONFIG_USB_GADGET_DUALSPEED - .speed = USB_SPEED_HIGH, -#else - .speed = USB_SPEED_FULL, -#endif - .function = (char *)stor_string_product, - .bind = msf_bind, - .unbind = msf_unbind, - .disconnect = msf_disconnect, - .setup = msf_setup, - - .driver = { - .name = (char *) DRIVER_NAME, - .owner = THIS_MODULE, - // .release = ... - // .suspend = ... - // .resume = ... - }, -}; +/****************************** ADD FUNCTION ******************************/ +static struct usb_gadget_strings *msf_strings[] = { + &stor_stringtab, + NULL, +}; -static int __init msf_alloc(void) +static int msf_add(struct usb_composite_dev *cdev, + struct usb_configuration *c, + struct msf_common *common) { - struct msf *msf; - struct stor_buffhd *bh; - unsigned i; + struct msf *msf; + int rc; msf = kzalloc(sizeof *msf, GFP_KERNEL); - if (!msf) - return -ENOMEM; - - msf->common = kzalloc(sizeof *msf->common, GFP_KERNEL); - if (!msf->common) { - kfree(msf); + if (unlikely(!msf)) return -ENOMEM; - } - - bh = msf->common->buffhds; - i = STORAGE_NUM_BUFFERS - 1; - do { - bh->next = bh + 1; - } while (++bh, --i); - bh->next = msf->common->buffhds; spin_lock_init(&msf->lock); - init_rwsem(&msf->common->filesem); - kref_init(&msf->ref); init_completion(&msf->thread_notifier); - the_msf = msf; - return 0; -} + msf->cdev = cdev; + msf->function.name = DRIVER_DESC; + msf->function.strings = msf_strings; + msf->function.descriptors = stor_fs_function; + msf->function.bind = msf_bind; + msf->function.unbind = msf_unbind; + msf->function.setup = msf_setup; + msf->function.set_alt = msf_set_alt; + msf->function.disable = msf_disable; + msf->common = common; + msf_common_get(common); -static int __init msf_init(void) -{ - int rc; - struct msf *msf; + rc = usb_add_function(c, &msf->function); - if ((rc = msf_alloc()) != 0) - return rc; - msf = the_msf; - if ((rc = usb_gadget_register_driver(&msf_driver)) != 0) - kref_put(&msf->ref, msf_release); - return rc; -} -module_init(msf_init); - - -static void __exit msf_cleanup(void) -{ - struct msf *msf = the_msf; - - /* Unregister the driver iff the thread hasn't already done so */ - if (test_and_clear_bit(REGISTERED, &msf->atomic_bitflags)) - usb_gadget_unregister_driver(&msf_driver); - - /* Wait for the thread to finish up */ - wait_for_completion(&msf->thread_notifier); + if (unlikely(rc != 0)) { + msf_common_put(msf->common); + kfree(msf); + } - kref_put(&msf->ref, msf_release); + return rc; } -module_exit(msf_cleanup); diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index af480ef..1bf1d89 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -605,7 +605,7 @@ static int populate_config_buf(struct usb_gadget *gadget, { enum usb_device_speed speed = gadget->speed; int len; - const struct usb_descriptor_header **function; + struct usb_descriptor_header **function; if (index > 0) return -EINVAL; diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c new file mode 100644 index 0000000..494cb30 --- /dev/null +++ b/drivers/usb/gadget/mass_storage.c @@ -0,0 +1,235 @@ +/* + * file_storage.c -- File-backed USB Storage Gadget, for USB development + * + * Copyright (C) 2003-2008 Alan Stern + * + * Copyright (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz <mnazarewicz@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 + */ + + +/* + * The File-backed Storage Gadget acts as a USB Mass Storage device, + * appearing to the host as a disk drive or as a CD-ROM drive. In addition + * to providing an example of a genuinely useful gadget driver for a USB + * device, it also illustrates a technique of double-buffering for increased + * throughput. Last but not least, it gives an easy way to probe the + * behavior of the Mass Storage drivers in a USB host. + * + * Since this file serves only administrative purposes and all the + * business logic is implemented in f_file_storage.* and + * f_file_storage_params.* files. You should read comments in + * f_file_storage.h and f_file_storage_params.h for more detailed + * description. + */ + + +#include <linux/kernel.h> +#include <linux/utsname.h> +#include <linux/usb/ch9.h> + + +/*-------------------------------------------------------------------------*/ + +#define DRIVER_DESC "Mass Storage Gadget" +#define DRIVER_VERSION "2009/07/21" + +/*-------------------------------------------------------------------------*/ + +/* + * 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 "composite.c" +#include "usbstring.c" +#include "config.c" +#include "epautoconf.c" +#include "f_mass_storage.c" + +/*-------------------------------------------------------------------------*/ + +static struct usb_device_descriptor msg_device_desc = { + .bLength = sizeof msg_device_desc, + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_PER_INTERFACE, + + /* Vendor and product id can be overridden by module parameters. */ + .idVendor = cpu_to_le16(STORAGE_VENDOR_ID), + .idProduct = cpu_to_le16(STORAGE_PRODUCT_ID), + /* .bcdDevice = f(hardware) */ + /* .iManufacturer = DYNAMIC */ + /* .iProduct = DYNAMIC */ + /* NO SERIAL NUMBER */ + .bNumConfigurations = 1, +}; + +static struct usb_otg_descriptor otg_descriptor = { + .bLength = sizeof otg_descriptor, + .bDescriptorType = USB_DT_OTG, + + /* REVISIT SRP-only hardware is possible, although + * it would not be called "OTG" ... + */ + .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, +}; + +static const struct usb_descriptor_header *otg_desc[] = { + (struct usb_descriptor_header *) &otg_descriptor, + NULL, +}; + + +/* string IDs are assigned dynamically */ + +#define STRING_MANUFACTURER_IDX 0 +#define STRING_PRODUCT_IDX 1 +#define STRING_CONFIGURATION_IDX 2 + +static char manufacturer[50]; + +static struct usb_string strings_dev[] = { + [STRING_MANUFACTURER_IDX].s = manufacturer, + [STRING_PRODUCT_IDX].s = DRIVER_DESC, + [STRING_CONFIGURATION_IDX].s = "Self Powered", + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ + .strings = strings_dev, +}; + +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, +}; + + + +/****************************** Configurations ******************************/ + + +static int __init msg_do_config(struct usb_configuration *c) +{ + struct msf_common *common; + int ret; + + if (gadget_is_otg(c->cdev->gadget)) { + c->descriptors = otg_desc; + c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + common = msf_common_init(c->cdev); + if (IS_ERR(common)) + return PTR_ERR(common); + + ret = msf_add(c->cdev, c, common); + msf_common_put(common); + return ret; +} + +static struct usb_configuration msg_config_driver = { + .label = "Linux File-Backed Storage", + .bind = msg_do_config, + .bConfigurationValue = 1, + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, +}; + + + +/****************************** Gadget Bind ******************************/ + + +static int __init msg_bind(struct usb_composite_dev *cdev) +{ + struct usb_gadget *gadget = cdev->gadget; + int status; + + /* Allocate string descriptor numbers ... note that string + * contents can be overridden by the composite_dev glue. + */ + + /* device descriptor strings: manufacturer, product */ + snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", + init_utsname()->sysname, init_utsname()->release, + gadget->name); + status = usb_string_id(cdev); + if (status < 0) + return status; + strings_dev[STRING_MANUFACTURER_IDX].id = status; + msg_device_desc.iManufacturer = status; + + status = usb_string_id(cdev); + if (status < 0) + return status; + strings_dev[STRING_PRODUCT_IDX].id = status; + msg_device_desc.iProduct = status; + + status = usb_string_id(cdev); + if (status < 0) + return status; + strings_dev[STRING_CONFIGURATION_IDX].id = status; + msg_config_driver.iConfiguration = status; + + /* register our second configuration */ + status = usb_add_config(cdev, &msg_config_driver); + if (status < 0) + return status; + + dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); + return 0; +} + +static int __exit msg_unbind(struct usb_composite_dev *cdev) +{ + return 0; +} + + +/****************************** Some noise ******************************/ + + +static struct usb_composite_driver msg_driver = { + .name = "g_mass_storage", + .dev = &msg_device_desc, + .strings = dev_strings, + .bind = msg_bind, + .unbind = __exit_p(msg_unbind), +}; + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Michal Nazarewicz"); +MODULE_LICENSE("GPL"); + +static int __init msg_init(void) +{ + return usb_composite_register(&msg_driver); +} +module_init(msg_init); + +static void __exit msg_cleanup(void) +{ + usb_composite_unregister(&msg_driver); +} +module_exit(msg_cleanup); diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index 2c155e3..83dfe8b 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -40,6 +40,9 @@ * If STOR_DESCRIPTORS_INTR_EP is defined, the file will define * an interrupt endpoint descriptor. Otherwise, it will be ommited. * + * If STOR_DESCRIPTORS_NO_OTG is defined, the otg descriptors will be + * ommited. + * * If STOR_BUFFHD_STATIC_BUFFER is defined, the struct stor_buffhd buf * field will be an array of STORAGE_BUFFLEN chars rather then * a pointer to void. @@ -494,6 +497,7 @@ enum { }; +#ifndef STOR_DESCRIPTORS_NO_OTG static struct usb_otg_descriptor stor_otg_desc = { .bLength = sizeof stor_otg_desc, @@ -501,6 +505,7 @@ stor_otg_desc = { .bmAttributes = USB_OTG_SRP, }; +#endif /* There is only one interface. */ @@ -554,8 +559,10 @@ stor_fs_intr_in_desc = { #endif -static const struct usb_descriptor_header *stor_fs_function[] = { +static struct usb_descriptor_header *stor_fs_function[] = { +#ifndef STOR_DESCRIPTORS_NO_OTG (struct usb_descriptor_header *) &stor_otg_desc, +#endif (struct usb_descriptor_header *) &stor_intf_desc, (struct usb_descriptor_header *) &stor_fs_bulk_in_desc, (struct usb_descriptor_header *) &stor_fs_bulk_out_desc, @@ -613,8 +620,10 @@ stor_hs_intr_in_desc = { #endif -static const struct usb_descriptor_header *stor_hs_function[] = { +static struct usb_descriptor_header *stor_hs_function[] = { +#ifndef STOR_DESCRIPTORS_NO_OTG (struct usb_descriptor_header *) &stor_otg_desc, +#endif (struct usb_descriptor_header *) &stor_intf_desc, (struct usb_descriptor_header *) &stor_hs_bulk_in_desc, (struct usb_descriptor_header *) &stor_hs_bulk_out_desc, -- 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