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>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
6648f29d3b
commit
d23b0f08d1
@@ -732,6 +732,24 @@ config USB_FILE_STORAGE_TEST
|
|||||||
behavior of USB Mass Storage hosts. Not needed for
|
behavior of USB Mass Storage hosts. Not needed for
|
||||||
normal operation.
|
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
|
config USB_G_SERIAL
|
||||||
tristate "Serial Gadget (with CDC ACM and CDC OBEX support)"
|
tristate "Serial Gadget (with CDC ACM and CDC OBEX support)"
|
||||||
help
|
help
|
||||||
|
@@ -39,6 +39,7 @@ g_serial-objs := serial.o
|
|||||||
g_midi-objs := gmidi.o
|
g_midi-objs := gmidi.o
|
||||||
gadgetfs-objs := inode.o
|
gadgetfs-objs := inode.o
|
||||||
g_file_storage-objs := file_storage.o
|
g_file_storage-objs := file_storage.o
|
||||||
|
g_mass_storage-objs := mass_storage.o
|
||||||
g_printer-objs := printer.o
|
g_printer-objs := printer.o
|
||||||
g_cdc-objs := cdc2.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_ETH) += g_ether.o
|
||||||
obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o
|
obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o
|
||||||
obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.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_SERIAL) += g_serial.o
|
||||||
obj-$(CONFIG_USB_G_PRINTER) += g_printer.o
|
obj-$(CONFIG_USB_G_PRINTER) += g_printer.o
|
||||||
obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o
|
obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o
|
||||||
|
@@ -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 FSG_DRIVER_DESC "Mass Storage Function"
|
||||||
#define DRIVER_NAME "g_file_storage"
|
#define FSG_DRIVER_VERSION "20 November 2008"
|
||||||
#define 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";
|
static const char fsg_string_interface[] = "Mass Storage";
|
||||||
|
|
||||||
|
|
||||||
#define FSG_NO_INTR_EP 1
|
#define FSG_NO_INTR_EP 1
|
||||||
#define FSG_BUFFHD_STATIC_BUFFER 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"
|
#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,
|
.removable = 0,
|
||||||
.can_stall = 1,
|
.can_stall = 1,
|
||||||
.cdrom = 0,
|
.cdrom = 0,
|
||||||
|
.release = 0xffff,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -347,14 +324,18 @@ struct fsg_common {
|
|||||||
|
|
||||||
|
|
||||||
struct fsg_dev {
|
struct fsg_dev {
|
||||||
|
struct usb_function function;
|
||||||
|
struct usb_composite_dev*cdev;
|
||||||
|
struct usb_gadget *gadget; /* Copy of cdev->gadget */
|
||||||
struct fsg_common *common;
|
struct fsg_common *common;
|
||||||
|
|
||||||
|
u16 interface_number;
|
||||||
|
|
||||||
/* lock protects: state, all the req_busy's */
|
/* lock protects: state, all the req_busy's */
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
struct usb_gadget *gadget;
|
|
||||||
|
|
||||||
struct usb_ep *ep0; // Handy copy of gadget->ep0
|
struct usb_ep *ep0; /* Copy of gadget->ep0 */
|
||||||
struct usb_request *ep0req; // For control responses
|
struct usb_request *ep0req; /* Copy of cdev->req */
|
||||||
unsigned int ep0_req_tag;
|
unsigned int ep0_req_tag;
|
||||||
const char *ep0req_name;
|
const char *ep0req_name;
|
||||||
|
|
||||||
@@ -390,6 +371,13 @@ struct fsg_dev {
|
|||||||
u32 usb_amount_left;
|
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 *);
|
typedef void (*fsg_routine_t)(struct fsg_dev *);
|
||||||
|
|
||||||
static int exception_in_progress(struct fsg_dev *fsg)
|
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;
|
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)
|
static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep)
|
||||||
@@ -431,95 +415,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 */
|
/* These routines may be called in process context or in_irq */
|
||||||
@@ -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)
|
static int ep0_queue(struct fsg_dev *fsg)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
rc = usb_ep_queue(fsg->ep0, fsg->ep0req, GFP_ATOMIC);
|
rc = usb_ep_queue(fsg->ep0, fsg->ep0req, GFP_ATOMIC);
|
||||||
|
fsg->ep0->driver_data = fsg;
|
||||||
if (rc != 0 && rc != -ESHUTDOWN) {
|
if (rc != 0 && rc != -ESHUTDOWN) {
|
||||||
|
|
||||||
/* We can't do much more than wait for a reset */
|
/* 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;
|
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.
|
/* 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. */
|
/* 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)
|
const struct usb_ctrlrequest *ctrl)
|
||||||
{
|
{
|
||||||
|
struct fsg_dev *fsg = fsg_from_func(f);
|
||||||
struct usb_request *req = fsg->ep0req;
|
struct usb_request *req = fsg->ep0req;
|
||||||
u16 w_index = le16_to_cpu(ctrl->wIndex);
|
u16 w_index = le16_to_cpu(ctrl->wIndex);
|
||||||
u16 w_value = le16_to_cpu(ctrl->wValue);
|
u16 w_value = le16_to_cpu(ctrl->wValue);
|
||||||
@@ -669,7 +535,7 @@ static int class_setup_req(struct fsg_dev *fsg,
|
|||||||
if (ctrl->bRequestType !=
|
if (ctrl->bRequestType !=
|
||||||
(USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE))
|
(USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE))
|
||||||
break;
|
break;
|
||||||
if (w_index != 0 || w_value != 0)
|
if (w_index != fsg->interface_number || w_value != 0)
|
||||||
return -EDOM;
|
return -EDOM;
|
||||||
|
|
||||||
/* Raise an exception to stop the current operation
|
/* Raise an exception to stop the current operation
|
||||||
@@ -682,7 +548,7 @@ static int class_setup_req(struct fsg_dev *fsg,
|
|||||||
if (ctrl->bRequestType !=
|
if (ctrl->bRequestType !=
|
||||||
(USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE))
|
(USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE))
|
||||||
break;
|
break;
|
||||||
if (w_index != 0 || w_value != 0)
|
if (w_index != fsg->interface_number || w_value != 0)
|
||||||
return -EDOM;
|
return -EDOM;
|
||||||
VDBG(fsg, "get max LUN\n");
|
VDBG(fsg, "get max LUN\n");
|
||||||
*(u8 *) req->buf = fsg->common->nluns - 1;
|
*(u8 *) req->buf = fsg->common->nluns - 1;
|
||||||
@@ -698,160 +564,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 */
|
/* 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 */
|
/* Enable the interface */
|
||||||
if (new_config != 0) {
|
if (new_config != 0) {
|
||||||
fsg->config = new_config;
|
fsg->config = new_config;
|
||||||
if ((rc = do_set_interface(fsg, 0)) != 0)
|
rc = do_set_interface(fsg, 0);
|
||||||
fsg->config = 0; // Reset on errors
|
if (rc != 0)
|
||||||
else {
|
fsg->config = 0; /* Reset on errors */
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return rc;
|
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)
|
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 */
|
/* Carry out any extra actions required for the exception */
|
||||||
switch (old_state) {
|
switch (old_state) {
|
||||||
default:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FSG_STATE_ABORT_BULK_OUT:
|
case FSG_STATE_ABORT_BULK_OUT:
|
||||||
send_status(fsg);
|
send_status(fsg);
|
||||||
spin_lock_irq(&fsg->lock);
|
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;
|
// fsg->common->luns[i].unit_attention_data = SS_RESET_OCCURRED;
|
||||||
break;
|
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:
|
case FSG_STATE_CONFIG_CHANGE:
|
||||||
rc = do_set_config(fsg, new_config);
|
rc = do_set_config(fsg, new_config);
|
||||||
if (fsg->ep0_req_tag != exception_req_tag)
|
if (fsg->ep0_req_tag != exception_req_tag)
|
||||||
@@ -2671,12 +2379,6 @@ static void handle_exception(struct fsg_dev *fsg)
|
|||||||
ep0_queue(fsg);
|
ep0_queue(fsg);
|
||||||
break;
|
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_EXIT:
|
||||||
case FSG_STATE_TERMINATED:
|
case FSG_STATE_TERMINATED:
|
||||||
do_set_config(fsg, 0); // Free resources
|
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
|
fsg->state = FSG_STATE_TERMINATED; // Stop the thread
|
||||||
spin_unlock_irq(&fsg->lock);
|
spin_unlock_irq(&fsg->lock);
|
||||||
break;
|
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))
|
if (!exception_in_progress(fsg))
|
||||||
fsg->state = FSG_STATE_IDLE;
|
fsg->state = FSG_STATE_IDLE;
|
||||||
spin_unlock_irq(&fsg->lock);
|
spin_unlock_irq(&fsg->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_irq(&fsg->lock);
|
spin_lock_irq(&fsg->lock);
|
||||||
fsg->thread_task = NULL;
|
fsg->thread_task = NULL;
|
||||||
spin_unlock_irq(&fsg->lock);
|
spin_unlock_irq(&fsg->lock);
|
||||||
|
|
||||||
|
/* XXX */
|
||||||
/* If we are exiting because of a signal, unregister the
|
/* If we are exiting because of a signal, unregister the
|
||||||
* gadget driver. */
|
* gadget driver. */
|
||||||
if (test_and_clear_bit(REGISTERED, &fsg->atomic_bitflags))
|
/* if (test_and_clear_bit(REGISTERED, &fsg->atomic_bitflags)) */
|
||||||
usb_gadget_unregister_driver(&fsg_driver);
|
/* usb_gadget_unregister_driver(&fsg_driver); */
|
||||||
|
|
||||||
/* Let the unbind and cleanup routines know the thread has exited */
|
/* Let the unbind and cleanup routines know the thread has exited */
|
||||||
complete_and_exit(&fsg->thread_notifier, 0);
|
complete_and_exit(&fsg->thread_notifier, 0);
|
||||||
@@ -2762,10 +2473,9 @@ static int fsg_main_thread(void *fsg_)
|
|||||||
|
|
||||||
/*************************** DEVICE ATTRIBUTES ***************************/
|
/*************************** DEVICE ATTRIBUTES ***************************/
|
||||||
|
|
||||||
|
/* Write permission is checked per LUN in store_*() functions. */
|
||||||
/* The write permissions and store_xxx pointers are set in fsg_bind() */
|
static DEVICE_ATTR(ro, 0644, fsg_show_ro, fsg_store_ro);
|
||||||
static DEVICE_ATTR(ro, 0444, fsg_show_ro, NULL);
|
static DEVICE_ATTR(file, 0644, fsg_show_file, fsg_store_file);
|
||||||
static DEVICE_ATTR(file, 0444, fsg_show_file, NULL);
|
|
||||||
|
|
||||||
|
|
||||||
/****************************** FSG COMMON ******************************/
|
/****************************** 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,
|
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_buffhd *bh;
|
||||||
struct fsg_lun *curlun;
|
struct fsg_lun *curlun;
|
||||||
int nluns, i, rc;
|
int nluns, i, rc;
|
||||||
|
char *pathbuf;
|
||||||
|
|
||||||
/* Find out how many LUNs there should be */
|
/* Find out how many LUNs there should be */
|
||||||
nluns = mod_data.nluns;
|
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->removable = mod_data.removable;
|
||||||
curlun->dev.release = fsg_lun_release;
|
curlun->dev.release = fsg_lun_release;
|
||||||
curlun->dev.parent = &gadget->dev;
|
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_drvdata(&curlun->dev, &common->filesem);
|
||||||
dev_set_name(&curlun->dev,"%s-lun%d",
|
dev_set_name(&curlun->dev,"%s-lun%d",
|
||||||
dev_name(&gadget->dev), i);
|
dev_name(&gadget->dev), i);
|
||||||
@@ -2906,6 +2618,33 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
|
|||||||
|
|
||||||
|
|
||||||
kref_init(&common->ref);
|
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;
|
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 fsg_dev *fsg = fsg_from_func(f);
|
||||||
struct usb_request *req = fsg->ep0req;
|
|
||||||
|
|
||||||
DBG(fsg, "unbind\n");
|
DBG(fsg, "unbind\n");
|
||||||
clear_bit(REGISTERED, &fsg->atomic_bitflags);
|
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);
|
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);
|
fsg_common_put(fsg->common);
|
||||||
kfree(fsg);
|
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 rc;
|
||||||
int i;
|
int i;
|
||||||
struct fsg_lun *curlun;
|
|
||||||
struct usb_ep *ep;
|
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;
|
fsg->gadget = gadget;
|
||||||
set_gadget_data(gadget, fsg);
|
|
||||||
fsg->ep0 = gadget->ep0;
|
fsg->ep0 = gadget->ep0;
|
||||||
fsg->ep0->driver_data = fsg;
|
fsg->ep0req = c->cdev->req;
|
||||||
|
|
||||||
spin_lock_init(&fsg->lock);
|
/* New interface */
|
||||||
init_completion(&fsg->thread_notifier);
|
i = usb_interface_id(c, f);
|
||||||
|
if (i < 0)
|
||||||
/* Enable the store_xxx attributes */
|
return i;
|
||||||
if (mod_data.removable) {
|
fsg_intf_desc.bInterfaceNumber = i;
|
||||||
dev_attr_file.attr.mode = 0644;
|
fsg->interface_number = i;
|
||||||
dev_attr_file.store = fsg_store_file;
|
|
||||||
if (!mod_data.cdrom) {
|
|
||||||
dev_attr_ro.attr.mode = 0644;
|
|
||||||
dev_attr_ro.store = fsg_store_ro;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Find all the endpoints we will use */
|
/* Find all the endpoints we will use */
|
||||||
usb_ep_autoconfig_reset(gadget);
|
|
||||||
ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc);
|
ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc);
|
||||||
if (!ep)
|
if (!ep)
|
||||||
goto autoconf_fail;
|
goto autoconf_fail;
|
||||||
@@ -3026,53 +2736,26 @@ static int __init fsg_bind(struct usb_gadget *gadget)
|
|||||||
ep->driver_data = fsg; // claim the endpoint
|
ep->driver_data = fsg; // claim the endpoint
|
||||||
fsg->bulk_out = ep;
|
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)) {
|
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 */
|
/* Assume endpoint addresses are the same for both speeds */
|
||||||
fsg_hs_bulk_in_desc.bEndpointAddress =
|
fsg_hs_bulk_in_desc.bEndpointAddress =
|
||||||
fsg_fs_bulk_in_desc.bEndpointAddress;
|
fsg_fs_bulk_in_desc.bEndpointAddress;
|
||||||
fsg_hs_bulk_out_desc.bEndpointAddress =
|
fsg_hs_bulk_out_desc.bEndpointAddress =
|
||||||
fsg_fs_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;
|
/* maybe allocate device-global string IDs, and patch descriptors */
|
||||||
|
if (fsg_strings[FSG_STRING_INTERFACE].id == 0) {
|
||||||
/* Allocate the request and buffer for endpoint 0 */
|
i = usb_string_id(c->cdev);
|
||||||
fsg->ep0req = req = usb_ep_alloc_request(fsg->ep0, GFP_KERNEL);
|
if (i < 0)
|
||||||
if (!req)
|
return i;
|
||||||
goto out;
|
fsg_strings[FSG_STRING_INTERFACE].id = i;
|
||||||
req->buf = kmalloc(EP0_BUFSIZE, GFP_KERNEL);
|
fsg_intf_desc.iInterface = i;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fsg->thread_task = kthread_create(fsg_main_thread, fsg,
|
fsg->thread_task = kthread_create(fsg_main_thread, fsg,
|
||||||
"file-storage-gadget");
|
"file-storage-gadget");
|
||||||
if (IS_ERR(fsg->thread_task)) {
|
if (IS_ERR(fsg->thread_task)) {
|
||||||
@@ -3080,37 +2763,12 @@ static int __init fsg_bind(struct usb_gadget *gadget)
|
|||||||
goto out;
|
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));
|
DBG(fsg, "I/O thread pid: %d\n", task_pid_nr(fsg->thread_task));
|
||||||
|
|
||||||
set_bit(REGISTERED, &fsg->atomic_bitflags);
|
set_bit(REGISTERED, &fsg->atomic_bitflags);
|
||||||
|
|
||||||
/* Tell the thread to start working */
|
/* Tell the thread to start working */
|
||||||
wake_up_process(fsg->thread_task);
|
wake_up_process(fsg->thread_task);
|
||||||
|
|
||||||
the_fsg = fsg;
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
autoconf_fail:
|
autoconf_fail:
|
||||||
@@ -3119,48 +2777,56 @@ autoconf_fail:
|
|||||||
|
|
||||||
out:
|
out:
|
||||||
fsg->state = FSG_STATE_TERMINATED; // The thread is dead
|
fsg->state = FSG_STATE_TERMINATED; // The thread is dead
|
||||||
fsg_unbind(gadget);
|
fsg_unbind(c, f);
|
||||||
complete(&fsg->thread_notifier);
|
complete(&fsg->thread_notifier);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/****************************** ADD FUNCTION ******************************/
|
||||||
|
|
||||||
static struct usb_gadget_driver fsg_driver = {
|
static struct usb_gadget_strings *fsg_strings_array[] = {
|
||||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
&fsg_stringtab,
|
||||||
.speed = USB_SPEED_HIGH,
|
NULL,
|
||||||
#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 int fsg_add(struct usb_composite_dev *cdev,
|
||||||
static int __init fsg_init(void)
|
struct usb_configuration *c,
|
||||||
|
struct fsg_common *common)
|
||||||
{
|
{
|
||||||
return usb_gadget_register_driver(&fsg_driver);
|
struct fsg_dev *fsg;
|
||||||
}
|
int rc;
|
||||||
module_init(fsg_init);
|
|
||||||
|
|
||||||
|
fsg = kzalloc(sizeof *fsg, GFP_KERNEL);
|
||||||
|
if (unlikely(!fsg))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
static void __exit fsg_cleanup(void)
|
spin_lock_init(&fsg->lock);
|
||||||
{
|
init_completion(&fsg->thread_notifier);
|
||||||
/* Unregister the driver iff the thread hasn't already done so */
|
|
||||||
if (the_fsg &&
|
fsg->cdev = cdev;
|
||||||
test_and_clear_bit(REGISTERED, &the_fsg->atomic_bitflags))
|
fsg->function.name = FSG_DRIVER_DESC;
|
||||||
usb_gadget_unregister_driver(&fsg_driver);
|
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);
|
|
||||||
|
@@ -594,10 +594,9 @@ static int populate_config_buf(struct usb_gadget *gadget,
|
|||||||
|
|
||||||
if (gadget_is_dualspeed(gadget) && type == USB_DT_OTHER_SPEED_CONFIG)
|
if (gadget_is_dualspeed(gadget) && type == USB_DT_OTHER_SPEED_CONFIG)
|
||||||
speed = (USB_SPEED_FULL + USB_SPEED_HIGH) - speed;
|
speed = (USB_SPEED_FULL + USB_SPEED_HIGH) - speed;
|
||||||
if (gadget_is_dualspeed(gadget) && speed == USB_SPEED_HIGH)
|
function = gadget_is_dualspeed(gadget) && speed == USB_SPEED_HIGH
|
||||||
function = fsg_hs_function;
|
? (const struct usb_descriptor_header **)fsg_hs_function
|
||||||
else
|
: (const struct usb_descriptor_header **)fsg_fs_function;
|
||||||
function = fsg_fs_function;
|
|
||||||
|
|
||||||
/* for now, don't advertise srp-only devices */
|
/* for now, don't advertise srp-only devices */
|
||||||
if (!gadget_is_otg(gadget))
|
if (!gadget_is_otg(gadget))
|
||||||
|
233
drivers/usb/gadget/mass_storage.c
Normal file
233
drivers/usb/gadget/mass_storage.c
Normal file
@@ -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);
|
@@ -84,11 +84,19 @@
|
|||||||
#define LWARN(lun, fmt, args...) dev_warn(&(lun)->dev, fmt, ## args)
|
#define LWARN(lun, fmt, args...) dev_warn(&(lun)->dev, fmt, ## args)
|
||||||
#define LINFO(lun, fmt, args...) dev_info(&(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)
|
/* Keep those macros in sync with thos in
|
||||||
#define VDBG(d, fmt, args...) dev_vdbg(&(d)->gadget->dev, fmt, ## args)
|
* include/linux/ubs/composite.h or else GCC will complain. If they
|
||||||
#define ERROR(d, fmt, args...) dev_err (&(d)->gadget->dev, fmt, ## args)
|
* are identical (the same names of arguments, white spaces in the
|
||||||
#define WARNING(d, fmt, args...) dev_warn(&(d)->gadget->dev, fmt, ## args)
|
* same places) GCC will allow redefinition otherwise (even if some
|
||||||
#define INFO(d, fmt, args...) dev_info(&(d)->gadget->dev, fmt, ## args)
|
* 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
|
#endif
|
||||||
|
|
||||||
static const struct usb_descriptor_header *fsg_fs_function[] = {
|
static struct usb_descriptor_header *fsg_fs_function[] = {
|
||||||
#ifndef FSG_NO_OTG
|
#ifndef FSG_NO_OTG
|
||||||
(struct usb_descriptor_header *) &fsg_otg_desc,
|
(struct usb_descriptor_header *) &fsg_otg_desc,
|
||||||
#endif
|
#endif
|
||||||
@@ -493,7 +501,7 @@ fsg_hs_intr_in_desc = {
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const struct usb_descriptor_header *fsg_hs_function[] = {
|
static struct usb_descriptor_header *fsg_hs_function[] = {
|
||||||
#ifndef FSG_NO_OTG
|
#ifndef FSG_NO_OTG
|
||||||
(struct usb_descriptor_header *) &fsg_otg_desc,
|
(struct usb_descriptor_header *) &fsg_otg_desc,
|
||||||
#endif
|
#endif
|
||||||
|
Reference in New Issue
Block a user