[PATCH 11/11] [Storage] Created Mass Storage Function

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux