diff options
author | Michal Nazarewicz <m.nazarewicz@samsung.com> | 2009-11-09 14:15:20 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-12-11 11:55:22 -0800 |
commit | d23b0f08d18fc42f26f6a0776c6d827eb35143a9 (patch) | |
tree | e6557975165865e43984045279e262d62b939dde /drivers/usb/gadget | |
parent | 6648f29d3be2972a74ef8e29aa5d425ab4f1fc48 (diff) |
USB: g_mass_storage: Mass Storage Function created
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.
Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/gadget')
-rw-r--r-- | drivers/usb/gadget/Kconfig | 18 | ||||
-rw-r--r-- | drivers/usb/gadget/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/gadget/f_mass_storage.c | 644 | ||||
-rw-r--r-- | drivers/usb/gadget/file_storage.c | 7 | ||||
-rw-r--r-- | drivers/usb/gadget/mass_storage.c | 233 | ||||
-rw-r--r-- | drivers/usb/gadget/storage_common.c | 22 |
6 files changed, 426 insertions, 500 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index a18e3c5dd82e..3bb250fd5321 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -732,6 +732,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 9d7b87c52e9f..025ba2ed7907 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -39,6 +39,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 @@ -47,6 +48,7 @@ obj-$(CONFIG_USB_AUDIO) += g_audio.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 5a0f5708c094..5fc01c04af05 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -233,47 +233,23 @@ -/* - * 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 November 2008" +#define FSG_DRIVER_DESC "Mass Storage Function" +#define FSG_DRIVER_VERSION "20 November 2008" -static char fsg_string_manufacturer[64]; -static const char fsg_string_product[] = DRIVER_DESC; -static char fsg_string_serial[13]; -static const char fsg_string_config[] = "Self-powered"; static const char fsg_string_interface[] = "Mass Storage"; #define FSG_NO_INTR_EP 1 #define FSG_BUFFHD_STATIC_BUFFER 1 +#define FSG_NO_DEVICE_STRINGS 1 +#define FSG_NO_OTG 1 +#define FSG_NO_INTR_EP 1 #include "storage_common.c" -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. - */ - - /*-------------------------------------------------------------------------*/ @@ -295,6 +271,7 @@ static struct { .removable = 0, .can_stall = 1, .cdrom = 0, + .release = 0xffff, }; @@ -347,14 +324,18 @@ struct fsg_common { struct fsg_dev { + struct usb_function function; + struct usb_composite_dev*cdev; + struct usb_gadget *gadget; /* Copy of cdev->gadget */ struct fsg_common *common; + u16 interface_number; + /* lock protects: state, all the req_busy's */ spinlock_t lock; - struct usb_gadget *gadget; - 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; @@ -390,6 +371,13 @@ struct fsg_dev { u32 usb_amount_left; }; + +static inline struct fsg_dev *fsg_from_func(struct usb_function *f) +{ + return container_of(f, struct fsg_dev, function); +} + + typedef void (*fsg_routine_t)(struct fsg_dev *); static int exception_in_progress(struct fsg_dev *fsg) @@ -410,10 +398,6 @@ static void set_bulk_out_req_length(struct fsg_dev *fsg, bh->outreq->length = length; } -static struct fsg_dev *the_fsg; -static struct usb_gadget_driver fsg_driver; - - /*-------------------------------------------------------------------------*/ static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep) @@ -433,95 +417,6 @@ static int fsg_set_halt(struct fsg_dev *fsg, 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 fsg_bind(). - */ - -/* 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(FSG_VENDOR_ID), - .idProduct = cpu_to_le16(FSG_PRODUCT_ID), - .bcdDevice = cpu_to_le16(0xffff), - - .iManufacturer = FSG_STRING_MANUFACTURER, - .iProduct = FSG_STRING_PRODUCT, - .iSerialNumber = FSG_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 = FSG_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 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 = fsg_hs_function; - else - function = fsg_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 fsg->lock */ @@ -555,25 +450,12 @@ static void raise_exception(struct fsg_dev *fsg, enum fsg_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 fsg_disconnect(struct usb_gadget *gadget) -{ - struct fsg_dev *fsg = get_gadget_data(gadget); - - DBG(fsg, "disconnect or port reset\n"); - raise_exception(fsg, FSG_STATE_DISCONNECT); -} - - static int ep0_queue(struct fsg_dev *fsg) { int rc; rc = usb_ep_queue(fsg->ep0, fsg->ep0req, GFP_ATOMIC); + fsg->ep0->driver_data = fsg; if (rc != 0 && rc != -ESHUTDOWN) { /* We can't do much more than wait for a reset */ @@ -583,23 +465,6 @@ static int ep0_queue(struct fsg_dev *fsg) return rc; } -static void ep0_complete(struct usb_ep *ep, struct usb_request *req) -{ - 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", __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) - ((fsg_routine_t) (req->context))(fsg); -} - - /*-------------------------------------------------------------------------*/ /* Bulk and interrupt endpoint completion handlers. @@ -652,9 +517,10 @@ static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) /* Ep0 class-specific handlers. These always run in_irq. */ -static int class_setup_req(struct fsg_dev *fsg, +static int fsg_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) { + struct fsg_dev *fsg = fsg_from_func(f); struct usb_request *req = fsg->ep0req; u16 w_index = le16_to_cpu(ctrl->wIndex); u16 w_value = le16_to_cpu(ctrl->wValue); @@ -669,7 +535,7 @@ static int class_setup_req(struct fsg_dev *fsg, if (ctrl->bRequestType != (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) break; - if (w_index != 0 || w_value != 0) + if (w_index != fsg->interface_number || w_value != 0) return -EDOM; /* Raise an exception to stop the current operation @@ -682,7 +548,7 @@ static int class_setup_req(struct fsg_dev *fsg, if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) break; - if (w_index != 0 || w_value != 0) + if (w_index != fsg->interface_number || w_value != 0) return -EDOM; VDBG(fsg, "get max LUN\n"); *(u8 *) req->buf = fsg->common->nluns - 1; @@ -700,160 +566,6 @@ static int class_setup_req(struct fsg_dev *fsg, /*-------------------------------------------------------------------------*/ -/* Ep0 standard request handlers. These always run in_irq. */ - -static int standard_setup_req(struct fsg_dev *fsg, - const struct usb_ctrlrequest *ctrl) -{ - struct usb_request *req = fsg->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: - VDBG(fsg, "get device descriptor\n"); - value = sizeof device_desc; - memcpy(req->buf, &device_desc, value); - break; - case USB_DT_DEVICE_QUALIFIER: - VDBG(fsg, "get device qualifier\n"); - if (!gadget_is_dualspeed(fsg->gadget)) - break; - 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 (!gadget_is_dualspeed(fsg->gadget)) - break; - goto get_config; - case USB_DT_CONFIG: - VDBG(fsg, "get configuration descriptor\n"); -get_config: - value = populate_config_buf(fsg->gadget, - req->buf, - w_value >> 8, - w_value & 0xff); - break; - - case USB_DT_STRING: - VDBG(fsg, "get string descriptor\n"); - - /* wIndex == language code */ - value = usb_gadget_get_string(&fsg_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; - VDBG(fsg, "set configuration\n"); - if (w_value == CONFIG_VALUE || w_value == 0) { - fsg->new_config = w_value; - - /* Raise an exception to wipe out previous transaction - * state (queued bufs, etc) and set the new config. */ - raise_exception(fsg, FSG_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; - VDBG(fsg, "get configuration\n"); - *(u8 *) req->buf = fsg->config; - value = 1; - break; - - case USB_REQ_SET_INTERFACE: - if (ctrl->bRequestType != (USB_DIR_OUT| USB_TYPE_STANDARD | - USB_RECIP_INTERFACE)) - break; - if (fsg->config && w_index == 0) { - - /* Raise an exception to wipe out previous transaction - * state (queued bufs, etc) and install the new - * interface altsetting. */ - raise_exception(fsg, FSG_STATE_INTERFACE_CHANGE); - value = DELAYED_STATUS; - } - break; - case USB_REQ_GET_INTERFACE: - if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | - USB_RECIP_INTERFACE)) - break; - if (!fsg->config) - break; - if (w_index != 0) { - value = -EDOM; - break; - } - VDBG(fsg, "get interface\n"); - *(u8 *) req->buf = 0; - 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, le16_to_cpu(ctrl->wLength)); - } - - return value; -} - - -static int fsg_setup(struct usb_gadget *gadget, - const struct usb_ctrlrequest *ctrl) -{ - struct fsg_dev *fsg = get_gadget_data(gadget); - int rc; - int w_length = le16_to_cpu(ctrl->wLength); - - ++fsg->ep0_req_tag; // Record arrival of a new request - fsg->ep0req->context = NULL; - fsg->ep0req->length = 0; - dump_msg(fsg, "ep0-setup", (u8 *) ctrl, sizeof(*ctrl)); - - if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) - rc = class_setup_req(fsg, ctrl); - else - rc = standard_setup_req(fsg, ctrl); - - /* Respond with data/status or defer until later? */ - if (rc >= 0 && rc != DELAYED_STATUS) { - rc = min(rc, w_length); - fsg->ep0req->length = rc; - fsg->ep0req->zero = rc < w_length; - fsg->ep0req_name = (ctrl->bRequestType & USB_DIR_IN ? - "ep0-in" : "ep0-out"); - rc = ep0_queue(fsg); - } - - /* Device either stalls (rc < 0) or reports success */ - return rc; -} - - -/*-------------------------------------------------------------------------*/ - /* All the following routines run in process context */ @@ -2518,24 +2230,33 @@ static int do_set_config(struct fsg_dev *fsg, u8 new_config) /* Enable the interface */ if (new_config != 0) { fsg->config = new_config; - if ((rc = do_set_interface(fsg, 0)) != 0) - fsg->config = 0; // Reset on errors - else { - char *speed; - - switch (fsg->gadget->speed) { - case USB_SPEED_LOW: speed = "low"; break; - case USB_SPEED_FULL: speed = "full"; break; - case USB_SPEED_HIGH: speed = "high"; break; - default: speed = "?"; break; - } - INFO(fsg, "%s speed config #%d\n", speed, fsg->config); - } + rc = do_set_interface(fsg, 0); + if (rc != 0) + fsg->config = 0; /* Reset on errors */ } return rc; } +/****************************** ALT CONFIGS ******************************/ + + +static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct fsg_dev *fsg = fsg_from_func(f); + fsg->new_config = 1; + raise_exception(fsg, FSG_STATE_CONFIG_CHANGE); + return 0; +} + +static void fsg_disable(struct usb_function *f) +{ + struct fsg_dev *fsg = fsg_from_func(f); + fsg->new_config = 0; + raise_exception(fsg, FSG_STATE_CONFIG_CHANGE); +} + + /*-------------------------------------------------------------------------*/ static void handle_exception(struct fsg_dev *fsg) @@ -2623,9 +2344,6 @@ static void handle_exception(struct fsg_dev *fsg) /* Carry out any extra actions required for the exception */ switch (old_state) { - default: - break; - case FSG_STATE_ABORT_BULK_OUT: send_status(fsg); spin_lock_irq(&fsg->lock); @@ -2651,16 +2369,6 @@ static void handle_exception(struct fsg_dev *fsg) // fsg->common->luns[i].unit_attention_data = SS_RESET_OCCURRED; break; - case FSG_STATE_INTERFACE_CHANGE: - rc = do_set_interface(fsg, 0); - if (fsg->ep0_req_tag != exception_req_tag) - break; - if (rc != 0) // STALL on errors - fsg_set_halt(fsg, fsg->ep0); - else // Complete the status stage - ep0_queue(fsg); - break; - case FSG_STATE_CONFIG_CHANGE: rc = do_set_config(fsg, new_config); if (fsg->ep0_req_tag != exception_req_tag) @@ -2671,12 +2379,6 @@ static void handle_exception(struct fsg_dev *fsg) ep0_queue(fsg); break; - case FSG_STATE_DISCONNECT: - for (i = 0; i < fsg->common->nluns; ++i) - fsg_lun_fsync_sub(&fsg->common->luns[i]); - do_set_config(fsg, 0); // Unconfigured state - break; - case FSG_STATE_EXIT: case FSG_STATE_TERMINATED: do_set_config(fsg, 0); // Free resources @@ -2684,6 +2386,14 @@ static void handle_exception(struct fsg_dev *fsg) fsg->state = FSG_STATE_TERMINATED; // Stop the thread spin_unlock_irq(&fsg->lock); break; + + case FSG_STATE_INTERFACE_CHANGE: + case FSG_STATE_DISCONNECT: + case FSG_STATE_COMMAND_PHASE: + case FSG_STATE_DATA_PHASE: + case FSG_STATE_STATUS_PHASE: + case FSG_STATE_IDLE: + break; } } @@ -2744,16 +2454,17 @@ static int fsg_main_thread(void *fsg_) if (!exception_in_progress(fsg)) fsg->state = FSG_STATE_IDLE; spin_unlock_irq(&fsg->lock); - } + } spin_lock_irq(&fsg->lock); fsg->thread_task = NULL; spin_unlock_irq(&fsg->lock); + /* XXX */ /* If we are exiting because of a signal, unregister the * gadget driver. */ - if (test_and_clear_bit(REGISTERED, &fsg->atomic_bitflags)) - usb_gadget_unregister_driver(&fsg_driver); + /* if (test_and_clear_bit(REGISTERED, &fsg->atomic_bitflags)) */ + /* usb_gadget_unregister_driver(&fsg_driver); */ /* Let the unbind and cleanup routines know the thread has exited */ complete_and_exit(&fsg->thread_notifier, 0); @@ -2762,10 +2473,9 @@ static int fsg_main_thread(void *fsg_) /*************************** DEVICE ATTRIBUTES ***************************/ - -/* The write permissions and store_xxx pointers are set in fsg_bind() */ -static DEVICE_ATTR(ro, 0444, fsg_show_ro, NULL); -static DEVICE_ATTR(file, 0444, fsg_show_file, NULL); +/* Write permission is checked per LUN in store_*() functions. */ +static DEVICE_ATTR(ro, 0644, fsg_show_ro, fsg_store_ro); +static DEVICE_ATTR(file, 0644, fsg_show_file, fsg_store_file); /****************************** FSG COMMON ******************************/ @@ -2789,11 +2499,13 @@ static inline void fsg_common_put(struct fsg_common *common) static struct fsg_common *fsg_common_init(struct fsg_common *common, - struct usb_gadget *gadget) + struct usb_composite_dev *cdev) { + struct usb_gadget *gadget = cdev->gadget; struct fsg_buffhd *bh; struct fsg_lun *curlun; int nluns, i, rc; + char *pathbuf; /* Find out how many LUNs there should be */ nluns = mod_data.nluns; @@ -2833,7 +2545,7 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, curlun->removable = mod_data.removable; curlun->dev.release = fsg_lun_release; curlun->dev.parent = &gadget->dev; - curlun->dev.driver = &fsg_driver.driver; + /* curlun->dev.driver = &fsg_driver.driver; XXX */ dev_set_drvdata(&curlun->dev, &common->filesem); dev_set_name(&curlun->dev,"%s-lun%d", dev_name(&gadget->dev), i); @@ -2906,6 +2618,33 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, kref_init(&common->ref); + + /* Information */ + INFO(common, FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n"); + INFO(common, "Number of LUNs=%d\n", common->nluns); + + pathbuf = kmalloc(PATH_MAX, GFP_KERNEL); + for (i = 0, nluns = common->nluns, curlun = common->luns; + i < nluns; + ++curlun, ++i) { + char *p = "(no medium)"; + if (fsg_lun_is_open(curlun)) { + p = "(error)"; + if (pathbuf) { + p = d_path(&curlun->filp->f_path, + pathbuf, PATH_MAX); + if (IS_ERR(p)) + p = "(error)"; + } + } + LINFO(curlun, "LUN: %s%s%sfile: %s\n", + curlun->removable ? "removable " : "", + curlun->ro ? "read only " : "", + curlun->cdrom ? "CD-ROM " : "", + p); + } + kfree(pathbuf); + return common; @@ -2944,10 +2683,9 @@ static void fsg_common_release(struct kref *ref) /*-------------------------------------------------------------------------*/ -static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) +static void fsg_unbind(struct usb_configuration *c, struct usb_function *f) { - struct fsg_dev *fsg = get_gadget_data(gadget); - struct usb_request *req = fsg->ep0req; + struct fsg_dev *fsg = fsg_from_func(f); DBG(fsg, "unbind\n"); clear_bit(REGISTERED, &fsg->atomic_bitflags); @@ -2961,59 +2699,31 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) complete(&fsg->thread_notifier); } - /* Free the request and buffer for endpoint 0 */ - if (req) { - kfree(req->buf); - usb_ep_free_request(fsg->ep0, req); - } - fsg_common_put(fsg->common); kfree(fsg); - set_gadget_data(gadget, NULL); } -static int __init fsg_bind(struct usb_gadget *gadget) +static int fsg_bind(struct usb_configuration *c, struct usb_function *f) { - struct fsg_dev *fsg; + struct fsg_dev *fsg = fsg_from_func(f); + struct usb_gadget *gadget = c->cdev->gadget; int rc; int i; - struct fsg_lun *curlun; struct usb_ep *ep; - struct usb_request *req; - char *pathbuf, *p; - /* Allocate */ - fsg = kzalloc(sizeof *fsg, GFP_KERNEL); - if (!fsg) - return -ENOMEM; - - /* Initialise common */ - fsg->common = fsg_common_init(0, gadget); - if (IS_ERR(fsg->common)) - return PTR_ERR(fsg->common); - - /* Basic parameters */ fsg->gadget = gadget; - set_gadget_data(gadget, fsg); fsg->ep0 = gadget->ep0; - fsg->ep0->driver_data = fsg; + fsg->ep0req = c->cdev->req; - spin_lock_init(&fsg->lock); - init_completion(&fsg->thread_notifier); - - /* Enable the store_xxx attributes */ - if (mod_data.removable) { - dev_attr_file.attr.mode = 0644; - dev_attr_file.store = fsg_store_file; - if (!mod_data.cdrom) { - dev_attr_ro.attr.mode = 0644; - dev_attr_ro.store = fsg_store_ro; - } - } + /* New interface */ + i = usb_interface_id(c, f); + if (i < 0) + return i; + fsg_intf_desc.bInterfaceNumber = i; + fsg->interface_number = i; /* Find all the endpoints we will use */ - usb_ep_autoconfig_reset(gadget); ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc); if (!ep) goto autoconf_fail; @@ -3026,53 +2736,26 @@ static int __init fsg_bind(struct usb_gadget *gadget) ep->driver_data = fsg; // claim the endpoint fsg->bulk_out = ep; - /* Fix up the descriptors */ - device_desc.bMaxPacketSize0 = fsg->ep0->maxpacket; - 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 = fsg->ep0->maxpacket; - /* Assume endpoint addresses are the same for both speeds */ fsg_hs_bulk_in_desc.bEndpointAddress = fsg_fs_bulk_in_desc.bEndpointAddress; fsg_hs_bulk_out_desc.bEndpointAddress = fsg_fs_bulk_out_desc.bEndpointAddress; + f->hs_descriptors = fsg_hs_function; } - if (gadget_is_otg(gadget)) - fsg_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 = kmalloc(EP0_BUFSIZE, GFP_KERNEL); - if (!req->buf) - goto out; - req->complete = ep0_complete; - - /* This should reflect the actual gadget power source */ - usb_gadget_set_selfpowered(gadget); - - snprintf(fsg_string_manufacturer, sizeof fsg_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 fsg_string_serial - 2; i += 2) { - unsigned char c = DRIVER_VERSION[i / 2]; - if (!c) - break; - sprintf(&fsg_string_serial[i], "%02X", c); + /* maybe allocate device-global string IDs, and patch descriptors */ + if (fsg_strings[FSG_STRING_INTERFACE].id == 0) { + i = usb_string_id(c->cdev); + if (i < 0) + return i; + fsg_strings[FSG_STRING_INTERFACE].id = i; + fsg_intf_desc.iInterface = i; } + fsg->thread_task = kthread_create(fsg_main_thread, fsg, "file-storage-gadget"); if (IS_ERR(fsg->thread_task)) { @@ -3080,37 +2763,12 @@ static int __init fsg_bind(struct usb_gadget *gadget) goto out; } - INFO(fsg, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); - INFO(fsg, "Number of LUNs=%d\n", fsg->common->nluns); - - pathbuf = kmalloc(PATH_MAX, GFP_KERNEL); - for (i = 0; i < fsg->common->nluns; ++i) { - curlun = &fsg->common->luns[i]; - if (fsg_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); - - DBG(fsg, "removable=%d, stall=%d, cdrom=%d, buflen=%u\n", - mod_data.removable, mod_data.can_stall, - mod_data.cdrom, FSG_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); - - the_fsg = fsg; return 0; autoconf_fail: @@ -3119,48 +2777,56 @@ autoconf_fail: out: fsg->state = FSG_STATE_TERMINATED; // The thread is dead - fsg_unbind(gadget); + fsg_unbind(c, f); complete(&fsg->thread_notifier); return rc; } -/*-------------------------------------------------------------------------*/ +/****************************** ADD FUNCTION ******************************/ -static struct usb_gadget_driver fsg_driver = { -#ifdef CONFIG_USB_GADGET_DUALSPEED - .speed = USB_SPEED_HIGH, -#else - .speed = USB_SPEED_FULL, -#endif - .function = (char *) fsg_string_product, - .bind = fsg_bind, - .unbind = fsg_unbind, - .disconnect = fsg_disconnect, - .setup = fsg_setup, - - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, - // .release = ... - // .suspend = ... - // .resume = ... - }, +static struct usb_gadget_strings *fsg_strings_array[] = { + &fsg_stringtab, + NULL, }; - -static int __init fsg_init(void) +static int fsg_add(struct usb_composite_dev *cdev, + struct usb_configuration *c, + struct fsg_common *common) { - return usb_gadget_register_driver(&fsg_driver); -} -module_init(fsg_init); + struct fsg_dev *fsg; + int rc; + + fsg = kzalloc(sizeof *fsg, GFP_KERNEL); + if (unlikely(!fsg)) + return -ENOMEM; + spin_lock_init(&fsg->lock); + init_completion(&fsg->thread_notifier); -static void __exit fsg_cleanup(void) -{ - /* Unregister the driver iff the thread hasn't already done so */ - if (the_fsg && - test_and_clear_bit(REGISTERED, &the_fsg->atomic_bitflags)) - usb_gadget_unregister_driver(&fsg_driver); + fsg->cdev = cdev; + fsg->function.name = FSG_DRIVER_DESC; + fsg->function.strings = fsg_strings_array; + fsg->function.descriptors = fsg_fs_function; + fsg->function.bind = fsg_bind; + fsg->function.unbind = fsg_unbind; + fsg->function.setup = fsg_setup; + fsg->function.set_alt = fsg_set_alt; + fsg->function.disable = fsg_disable; + + fsg->common = common; + /* Our caller holds a reference to common structure so we + * don't have to be worry about it being freed until we return + * from this function. So instead of incrementing counter now + * and decrement in error recovery we increment it only when + * call to usb_add_function() was successful. */ + + rc = usb_add_function(c, &fsg->function); + + if (likely(rc == 0)) + fsg_common_get(fsg->common); + else + kfree(fsg); + + return rc; } -module_exit(fsg_cleanup); diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 7998402b1840..fca3407e48f2 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -594,10 +594,9 @@ static int populate_config_buf(struct usb_gadget *gadget, 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 = fsg_hs_function; - else - function = fsg_fs_function; + function = gadget_is_dualspeed(gadget) && speed == USB_SPEED_HIGH + ? (const struct usb_descriptor_header **)fsg_hs_function + : (const struct usb_descriptor_header **)fsg_fs_function; /* for now, don't advertise srp-only devices */ if (!gadget_is_otg(gadget)) diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c new file mode 100644 index 000000000000..27339f04bc1b --- /dev/null +++ b/drivers/usb/gadget/mass_storage.c @@ -0,0 +1,233 @@ +/* + * mass_storage.c -- File-backed USB Storage Gadget, for USB development + * + * Copyright (C) 2003-2008 Alan Stern + * + * Copyright (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz <m.nazarewicz@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Mass Storage Gadget acts as a USB Mass Storage device, + * appearing to the host as a disk drive or as a CD-ROM drive. In + * addition to providing an example of a genuinely useful gadget + * driver for a USB device, it also illustrates a technique of + * double-buffering for increased throughput. Last but not least, it + * gives an easy way to probe the behavior of the Mass Storage drivers + * in a USB host. + * + * Since this file serves only administrative purposes and all the + * business logic is implemented in f_mass_storage.* file. Read + * comments in this file for more detailed description. + */ + + +#include <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(FSG_VENDOR_ID), + .idProduct = cpu_to_le16(FSG_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 fsg_common *common; + int ret; + + if (gadget_is_otg(c->cdev->gadget)) { + c->descriptors = otg_desc; + c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + common = fsg_common_init(0, c->cdev); + if (IS_ERR(common)) + return PTR_ERR(common); + + ret = fsg_add(c->cdev, c, common); + fsg_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 60bc696778c9..7e81a2d898f0 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -84,11 +84,19 @@ #define LWARN(lun, fmt, args...) dev_warn(&(lun)->dev, fmt, ## args) #define LINFO(lun, fmt, args...) dev_info(&(lun)->dev, 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) +/* Keep those macros in sync with thos in + * include/linux/ubs/composite.h or else GCC will complain. If they + * are identical (the same names of arguments, white spaces in the + * same places) GCC will allow redefinition otherwise (even if some + * white space is removed or added) warning will be issued. No + * checking if those symbols is defined is performed because warning + * is desired when those macros were defined by someone else to mean + * something else. */ +#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) @@ -429,7 +437,7 @@ fsg_fs_intr_in_desc = { #endif -static const struct usb_descriptor_header *fsg_fs_function[] = { +static struct usb_descriptor_header *fsg_fs_function[] = { #ifndef FSG_NO_OTG (struct usb_descriptor_header *) &fsg_otg_desc, #endif @@ -493,7 +501,7 @@ fsg_hs_intr_in_desc = { #endif -static const struct usb_descriptor_header *fsg_hs_function[] = { +static struct usb_descriptor_header *fsg_hs_function[] = { #ifndef FSG_NO_OTG (struct usb_descriptor_header *) &fsg_otg_desc, #endif |