-/*
- * 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.
- */
-
-
/*-------------------------------------------------------------------------*/
.removable = 0,
.can_stall = 1,
.cdrom = 0,
+ .release = 0xffff,
};
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;
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)
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)
}
-/*-------------------------------------------------------------------------*/
-
-/*
- * 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 */
/*-------------------------------------------------------------------------*/
-/* 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 */
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.
/* 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);
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
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;
}
-/*-------------------------------------------------------------------------*/
-
-/* 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 */
/* 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)
/* 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);
// 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)
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
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;
}
}
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);
/*************************** 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 ******************************/
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;
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);
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;
/*-------------------------------------------------------------------------*/
-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);
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;
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)) {
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:
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);
--- /dev/null
+/*
+ * 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);