diff options
author | Greg Kroah-Hartman | 2013-10-24 16:18:40 +0100 |
---|---|---|
committer | Greg Kroah-Hartman | 2013-10-24 16:18:40 +0100 |
commit | 5328f35b1584a9849ffe46afa42018946aa43851 (patch) | |
tree | f939d0577ae13ed22069ac4188ce21bacc7a3af5 /drivers/usb/gadget | |
parent | e3967e7b28de70d28313cc93d831d8525083097f (diff) | |
parent | 80d7d8a768cda6e8a08ab805a977d08741011da1 (diff) |
Merge tag 'usb-for-v3.13' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
Felipe writes:
usb: patches for v3.13
Final conversions to configfs for mass storage, acm_ms, and
multi gadgets.
MUSB should now work out of the box on AM335x-based boards
(beagle bone white and black) with DMA thanks to Sebastian's
work.
We can now enable VERBOSE_DEBUG on builds of drivers/usb/gadget/
by selecting CONFIG_USB_GADGET_VERBOSE.
s3c-hsotg got quite a few non-critical fixes but also learned
a few new tricks (isochronous transfers, multi count support).
The Marvel USB3 Controller driver got a memory leak fix.
devm_usb_get_phy() learned not to return NULL, ever.
Other than these patches, we have the usual set of cleanups
ranging from removal of unnecessary *_set_drvdata() to using
SIMPLE_DEV_PM_OPS.
Signed-of-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/gadget')
-rw-r--r-- | drivers/usb/gadget/Kconfig | 34 | ||||
-rw-r--r-- | drivers/usb/gadget/Makefile | 5 | ||||
-rw-r--r-- | drivers/usb/gadget/acm_ms.c | 125 | ||||
-rw-r--r-- | drivers/usb/gadget/configfs.c | 10 | ||||
-rw-r--r-- | drivers/usb/gadget/configfs.h | 6 | ||||
-rw-r--r-- | drivers/usb/gadget/f_mass_storage.c | 1254 | ||||
-rw-r--r-- | drivers/usb/gadget/f_mass_storage.h | 166 | ||||
-rw-r--r-- | drivers/usb/gadget/mass_storage.c | 125 | ||||
-rw-r--r-- | drivers/usb/gadget/multi.c | 247 | ||||
-rw-r--r-- | drivers/usb/gadget/mv_u3d_core.c | 2 | ||||
-rw-r--r-- | drivers/usb/gadget/s3c-hsotg.c | 167 | ||||
-rw-r--r-- | drivers/usb/gadget/storage_common.c | 430 | ||||
-rw-r--r-- | drivers/usb/gadget/storage_common.h | 229 | ||||
-rw-r--r-- | drivers/usb/gadget/udc-core.c | 3 | ||||
-rw-r--r-- | drivers/usb/gadget/zero.c | 25 |
15 files changed, 2037 insertions, 791 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 48cddf3cd6b8..a91e6422f930 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -58,6 +58,20 @@ config USB_GADGET_DEBUG trying to track down. Never enable these messages for a production build. +config USB_GADGET_VERBOSE + bool "Verbose debugging Messages (DEVELOPMENT)" + depends on USB_GADGET_DEBUG + help + Many controller and gadget drivers will print verbose debugging + messages if you use this option to ask for those messages. + + Avoid enabling these messages, even if you're actively + debugging such a driver. Many drivers will emit so many + messages that the driver timings are affected, which will + either create new failure modes or remove the one you're + trying to track down. Never enable these messages for a + production build. + config USB_GADGET_DEBUG_FILES boolean "Debugging information files (DEVELOPMENT)" depends on PROC_FS @@ -525,6 +539,9 @@ config USB_F_SUBSET config USB_F_RNDIS tristate +config USB_F_MASS_STORAGE + tristate + choice tristate "USB Gadget Drivers" default USB_ETH @@ -662,6 +679,16 @@ config USB_CONFIGFS_PHONET help The Phonet protocol implementation for USB device. +config USB_CONFIGFS_MASS_STORAGE + boolean "Mass storage" + depends on USB_CONFIGFS + select USB_F_MASS_STORAGE + 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. + config USB_ZERO tristate "Gadget Zero (DEVELOPMENT)" select USB_LIBCOMPOSITE @@ -878,6 +905,7 @@ config USB_MASS_STORAGE tristate "Mass Storage Gadget" depends on BLOCK select USB_LIBCOMPOSITE + select USB_F_MASS_STORAGE 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 @@ -1001,6 +1029,7 @@ config USB_G_ACM_MS select USB_LIBCOMPOSITE select USB_U_SERIAL select USB_F_ACM + select USB_F_MASS_STORAGE help This driver provides two functions in one configuration: a mass storage, and a CDC ACM (serial port) link. @@ -1015,8 +1044,8 @@ config USB_G_MULTI select USB_LIBCOMPOSITE select USB_U_SERIAL select USB_U_ETHER - select USB_U_RNDIS select USB_F_ACM + select USB_F_MASS_STORAGE help The Multifunction Composite Gadget provides Ethernet (RNDIS and/or CDC Ethernet), mass storage and ACM serial link @@ -1035,6 +1064,8 @@ config USB_G_MULTI config USB_G_MULTI_RNDIS bool "RNDIS + CDC Serial + Storage configuration" depends on USB_G_MULTI + select USB_U_RNDIS + select USB_F_RNDIS default y help This option enables a configuration with RNDIS, CDC Serial and @@ -1048,6 +1079,7 @@ config USB_G_MULTI_CDC bool "CDC Ethernet + CDC Serial + Storage configuration" depends on USB_G_MULTI default n + select USB_F_ECM help This option enables a configuration with CDC Ethernet (ECM), CDC Serial and Mass Storage functions available in the Multifunction diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 386db9daf1d9..f1af39603d4d 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -1,7 +1,8 @@ # # USB peripheral controller drivers # -ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG +ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG +ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG obj-$(CONFIG_USB_GADGET) += udc-core.o obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o @@ -60,6 +61,8 @@ usb_f_ecm_subset-y := f_subset.o obj-$(CONFIG_USB_F_SUBSET) += usb_f_ecm_subset.o usb_f_rndis-y := f_rndis.o obj-$(CONFIG_USB_F_RNDIS) += usb_f_rndis.o +usb_f_mass_storage-y := f_mass_storage.o storage_common.o +obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o # # USB gadget drivers diff --git a/drivers/usb/gadget/acm_ms.c b/drivers/usb/gadget/acm_ms.c index 4b947bb50f62..7bfa134fe0e3 100644 --- a/drivers/usb/gadget/acm_ms.c +++ b/drivers/usb/gadget/acm_ms.c @@ -31,16 +31,7 @@ #define ACM_MS_VENDOR_NUM 0x1d6b /* Linux Foundation */ #define ACM_MS_PRODUCT_NUM 0x0106 /* Composite Gadget: ACM + MS*/ -/*-------------------------------------------------------------------------*/ - -/* - * 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 "f_mass_storage.c" +#include "f_mass_storage.h" /*-------------------------------------------------------------------------*/ USB_GADGET_COMPOSITE_OPTIONS(); @@ -104,18 +95,35 @@ static struct usb_gadget_strings *dev_strings[] = { /****************************** Configurations ******************************/ static struct fsg_module_parameters fsg_mod_data = { .stall = 1 }; -FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); +#ifdef CONFIG_USB_GADGET_DEBUG_FILES -static struct fsg_common fsg_common; +static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS; + +#else + +/* + * Number of buffers we will use. + * 2 is usually enough for good buffering pipeline + */ +#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS + +#endif /* CONFIG_USB_DEBUG */ + +FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); /*-------------------------------------------------------------------------*/ static struct usb_function *f_acm; static struct usb_function_instance *f_acm_inst; + +static struct usb_function_instance *fi_msg; +static struct usb_function *f_msg; + /* * We _always_ have both ACM and mass storage functions. */ static int __init acm_ms_do_config(struct usb_configuration *c) { + struct fsg_opts *opts; int status; if (gadget_is_otg(c->cdev->gadget)) { @@ -123,31 +131,37 @@ static int __init acm_ms_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - f_acm_inst = usb_get_function_instance("acm"); - if (IS_ERR(f_acm_inst)) - return PTR_ERR(f_acm_inst); + opts = fsg_opts_from_func_inst(fi_msg); f_acm = usb_get_function(f_acm_inst); - if (IS_ERR(f_acm)) { - status = PTR_ERR(f_acm); - goto err_func; + if (IS_ERR(f_acm)) + return PTR_ERR(f_acm); + + f_msg = usb_get_function(fi_msg); + if (IS_ERR(f_msg)) { + status = PTR_ERR(f_msg); + goto put_acm; } status = usb_add_function(c, f_acm); if (status < 0) - goto err_conf; + goto put_msg; - status = fsg_bind_config(c->cdev, c, &fsg_common); - if (status < 0) - goto err_fsg; + status = fsg_common_run_thread(opts->common); + if (status) + goto remove_acm; + + status = usb_add_function(c, f_msg); + if (status) + goto remove_acm; return 0; -err_fsg: +remove_acm: usb_remove_function(c, f_acm); -err_conf: +put_msg: + usb_put_function(f_msg); +put_acm: usb_put_function(f_acm); -err_func: - usb_put_function_instance(f_acm_inst); return status; } @@ -163,45 +177,82 @@ static struct usb_configuration acm_ms_config_driver = { static int __init acm_ms_bind(struct usb_composite_dev *cdev) { struct usb_gadget *gadget = cdev->gadget; + struct fsg_opts *opts; + struct fsg_config config; int status; - void *retp; - /* set up mass storage function */ - retp = fsg_common_from_params(&fsg_common, cdev, &fsg_mod_data); - if (IS_ERR(retp)) { - status = PTR_ERR(retp); - return PTR_ERR(retp); + f_acm_inst = usb_get_function_instance("acm"); + if (IS_ERR(f_acm_inst)) + return PTR_ERR(f_acm_inst); + + fi_msg = usb_get_function_instance("mass_storage"); + if (IS_ERR(fi_msg)) { + status = PTR_ERR(fi_msg); + goto fail_get_msg; } + /* set up mass storage function */ + fsg_config_from_params(&config, &fsg_mod_data, fsg_num_buffers); + opts = fsg_opts_from_func_inst(fi_msg); + + opts->no_configfs = true; + status = fsg_common_set_num_buffers(opts->common, fsg_num_buffers); + if (status) + goto fail; + + status = fsg_common_set_nluns(opts->common, config.nluns); + if (status) + goto fail_set_nluns; + + status = fsg_common_set_cdev(opts->common, cdev, config.can_stall); + if (status) + goto fail_set_cdev; + + fsg_common_set_sysfs(opts->common, true); + status = fsg_common_create_luns(opts->common, &config); + if (status) + goto fail_set_cdev; + + fsg_common_set_inquiry_string(opts->common, config.vendor_name, + config.product_name); /* * Allocate string descriptor numbers ... note that string * contents can be overridden by the composite_dev glue. */ status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) - goto fail1; + goto fail_string_ids; device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; /* register our configuration */ status = usb_add_config(cdev, &acm_ms_config_driver, acm_ms_do_config); if (status < 0) - goto fail1; + goto fail_string_ids; usb_composite_overwrite_options(cdev, &coverwrite); dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n", DRIVER_DESC); - fsg_common_put(&fsg_common); return 0; /* error recovery */ -fail1: - fsg_common_put(&fsg_common); +fail_string_ids: + fsg_common_remove_luns(opts->common); +fail_set_cdev: + fsg_common_free_luns(opts->common); +fail_set_nluns: + fsg_common_free_buffers(opts->common); +fail: + usb_put_function_instance(fi_msg); +fail_get_msg: + usb_put_function_instance(f_acm_inst); return status; } static int __exit acm_ms_unbind(struct usb_composite_dev *cdev) { + usb_put_function(f_msg); + usb_put_function_instance(fi_msg); usb_put_function(f_acm); usb_put_function_instance(f_acm_inst); return 0; diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 8f0d6141e5e6..25885112fa35 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -557,7 +557,7 @@ static struct config_group *function_make( fi = usb_get_function_instance(func_name); if (IS_ERR(fi)) - return ERR_PTR(PTR_ERR(fi)); + return ERR_CAST(fi); ret = config_item_set_name(&fi->group.cg_item, name); if (ret) { @@ -991,6 +991,14 @@ static struct configfs_subsystem gadget_subsys = { .su_mutex = __MUTEX_INITIALIZER(gadget_subsys.su_mutex), }; +void unregister_gadget_item(struct config_item *item) +{ + struct gadget_info *gi = to_gadget_info(item); + + unregister_gadget(gi); +} +EXPORT_SYMBOL(unregister_gadget_item); + static int __init gadget_cfs_init(void) { int ret; diff --git a/drivers/usb/gadget/configfs.h b/drivers/usb/gadget/configfs.h new file mode 100644 index 000000000000..a7b564a913d1 --- /dev/null +++ b/drivers/usb/gadget/configfs.h @@ -0,0 +1,6 @@ +#ifndef USB__GADGET__CONFIGFS__H +#define USB__GADGET__CONFIGFS__H + +void unregister_gadget_item(struct config_item *item); + +#endif /* USB__GADGET__CONFIGFS__H */ diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index a01d7d38c016..8fa8b1f771fb 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -213,12 +213,14 @@ #include <linux/spinlock.h> #include <linux/string.h> #include <linux/freezer.h> +#include <linux/module.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include <linux/usb/composite.h> #include "gadget_chips.h" +#include "configfs.h" /*------------------------------------------------------------------------*/ @@ -228,26 +230,30 @@ static const char fsg_string_interface[] = "Mass Storage"; -#include "storage_common.c" +#include "storage_common.h" +#include "f_mass_storage.h" +/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */ +static struct usb_string fsg_strings[] = { + {FSG_STRING_INTERFACE, fsg_string_interface}, + {} +}; + +static struct usb_gadget_strings fsg_stringtab = { + .language = 0x0409, /* en-us */ + .strings = fsg_strings, +}; + +static struct usb_gadget_strings *fsg_strings_array[] = { + &fsg_stringtab, + NULL, +}; /*-------------------------------------------------------------------------*/ struct fsg_dev; struct fsg_common; -/* FSF callback functions */ -struct fsg_operations { - /* - * Callback function to call when thread exits. If no - * callback is set or it returns value lower then zero MSF - * will force eject all LUNs it operates on (including those - * marked as non-removable or with prevent_medium_removal flag - * set). - */ - int (*thread_exits)(struct fsg_common *common); -}; - /* Data shared by all the FSG instances. */ struct fsg_common { struct usb_gadget *gadget; @@ -268,13 +274,14 @@ struct fsg_common { struct fsg_buffhd *next_buffhd_to_fill; struct fsg_buffhd *next_buffhd_to_drain; struct fsg_buffhd *buffhds; + unsigned int fsg_num_buffers; int cmnd_size; u8 cmnd[MAX_COMMAND_SIZE]; unsigned int nluns; unsigned int lun; - struct fsg_lun *luns; + struct fsg_lun **luns; struct fsg_lun *curlun; unsigned int bulk_out_maxpacket; @@ -294,6 +301,7 @@ struct fsg_common { unsigned int short_packet_received:1; unsigned int bad_lun_okay:1; unsigned int running:1; + unsigned int sysfs:1; int thread_wakeup_needed; struct completion thread_notifier; @@ -313,27 +321,6 @@ struct fsg_common { struct kref ref; }; -struct fsg_config { - unsigned nluns; - struct fsg_lun_config { - const char *filename; - char ro; - char removable; - char cdrom; - char nofua; - } luns[FSG_MAX_LUNS]; - - /* Callback functions. */ - const struct fsg_operations *ops; - /* Gadget's private data. */ - void *private_data; - - const char *vendor_name; /* 8 characters or less */ - const char *product_name; /* 16 characters or less */ - - char can_stall; -}; - struct fsg_dev { struct usb_function function; struct usb_gadget *gadget; /* Copy of cdev->gadget */ @@ -2172,7 +2159,7 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) common->data_dir = DATA_DIR_NONE; common->lun = cbw->Lun; if (common->lun < common->nluns) - common->curlun = &common->luns[common->lun]; + common->curlun = common->luns[common->lun]; else common->curlun = NULL; common->tag = cbw->Tag; @@ -2244,7 +2231,7 @@ reset: if (common->fsg) { fsg = common->fsg; - for (i = 0; i < fsg_num_buffers; ++i) { + for (i = 0; i < common->fsg_num_buffers; ++i) { struct fsg_buffhd *bh = &common->buffhds[i]; if (bh->inreq) { @@ -2303,7 +2290,7 @@ reset: clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); /* Allocate the requests */ - for (i = 0; i < fsg_num_buffers; ++i) { + for (i = 0; i < common->fsg_num_buffers; ++i) { struct fsg_buffhd *bh = &common->buffhds[i]; rc = alloc_request(common, fsg->bulk_in, &bh->inreq); @@ -2320,7 +2307,9 @@ reset: common->running = 1; for (i = 0; i < common->nluns; ++i) - common->luns[i].unit_attention_data = SS_RESET_OCCURRED; + if (common->luns[i]) + common->luns[i]->unit_attention_data = + SS_RESET_OCCURRED; return rc; } @@ -2372,7 +2361,7 @@ static void handle_exception(struct fsg_common *common) /* Cancel all the pending transfers */ if (likely(common->fsg)) { - for (i = 0; i < fsg_num_buffers; ++i) { + for (i = 0; i < common->fsg_num_buffers; ++i) { bh = &common->buffhds[i]; if (bh->inreq_busy) usb_ep_dequeue(common->fsg->bulk_in, bh->inreq); @@ -2384,7 +2373,7 @@ static void handle_exception(struct fsg_common *common) /* Wait until everything is idle */ for (;;) { int num_active = 0; - for (i = 0; i < fsg_num_buffers; ++i) { + for (i = 0; i < common->fsg_num_buffers; ++i) { bh = &common->buffhds[i]; num_active += bh->inreq_busy + bh->outreq_busy; } @@ -2407,7 +2396,7 @@ static void handle_exception(struct fsg_common *common) */ spin_lock_irq(&common->lock); - for (i = 0; i < fsg_num_buffers; ++i) { + for (i = 0; i < common->fsg_num_buffers; ++i) { bh = &common->buffhds[i]; bh->state = BUF_STATE_EMPTY; } @@ -2420,7 +2409,9 @@ static void handle_exception(struct fsg_common *common) common->state = FSG_STATE_STATUS_PHASE; else { for (i = 0; i < common->nluns; ++i) { - curlun = &common->luns[i]; + curlun = common->luns[i]; + if (!curlun) + continue; curlun->prevent_medium_removal = 0; curlun->sense_data = SS_NO_SENSE; curlun->unit_attention_data = SS_NO_SENSE; @@ -2462,8 +2453,9 @@ static void handle_exception(struct fsg_common *common) * CONFIG_CHANGE cases. */ /* for (i = 0; i < common->nluns; ++i) */ - /* common->luns[i].unit_attention_data = */ - /* SS_RESET_OCCURRED; */ + /* if (common->luns[i]) */ + /* common->luns[i]->unit_attention_data = */ + /* SS_RESET_OCCURRED; */ break; case FSG_STATE_CONFIG_CHANGE: @@ -2559,12 +2551,13 @@ static int fsg_main_thread(void *common_) if (!common->ops || !common->ops->thread_exits || common->ops->thread_exits(common) < 0) { - struct fsg_lun *curlun = common->luns; + struct fsg_lun **curlun_it = common->luns; unsigned i = common->nluns; down_write(&common->filesem); - for (; i--; ++curlun) { - if (!fsg_lun_is_open(curlun)) + for (; i--; ++curlun_it) { + struct fsg_lun *curlun = *curlun_it; + if (!curlun || !fsg_lun_is_open(curlun)) continue; fsg_lun_close(curlun); @@ -2580,6 +2573,56 @@ static int fsg_main_thread(void *common_) /*************************** DEVICE ATTRIBUTES ***************************/ +static ssize_t ro_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + + return fsg_show_ro(curlun, buf); +} + +static ssize_t nofua_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + + return fsg_show_nofua(curlun, buf); +} + +static ssize_t file_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + struct rw_semaphore *filesem = dev_get_drvdata(dev); + + return fsg_show_file(curlun, filesem, buf); +} + +static ssize_t ro_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + struct rw_semaphore *filesem = dev_get_drvdata(dev); + + return fsg_store_ro(curlun, filesem, buf, count); +} + +static ssize_t nofua_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + + return fsg_store_nofua(curlun, buf, count); +} + +static ssize_t file_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + struct rw_semaphore *filesem = dev_get_drvdata(dev); + + return fsg_store_file(curlun, filesem, buf, count); +} + static DEVICE_ATTR_RW(ro); static DEVICE_ATTR_RW(nofua); static DEVICE_ATTR_RW(file); @@ -2597,221 +2640,422 @@ static void fsg_lun_release(struct device *dev) /* Nothing needs to be done */ } -static inline void fsg_common_get(struct fsg_common *common) +void fsg_common_get(struct fsg_common *common) { kref_get(&common->ref); } +EXPORT_SYMBOL_GPL(fsg_common_get); -static inline void fsg_common_put(struct fsg_common *common) +void fsg_common_put(struct fsg_common *common) { kref_put(&common->ref, fsg_common_release); } +EXPORT_SYMBOL_GPL(fsg_common_put); -static struct fsg_common *fsg_common_init(struct fsg_common *common, - struct usb_composite_dev *cdev, - struct fsg_config *cfg) +/* check if fsg_num_buffers is within a valid range */ +static inline int fsg_num_buffers_validate(unsigned int fsg_num_buffers) { - struct usb_gadget *gadget = cdev->gadget; - struct fsg_buffhd *bh; - struct fsg_lun *curlun; - struct fsg_lun_config *lcfg; - int nluns, i, rc; - char *pathbuf; - - rc = fsg_num_buffers_validate(); - if (rc != 0) - return ERR_PTR(rc); - - /* Find out how many LUNs there should be */ - nluns = cfg->nluns; - if (nluns < 1 || nluns > FSG_MAX_LUNS) { - dev_err(&gadget->dev, "invalid number of LUNs: %u\n", nluns); - return ERR_PTR(-EINVAL); - } + if (fsg_num_buffers >= 2 && fsg_num_buffers <= 4) + return 0; + pr_err("fsg_num_buffers %u is out of range (%d to %d)\n", + fsg_num_buffers, 2, 4); + return -EINVAL; +} - /* Allocate? */ +static struct fsg_common *fsg_common_setup(struct fsg_common *common) +{ if (!common) { - common = kzalloc(sizeof *common, GFP_KERNEL); + common = kzalloc(sizeof(*common), GFP_KERNEL); if (!common) return ERR_PTR(-ENOMEM); common->free_storage_on_release = 1; } else { - memset(common, 0, sizeof *common); common->free_storage_on_release = 0; } + init_rwsem(&common->filesem); + spin_lock_init(&common->lock); + kref_init(&common->ref); + init_completion(&common->thread_notifier); + init_waitqueue_head(&common->fsg_wait); + common->state = FSG_STATE_TERMINATED; - common->buffhds = kcalloc(fsg_num_buffers, - sizeof *(common->buffhds), GFP_KERNEL); - if (!common->buffhds) { - if (common->free_storage_on_release) - kfree(common); - return ERR_PTR(-ENOMEM); + return common; +} + +void fsg_common_set_sysfs(struct fsg_common *common, bool sysfs) +{ + common->sysfs = sysfs; +} +EXPORT_SYMBOL_GPL(fsg_common_set_sysfs); + +static void _fsg_common_free_buffers(struct fsg_buffhd *buffhds, unsigned n) +{ + if (buffhds) { + struct fsg_buffhd *bh = buffhds; + while (n--) { + kfree(bh->buf); + ++bh; + } + kfree(buffhds); } +} - common->ops = cfg->ops; - common->private_data = cfg->private_data; +int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n) +{ + struct fsg_buffhd *bh, *buffhds; + int i, rc; - common->gadget = gadget; - common->ep0 = gadget->ep0; - common->ep0req = cdev->req; - common->cdev = cdev; + rc = fsg_num_buffers_validate(n); + if (rc != 0) + return rc; + + buffhds = kcalloc(n, sizeof(*buffhds), GFP_KERNEL); + if (!buffhds) + return -ENOMEM; - /* Maybe allocate device-global string IDs, and patch descriptors */ - if (fsg_strings[FSG_STRING_INTERFACE].id == 0) { - rc = usb_string_id(cdev); - if (unlikely(rc < 0)) + /* Data buffers cyclic list */ + bh = buffhds; + i = n; + goto buffhds_first_it; + do { + bh->next = bh + 1; + ++bh; +buffhds_first_it: + bh->buf = kmalloc(FSG_BUFLEN, GFP_KERNEL); + if (unlikely(!bh->buf)) goto error_release; - fsg_strings[FSG_STRING_INTERFACE].id = rc; - fsg_intf_desc.iInterface = rc; - } + } while (--i); + bh->next = buffhds; + _fsg_common_free_buffers(common->buffhds, common->fsg_num_buffers); + common->fsg_num_buffers = n; + common->buffhds = buffhds; + + return 0; + +error_release: /* - * Create the LUNs, open their backing files, and register the - * LUN devices in sysfs. + * "buf"s pointed to by heads after n - i are NULL + * so releasing them won't hurt */ - curlun = kcalloc(nluns, sizeof(*curlun), GFP_KERNEL); - if (unlikely(!curlun)) { - rc = -ENOMEM; - goto error_release; + _fsg_common_free_buffers(buffhds, n); + + return -ENOMEM; +} +EXPORT_SYMBOL_GPL(fsg_common_set_num_buffers); + +static inline void fsg_common_remove_sysfs(struct fsg_lun *lun) +{ + device_remove_file(&lun->dev, &dev_attr_nofua); + /* + * device_remove_file() => + * + * here the attr (e.g. dev_attr_ro) is only used to be passed to: + * + * sysfs_remove_file() => + * + * here e.g. both dev_attr_ro_cdrom and dev_attr_ro are in + * the same namespace and + * from here only attr->name is passed to: + * + * sysfs_hash_and_remove() + * + * attr->name is the same for dev_attr_ro_cdrom and + * dev_attr_ro + * attr->name is the same for dev_attr_file and + * dev_attr_file_nonremovable + * + * so we don't differentiate between removing e.g. dev_attr_ro_cdrom + * and dev_attr_ro + */ + device_remove_file(&lun->dev, &dev_attr_ro); + device_remove_file(&lun->dev, &dev_attr_file); +} + +void fsg_common_remove_lun(struct fsg_lun *lun, bool sysfs) +{ + if (sysfs) { + fsg_common_remove_sysfs(lun); + device_unregister(&lun->dev); } - common->luns = curlun; + fsg_lun_close(lun); + kfree(lun); +} +EXPORT_SYMBOL_GPL(fsg_common_remove_lun); - init_rwsem(&common->filesem); +static void _fsg_common_remove_luns(struct fsg_common *common, int n) +{ + int i; - for (i = 0, lcfg = cfg->luns; i < nluns; ++i, ++curlun, ++lcfg) { - curlun->cdrom = !!lcfg->cdrom; - curlun->ro = lcfg->cdrom || lcfg->ro; - curlun->initially_ro = curlun->ro; - curlun->removable = lcfg->removable; - curlun->dev.release = fsg_lun_release; - curlun->dev.parent = &gadget->dev; - /* curlun->dev.driver = &fsg_driver.driver; XXX */ - dev_set_drvdata(&curlun->dev, &common->filesem); - dev_set_name(&curlun->dev, "lun%d", i); - - rc = device_register(&curlun->dev); - if (rc) { - INFO(common, "failed to register LUN%d: %d\n", i, rc); - common->nluns = i; - put_device(&curlun->dev); - goto error_release; + for (i = 0; i < n; ++i) + if (common->luns[i]) { + fsg_common_remove_lun(common->luns[i], common->sysfs); + common->luns[i] = NULL; } +} +EXPORT_SYMBOL_GPL(fsg_common_remove_luns); - rc = device_create_file(&curlun->dev, - curlun->cdrom - ? &dev_attr_ro_cdrom - : &dev_attr_ro); - if (rc) - goto error_luns; - rc = device_create_file(&curlun->dev, - curlun->removable - ? &dev_attr_file - : &dev_attr_file_nonremovable); - if (rc) - goto error_luns; - rc = device_create_file(&curlun->dev, &dev_attr_nofua); - if (rc) - goto error_luns; +void fsg_common_remove_luns(struct fsg_common *common) +{ + _fsg_common_remove_luns(common, common->nluns); +} - if (lcfg->filename) { - rc = fsg_lun_open(curlun, lcfg->filename); - if (rc) - goto error_luns; - } else if (!curlun->removable) { - ERROR(common, "no file given for LUN%d\n", i); - rc = -EINVAL; - goto error_luns; - } +void fsg_common_free_luns(struct fsg_common *common) +{ + fsg_common_remove_luns(common); + kfree(common->luns); + common->luns = NULL; +} +EXPORT_SYMBOL_GPL(fsg_common_free_luns); + +int fsg_common_set_nluns(struct fsg_common *common, int nluns) +{ + struct fsg_lun **curlun; + + /* Find out how many LUNs there should be */ + if (nluns < 1 || nluns > FSG_MAX_LUNS) { + pr_err("invalid number of LUNs: %u\n", nluns); + return -EINVAL; } + + curlun = kcalloc(nluns, sizeof(*curlun), GFP_KERNEL); + if (unlikely(!curlun)) + return -ENOMEM; + + if (common->luns) + fsg_common_free_luns(common); + + common->luns = curlun; common->nluns = nluns; - /* Data buffers cyclic list */ - bh = common->buffhds; - i = fsg_num_buffers; - goto buffhds_first_it; - do { - bh->next = bh + 1; - ++bh; -buffhds_first_it: - bh->buf = kmalloc(FSG_BUFLEN, GFP_KERNEL); - if (unlikely(!bh->buf)) { - rc = -ENOMEM; - goto error_release; - } - } while (--i); - bh->next = common->buffhds; + pr_info("Number of LUNs=%d\n", common->nluns); - /* Prepare inquiryString */ - i = get_default_bcdDevice(); - snprintf(common->inquiry_string, sizeof common->inquiry_string, - "%-8s%-16s%04x", cfg->vendor_name ?: "Linux", - /* Assume product name dependent on the first LUN */ - cfg->product_name ?: (common->luns->cdrom - ? "File-CD Gadget" - : "File-Stor Gadget"), - i); + return 0; +} +EXPORT_SYMBOL_GPL(fsg_common_set_nluns); + +void fsg_common_set_ops(struct fsg_common *common, + const struct fsg_operations *ops) +{ + common->ops = ops; +} +EXPORT_SYMBOL_GPL(fsg_common_set_ops); + +void fsg_common_free_buffers(struct fsg_common *common) +{ + _fsg_common_free_buffers(common->buffhds, common->fsg_num_buffers); + common->buffhds = NULL; +} +EXPORT_SYMBOL_GPL(fsg_common_free_buffers); + +int fsg_common_set_cdev(struct fsg_common *common, + struct usb_composite_dev *cdev, bool can_stall) +{ + struct usb_string *us; + + common->gadget = cdev->gadget; + common->ep0 = cdev->gadget->ep0; + common->ep0req = cdev->req; + common->cdev = cdev; + + us = usb_gstrings_attach(cdev, fsg_strings_array, + ARRAY_SIZE(fsg_strings)); + if (IS_ERR(us)) + return PTR_ERR(us); + + fsg_intf_desc.iInterface = us[FSG_STRING_INTERFACE].id; /* * Some peripheral controllers are known not to be able to * halt bulk endpoints correctly. If one of them is present, * disable stalls. */ - common->can_stall = cfg->can_stall && - !(gadget_is_at91(common->gadget)); + common->can_stall = can_stall && !(gadget_is_at91(common->gadget)); - spin_lock_init(&common->lock); - kref_init(&common->ref); + return 0; +} +EXPORT_SYMBOL_GPL(fsg_common_set_cdev); - /* Tell the thread to start working */ - common->thread_task = - kthread_create(fsg_main_thread, common, "file-storage"); - if (IS_ERR(common->thread_task)) { - rc = PTR_ERR(common->thread_task); - goto error_release; +static inline int fsg_common_add_sysfs(struct fsg_common *common, + struct fsg_lun *lun) +{ + int rc; + + rc = device_register(&lun->dev); + if (rc) { + put_device(&lun->dev); + return rc; } - init_completion(&common->thread_notifier); - init_waitqueue_head(&common->fsg_wait); - /* Information */ - INFO(common, FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n"); - INFO(common, "Number of LUNs=%d\n", common->nluns); + rc = device_create_file(&lun->dev, + lun->cdrom + ? &dev_attr_ro_cdrom + : &dev_attr_ro); + if (rc) + goto error; + rc = device_create_file(&lun->dev, + lun->removable + ? &dev_attr_file + : &dev_attr_file_nonremovable); + if (rc) + goto error; + rc = device_create_file(&lun->dev, &dev_attr_nofua); + if (rc) + goto error; + + return 0; + +error: + /* removing nonexistent files is a no-op */ + fsg_common_remove_sysfs(lun); + device_unregister(&lun->dev); + return rc; +} + +int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg, + unsigned int id, const char *name, + const char **name_pfx) +{ + struct fsg_lun *lun; + char *pathbuf, *p; + int rc = -ENOMEM; + + if (!common->nluns || !common->luns) + return -ENODEV; + + if (common->luns[id]) + return -EBUSY; + + if (!cfg->filename && !cfg->removable) { + pr_err("no file given for LUN%d\n", id); + return -EINVAL; + } + + lun = kzalloc(sizeof(*lun), GFP_KERNEL); + if (!lun) + return -ENOMEM; + + lun->name_pfx = name_pfx; + + lun->cdrom = !!cfg->cdrom; + lun->ro = cfg->cdrom || cfg->ro; + lun->initially_ro = lun->ro; + lun->removable = !!cfg->removable; + + if (!common->sysfs) { + /* we DON'T own the name!*/ + lun->name = name; + } else { + lun->dev.release = fsg_lun_release; + lun->dev.parent = &common->gadget->dev; + dev_set_drvdata(&lun->dev, &common->filesem); + dev_set_name(&lun->dev, name); + lun->name = dev_name(&lun->dev); + + rc = fsg_common_add_sysfs(common, lun); + if (rc) { + pr_info("failed to register LUN%d: %d\n", id, rc); + goto error_sysfs; + } + } + + common->luns[id] = lun; + + if (cfg->filename) { + rc = fsg_lun_open(lun, cfg->filename); + if (rc) + goto error_lun; + } 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)"; - } + p = "(no medium)"; + if (fsg_lun_is_open(lun)) { + p = "(error)"; + if (pathbuf) { + p = d_path(&lun->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); } + pr_info("LUN: %s%s%sfile: %s\n", + lun->removable ? "removable " : "", + lun->ro ? "read only " : "", + lun->cdrom ? "CD-ROM " : "", + p); kfree(pathbuf); + return 0; + +error_lun: + if (common->sysfs) { + fsg_common_remove_sysfs(lun); + device_unregister(&lun->dev); + } + fsg_lun_close(lun); + common->luns[id] = NULL; +error_sysfs: + kfree(lun); + return rc; +} +EXPORT_SYMBOL_GPL(fsg_common_create_lun); + +int fsg_common_create_luns(struct fsg_common *common, struct fsg_config *cfg) +{ + char buf[8]; /* enough for 100000000 different numbers, decimal */ + int i, rc; + + for (i = 0; i < common->nluns; ++i) { + snprintf(buf, sizeof(buf), "lun%d", i); + rc = fsg_common_create_lun(common, &cfg->luns[i], i, buf, NULL); + if (rc) + goto fail; + } + + pr_info("Number of LUNs=%d\n", common->nluns); + + return 0; + +fail: + _fsg_common_remove_luns(common, i); + return rc; +} +EXPORT_SYMBOL_GPL(fsg_common_create_luns); + +void fsg_common_set_inquiry_string(struct fsg_common *common, const char *vn, + const char *pn) +{ + int i; + + /* Prepare inquiryString */ + i = get_default_bcdDevice(); + snprintf(common->inquiry_string, sizeof(common->inquiry_string), + "%-8s%-16s%04x", vn ?: "Linux", + /* Assume product name dependent on the first LUN */ + pn ?: ((*common->luns)->cdrom + ? "File-CD Gadget" + : "File-Stor Gadget"), + i); +} +EXPORT_SYMBOL_GPL(fsg_common_set_inquiry_string); + +int fsg_common_run_thread(struct fsg_common *common) +{ + common->state = FSG_STATE_IDLE; + /* Tell the thread to start working */ + common->thread_task = + kthread_create(fsg_main_thread, common, "file-storage"); + if (IS_ERR(common->thread_task)) { + common->state = FSG_STATE_TERMINATED; + return PTR_ERR(common->thread_task); + } + DBG(common, "I/O thread pid: %d\n", task_pid_nr(common->thread_task)); wake_up_process(common->thread_task); - return common; - -error_luns: - common->nluns = i + 1; -error_release: - common->state = FSG_STATE_TERMINATED; /* The thread is dead */ - /* Call fsg_common_release() directly, ref might be not initialised. */ - fsg_common_release(&common->ref); - return ERR_PTR(rc); + return 0; } +EXPORT_SYMBOL_GPL(fsg_common_run_thread); static void fsg_common_release(struct kref *ref) { @@ -2824,36 +3068,26 @@ static void fsg_common_release(struct kref *ref) } if (likely(common->luns)) { - struct fsg_lun *lun = common->luns; + struct fsg_lun **lun_it = common->luns; unsigned i = common->nluns; /* In error recovery common->nluns may be zero. */ - for (; i; --i, ++lun) { - device_remove_file(&lun->dev, &dev_attr_nofua); - device_remove_file(&lun->dev, - lun->cdrom - ? &dev_attr_ro_cdrom - : &dev_attr_ro); - device_remove_file(&lun->dev, - lun->removable - ? &dev_attr_file - : &dev_attr_file_nonremovable); + for (; i; --i, ++lun_it) { + struct fsg_lun *lun = *lun_it; + if (!lun) + continue; + if (common->sysfs) + fsg_common_remove_sysfs(lun); fsg_lun_close(lun); - device_unregister(&lun->dev); + if (common->sysfs) + device_unregister(&lun->dev); + kfree(lun); } kfree(common->luns); } - { - struct fsg_buffhd *bh = common->buffhds; - unsigned i = fsg_num_buffers; - do { - kfree(bh->buf); - } while (++bh, --i); - } - - kfree(common->buffhds); + _fsg_common_free_buffers(common->buffhds, common->fsg_num_buffers); if (common->free_storage_on_release) kfree(common); } @@ -2861,24 +3095,6 @@ static void fsg_common_release(struct kref *ref) /*-------------------------------------------------------------------------*/ -static void fsg_unbind(struct usb_configuration *c, struct usb_function *f) -{ - struct fsg_dev *fsg = fsg_from_func(f); - struct fsg_common *common = fsg->common; - - DBG(fsg, "unbind\n"); - if (fsg->common->fsg == fsg) { - fsg->common->new_fsg = NULL; - raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); - /* FIXME: make interruptible or killable somehow? */ - wait_event(common->fsg_wait, common->fsg != fsg); - } - - fsg_common_put(common); - usb_free_all_descriptors(&fsg->function); - kfree(fsg); -} - static int fsg_bind(struct usb_configuration *c, struct usb_function *f) { struct fsg_dev *fsg = fsg_from_func(f); @@ -2887,6 +3103,19 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f) struct usb_ep *ep; unsigned max_burst; int ret; + struct fsg_opts *opts; + + opts = fsg_opts_from_func_inst(f->fi); + if (!opts->no_configfs) { + ret = fsg_common_set_cdev(fsg->common, c->cdev, + fsg->common->can_stall); + if (ret) + return ret; + fsg_common_set_inquiry_string(fsg->common, 0, 0); + ret = fsg_common_run_thread(fsg->common); + if (ret) + return ret; + } fsg->gadget = gadget; @@ -2939,95 +3168,472 @@ autoconf_fail: return -ENOTSUPP; } -/****************************** ADD FUNCTION ******************************/ +/****************************** ALLOCATE FUNCTION *************************/ -static struct usb_gadget_strings *fsg_strings_array[] = { - &fsg_stringtab, +static void fsg_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct fsg_dev *fsg = fsg_from_func(f); + struct fsg_common *common = fsg->common; + + DBG(fsg, "unbind\n"); + if (fsg->common->fsg == fsg) { + fsg->common->new_fsg = NULL; + raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); + /* FIXME: make interruptible or killable somehow? */ + wait_event(common->fsg_wait, common->fsg != fsg); + } + + usb_free_all_descriptors(&fsg->function); +} + +static inline struct fsg_lun_opts *to_fsg_lun_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct fsg_lun_opts, group); +} + +static inline struct fsg_opts *to_fsg_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct fsg_opts, + func_inst.group); +} + +CONFIGFS_ATTR_STRUCT(fsg_lun_opts); +CONFIGFS_ATTR_OPS(fsg_lun_opts); + +static void fsg_lun_attr_release(struct config_item *item) +{ + struct fsg_lun_opts *lun_opts; + + lun_opts = to_fsg_lun_opts(item); + kfree(lun_opts); +} + +static struct configfs_item_operations fsg_lun_item_ops = { + .release = fsg_lun_attr_release, + .show_attribute = fsg_lun_opts_attr_show, + .store_attribute = fsg_lun_opts_attr_store, +}; + +static ssize_t fsg_lun_opts_file_show(struct fsg_lun_opts *opts, char *page) +{ + struct fsg_opts *fsg_opts; + + fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent); + + return fsg_show_file(opts->lun, &fsg_opts->common->filesem, page); +} + +static ssize_t fsg_lun_opts_file_store(struct fsg_lun_opts *opts, + const char *page, size_t len) +{ + struct fsg_opts *fsg_opts; + + fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent); + + return fsg_store_file(opts->lun, &fsg_opts->common->filesem, page, len); +} + +static struct fsg_lun_opts_attribute fsg_lun_opts_file = + __CONFIGFS_ATTR(file, S_IRUGO | S_IWUSR, fsg_lun_opts_file_show, + fsg_lun_opts_file_store); + +static ssize_t fsg_lun_opts_ro_show(struct fsg_lun_opts *opts, char *page) +{ + return fsg_show_ro(opts->lun, page); +} + +static ssize_t fsg_lun_opts_ro_store(struct fsg_lun_opts *opts, + const char *page, size_t len) +{ + struct fsg_opts *fsg_opts; + + fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent); + + return fsg_store_ro(opts->lun, &fsg_opts->common->filesem, page, len); +} + +static struct fsg_lun_opts_attribute fsg_lun_opts_ro = + __CONFIGFS_ATTR(ro, S_IRUGO | S_IWUSR, fsg_lun_opts_ro_show, + fsg_lun_opts_ro_store); + +static ssize_t fsg_lun_opts_removable_show(struct fsg_lun_opts *opts, + char *page) +{ + return fsg_show_removable(opts->lun, page); +} + +static ssize_t fsg_lun_opts_removable_store(struct fsg_lun_opts *opts, + const char *page, size_t len) +{ + return fsg_store_removable(opts->lun, page, len); +} + +static struct fsg_lun_opts_attribute fsg_lun_opts_removable = + __CONFIGFS_ATTR(removable, S_IRUGO | S_IWUSR, + fsg_lun_opts_removable_show, + fsg_lun_opts_removable_store); + +static ssize_t fsg_lun_opts_cdrom_show(struct fsg_lun_opts *opts, char *page) +{ + return fsg_show_cdrom(opts->lun, page); +} + +static ssize_t fsg_lun_opts_cdrom_store(struct fsg_lun_opts *opts, + const char *page, size_t len) +{ + struct fsg_opts *fsg_opts; + + fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent); + + return fsg_store_cdrom(opts->lun, &fsg_opts->common->filesem, page, + len); +} + +static struct fsg_lun_opts_attribute fsg_lun_opts_cdrom = + __CONFIGFS_ATTR(cdrom, S_IRUGO | S_IWUSR, fsg_lun_opts_cdrom_show, + fsg_lun_opts_cdrom_store); + +static ssize_t fsg_lun_opts_nofua_show(struct fsg_lun_opts *opts, char *page) +{ + return fsg_show_nofua(opts->lun, page); +} + +static ssize_t fsg_lun_opts_nofua_store(struct fsg_lun_opts *opts, + const char *page, size_t len) +{ + return fsg_store_nofua(opts->lun, page, len); +} + +static struct fsg_lun_opts_attribute fsg_lun_opts_nofua = + __CONFIGFS_ATTR(nofua, S_IRUGO | S_IWUSR, fsg_lun_opts_nofua_show, + fsg_lun_opts_nofua_store); + +static struct configfs_attribute *fsg_lun_attrs[] = { + &fsg_lun_opts_file.attr, + &fsg_lun_opts_ro.attr, + &fsg_lun_opts_removable.attr, + &fsg_lun_opts_cdrom.attr, + &fsg_lun_opts_nofua.attr, NULL, }; -static int fsg_bind_config(struct usb_composite_dev *cdev, - struct usb_configuration *c, - struct fsg_common *common) +static struct config_item_type fsg_lun_type = { + .ct_item_ops = &fsg_lun_item_ops, + .ct_attrs = fsg_lun_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_group *fsg_lun_make(struct config_group *group, + const char *name) { - struct fsg_dev *fsg; + struct fsg_lun_opts *opts; + struct fsg_opts *fsg_opts; + struct fsg_lun_config config; + char *num_str; + u8 num; + int ret; + + num_str = strchr(name, '.'); + if (!num_str) { + pr_err("Unable to locate . in LUN.NUMBER\n"); + return ERR_PTR(-EINVAL); + } + num_str++; + + ret = kstrtou8(num_str, 0, &num); + if (ret) + return ERR_PTR(ret); + + fsg_opts = to_fsg_opts(&group->cg_item); + if (num >= FSG_MAX_LUNS) + return ERR_PTR(-ERANGE); + + mutex_lock(&fsg_opts->lock); + if (fsg_opts->refcnt || fsg_opts->common->luns[num]) { + ret = -EBUSY; + goto out; + } + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) { + ret = -ENOMEM; + goto out; + } + + memset(&config, 0, sizeof(config)); + config.removable = true; + + ret = fsg_common_create_lun(fsg_opts->common, &config, num, name, + (const char **)&group->cg_item.ci_name); + if (ret) { + kfree(opts); + goto out; + } + opts->lun = fsg_opts->common->luns[num]; + opts->lun_id = num; + mutex_unlock(&fsg_opts->lock); + + config_group_init_type_name(&opts->group, name, &fsg_lun_type); + + return &opts->group; +out: + mutex_unlock(&fsg_opts->lock); + return ERR_PTR(ret); +} + +static void fsg_lun_drop(struct config_group *group, struct config_item *item) +{ + struct fsg_lun_opts *lun_opts; + struct fsg_opts *fsg_opts; + + lun_opts = to_fsg_lun_opts(item); + fsg_opts = to_fsg_opts(&group->cg_item); + + mutex_lock(&fsg_opts->lock); + if (fsg_opts->refcnt) { + struct config_item *gadget; + + gadget = group->cg_item.ci_parent->ci_parent; + unregister_gadget_item(gadget); + } + + fsg_common_remove_lun(lun_opts->lun, fsg_opts->common->sysfs); + fsg_opts->common->luns[lun_opts->lun_id] = NULL; + lun_opts->lun_id = 0; + mutex_unlock(&fsg_opts->lock); + + config_item_put(item); +} + +CONFIGFS_ATTR_STRUCT(fsg_opts); +CONFIGFS_ATTR_OPS(fsg_opts); + +static void fsg_attr_release(struct config_item *item) +{ + struct fsg_opts *opts = to_fsg_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations fsg_item_ops = { + .release = fsg_attr_release, + .show_attribute = fsg_opts_attr_show, + .store_attribute = fsg_opts_attr_store, +}; + +static ssize_t fsg_opts_stall_show(struct fsg_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->common->can_stall); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t fsg_opts_stall_store(struct fsg_opts *opts, const char *page, + size_t len) +{ + int ret; + bool stall; + + mutex_lock(&opts->lock); + + if (opts->refcnt) { + mutex_unlock(&opts->lock); + return -EBUSY; + } + + ret = strtobool(page, &stall); + if (!ret) { + opts->common->can_stall = stall; + ret = len; + } + + mutex_unlock(&opts->lock); + + return ret; +} + +static struct fsg_opts_attribute fsg_opts_stall = + __CONFIGFS_ATTR(stall, S_IRUGO | S_IWUSR, fsg_opts_stall_show, + fsg_opts_stall_store); + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES +static ssize_t fsg_opts_num_buffers_show(struct fsg_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->common->fsg_num_buffers); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t fsg_opts_num_buffers_store(struct fsg_opts *opts, + const char *page, size_t len) +{ + int ret; + u8 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + ret = kstrtou8(page, 0, &num); + if (ret) + goto end; + + ret = fsg_num_buffers_validate(num); + if (ret) + goto end; + + fsg_common_set_num_buffers(opts->common, num); + ret = len; + +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct fsg_opts_attribute fsg_opts_num_buffers = + __CONFIGFS_ATTR(num_buffers, S_IRUGO | S_IWUSR, + fsg_opts_num_buffers_show, + fsg_opts_num_buffers_store); + +#endif + +static struct configfs_attribute *fsg_attrs[] = { + &fsg_opts_stall.attr, +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + &fsg_opts_num_buffers.attr, +#endif + NULL, +}; + +static struct configfs_group_operations fsg_group_ops = { + .make_group = fsg_lun_make, + .drop_item = fsg_lun_drop, +}; + +static struct config_item_type fsg_func_type = { + .ct_item_ops = &fsg_item_ops, + .ct_group_ops = &fsg_group_ops, + .ct_attrs = fsg_attrs, + .ct_owner = THIS_MODULE, +}; + +static void fsg_free_inst(struct usb_function_instance *fi) +{ + struct fsg_opts *opts; + + opts = fsg_opts_from_func_inst(fi); + fsg_common_put(opts->common); + kfree(opts); +} + +static struct usb_function_instance *fsg_alloc_inst(void) +{ + struct fsg_opts *opts; + struct fsg_lun_config config; int rc; - fsg = kzalloc(sizeof *fsg, GFP_KERNEL); + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + mutex_init(&opts->lock); + opts->func_inst.free_func_inst = fsg_free_inst; + opts->common = fsg_common_setup(opts->common); + if (IS_ERR(opts->common)) { + rc = PTR_ERR(opts->common); + goto release_opts; + } + rc = fsg_common_set_nluns(opts->common, FSG_MAX_LUNS); + if (rc) + goto release_opts; + + rc = fsg_common_set_num_buffers(opts->common, + CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS); + if (rc) + goto release_luns; + + pr_info(FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n"); + + memset(&config, 0, sizeof(config)); + config.removable = true; + rc = fsg_common_create_lun(opts->common, &config, 0, "lun.0", + (const char **)&opts->func_inst.group.cg_item.ci_name); + opts->lun0.lun = opts->common->luns[0]; + opts->lun0.lun_id = 0; + config_group_init_type_name(&opts->lun0.group, "lun.0", &fsg_lun_type); + opts->default_groups[0] = &opts->lun0.group; + opts->func_inst.group.default_groups = opts->default_groups; + + config_group_init_type_name(&opts->func_inst.group, "", &fsg_func_type); + + return &opts->func_inst; + +release_luns: + kfree(opts->common->luns); +release_opts: + kfree(opts); + return ERR_PTR(rc); +} + +static void fsg_free(struct usb_function *f) +{ + struct fsg_dev *fsg; + struct fsg_opts *opts; + + fsg = container_of(f, struct fsg_dev, function); + opts = container_of(f->fi, struct fsg_opts, func_inst); + + mutex_lock(&opts->lock); + opts->refcnt--; + mutex_unlock(&opts->lock); + + kfree(fsg); +} + +static struct usb_function *fsg_alloc(struct usb_function_instance *fi) +{ + struct fsg_opts *opts = fsg_opts_from_func_inst(fi); + struct fsg_common *common = opts->common; + struct fsg_dev *fsg; + + fsg = kzalloc(sizeof(*fsg), GFP_KERNEL); if (unlikely(!fsg)) - return -ENOMEM; + return ERR_PTR(-ENOMEM); - fsg->function.name = FSG_DRIVER_DESC; - fsg->function.strings = fsg_strings_array; - 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; + mutex_lock(&opts->lock); + opts->refcnt++; + mutex_unlock(&opts->lock); + fsg->function.name = FSG_DRIVER_DESC; + 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->function.free_func = fsg_free; 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 (unlikely(rc)) - kfree(fsg); - else - fsg_common_get(fsg->common); - return rc; + return &fsg->function; } +DECLARE_USB_FUNCTION_INIT(mass_storage, fsg_alloc_inst, fsg_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michal Nazarewicz"); /************************* Module parameters *************************/ -struct fsg_module_parameters { - char *file[FSG_MAX_LUNS]; - bool ro[FSG_MAX_LUNS]; - bool removable[FSG_MAX_LUNS]; - bool cdrom[FSG_MAX_LUNS]; - bool nofua[FSG_MAX_LUNS]; - - unsigned int file_count, ro_count, removable_count, cdrom_count; - unsigned int nofua_count; - unsigned int luns; /* nluns */ - bool stall; /* can_stall */ -}; -#define _FSG_MODULE_PARAM_ARRAY(prefix, params, name, type, desc) \ - module_param_array_named(prefix ## name, params.name, type, \ - &prefix ## params.name ## _count, \ - S_IRUGO); \ - MODULE_PARM_DESC(prefix ## name, desc) - -#define _FSG_MODULE_PARAM(prefix, params, name, type, desc) \ - module_param_named(prefix ## name, params.name, type, \ - S_IRUGO); \ - MODULE_PARM_DESC(prefix ## name, desc) - -#define FSG_MODULE_PARAMETERS(prefix, params) \ - _FSG_MODULE_PARAM_ARRAY(prefix, params, file, charp, \ - "names of backing files or devices"); \ - _FSG_MODULE_PARAM_ARRAY(prefix, params, ro, bool, \ - "true to force read-only"); \ - _FSG_MODULE_PARAM_ARRAY(prefix, params, removable, bool, \ - "true to simulate removable media"); \ - _FSG_MODULE_PARAM_ARRAY(prefix, params, cdrom, bool, \ - "true to simulate CD-ROM instead of disk"); \ - _FSG_MODULE_PARAM_ARRAY(prefix, params, nofua, bool, \ - "true to ignore SCSI WRITE(10,12) FUA bit"); \ - _FSG_MODULE_PARAM(prefix, params, luns, uint, \ - "number of LUNs"); \ - _FSG_MODULE_PARAM(prefix, params, stall, bool, \ - "false to prevent bulk stalls") - -static void -fsg_config_from_params(struct fsg_config *cfg, - const struct fsg_module_parameters *params) +void fsg_config_from_params(struct fsg_config *cfg, + const struct fsg_module_parameters *params, + unsigned int fsg_num_buffers) { struct fsg_lun_config *lun; unsigned i; @@ -3055,19 +3661,7 @@ fsg_config_from_params(struct fsg_config *cfg, /* Finalise */ cfg->can_stall = params->stall; + cfg->fsg_num_buffers = fsg_num_buffers; } +EXPORT_SYMBOL_GPL(fsg_config_from_params); -static inline struct fsg_common * -fsg_common_from_params(struct fsg_common *common, - struct usb_composite_dev *cdev, - const struct fsg_module_parameters *params) - __attribute__((unused)); -static inline struct fsg_common * -fsg_common_from_params(struct fsg_common *common, - struct usb_composite_dev *cdev, - const struct fsg_module_parameters *params) -{ - struct fsg_config cfg; - fsg_config_from_params(&cfg, params); - return fsg_common_init(common, cdev, &cfg); -} diff --git a/drivers/usb/gadget/f_mass_storage.h b/drivers/usb/gadget/f_mass_storage.h new file mode 100644 index 000000000000..b4866fcef30b --- /dev/null +++ b/drivers/usb/gadget/f_mass_storage.h @@ -0,0 +1,166 @@ +#ifndef USB_F_MASS_STORAGE_H +#define USB_F_MASS_STORAGE_H + +#include <linux/usb/composite.h> +#include "storage_common.h" + +struct fsg_module_parameters { + char *file[FSG_MAX_LUNS]; + bool ro[FSG_MAX_LUNS]; + bool removable[FSG_MAX_LUNS]; + bool cdrom[FSG_MAX_LUNS]; + bool nofua[FSG_MAX_LUNS]; + + unsigned int file_count, ro_count, removable_count, cdrom_count; + unsigned int nofua_count; + unsigned int luns; /* nluns */ + bool stall; /* can_stall */ +}; + +#define _FSG_MODULE_PARAM_ARRAY(prefix, params, name, type, desc) \ + module_param_array_named(prefix ## name, params.name, type, \ + &prefix ## params.name ## _count, \ + S_IRUGO); \ + MODULE_PARM_DESC(prefix ## name, desc) + +#define _FSG_MODULE_PARAM(prefix, params, name, type, desc) \ + module_param_named(prefix ## name, params.name, type, \ + S_IRUGO); \ + MODULE_PARM_DESC(prefix ## name, desc) + +#define __FSG_MODULE_PARAMETERS(prefix, params) \ + _FSG_MODULE_PARAM_ARRAY(prefix, params, file, charp, \ + "names of backing files or devices"); \ + _FSG_MODULE_PARAM_ARRAY(prefix, params, ro, bool, \ + "true to force read-only"); \ + _FSG_MODULE_PARAM_ARRAY(prefix, params, removable, bool, \ + "true to simulate removable media"); \ + _FSG_MODULE_PARAM_ARRAY(prefix, params, cdrom, bool, \ + "true to simulate CD-ROM instead of disk"); \ + _FSG_MODULE_PARAM_ARRAY(prefix, params, nofua, bool, \ + "true to ignore SCSI WRITE(10,12) FUA bit"); \ + _FSG_MODULE_PARAM(prefix, params, luns, uint, \ + "number of LUNs"); \ + _FSG_MODULE_PARAM(prefix, params, stall, bool, \ + "false to prevent bulk stalls") + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + +#define FSG_MODULE_PARAMETERS(prefix, params) \ + __FSG_MODULE_PARAMETERS(prefix, params); \ + module_param_named(num_buffers, fsg_num_buffers, uint, S_IRUGO);\ + MODULE_PARM_DESC(num_buffers, "Number of pipeline buffers") +#else + +#define FSG_MODULE_PARAMETERS(prefix, params) \ + __FSG_MODULE_PARAMETERS(prefix, params) + +#endif + +struct fsg_common; + +/* FSF callback functions */ +struct fsg_operations { + /* + * Callback function to call when thread exits. If no + * callback is set or it returns value lower then zero MSF + * will force eject all LUNs it operates on (including those + * marked as non-removable or with prevent_medium_removal flag + * set). + */ + int (*thread_exits)(struct fsg_common *common); +}; + +struct fsg_lun_opts { + struct config_group group; + struct fsg_lun *lun; + int lun_id; +}; + +struct fsg_opts { + struct fsg_common *common; + struct usb_function_instance func_inst; + struct fsg_lun_opts lun0; + struct config_group *default_groups[2]; + bool no_configfs; /* for legacy gadgets */ + + /* + * Read/write access to configfs attributes is handled by configfs. + * + * This is to protect the data from concurrent access by read/write + * and create symlink/remove symlink. + */ + struct mutex lock; + int refcnt; +}; + +struct fsg_lun_config { + const char *filename; + char ro; + char removable; + char cdrom; + char nofua; +}; + +struct fsg_config { + unsigned nluns; + struct fsg_lun_config luns[FSG_MAX_LUNS]; + + /* Callback functions. */ + const struct fsg_operations *ops; + /* Gadget's private data. */ + void *private_data; + + const char *vendor_name; /* 8 characters or less */ + const char *product_name; /* 16 characters or less */ + + char can_stall; + unsigned int fsg_num_buffers; +}; + +static inline struct fsg_opts * +fsg_opts_from_func_inst(const struct usb_function_instance *fi) +{ + return container_of(fi, struct fsg_opts, func_inst); +} + +void fsg_common_get(struct fsg_common *common); + +void fsg_common_put(struct fsg_common *common); + +void fsg_common_set_sysfs(struct fsg_common *common, bool sysfs); + +int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n); + +void fsg_common_free_buffers(struct fsg_common *common); + +int fsg_common_set_cdev(struct fsg_common *common, + struct usb_composite_dev *cdev, bool can_stall); + +void fsg_common_remove_lun(struct fsg_lun *lun, bool sysfs); + +void fsg_common_remove_luns(struct fsg_common *common); + +void fsg_common_free_luns(struct fsg_common *common); + +int fsg_common_set_nluns(struct fsg_common *common, int nluns); + +void fsg_common_set_ops(struct fsg_common *common, + const struct fsg_operations *ops); + +int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg, + unsigned int id, const char *name, + const char **name_pfx); + +int fsg_common_create_luns(struct fsg_common *common, struct fsg_config *cfg); + +void fsg_common_set_inquiry_string(struct fsg_common *common, const char *vn, + const char *pn); + +int fsg_common_run_thread(struct fsg_common *common); + +void fsg_config_from_params(struct fsg_config *cfg, + const struct fsg_module_parameters *params, + unsigned int fsg_num_buffers); + +#endif /* USB_F_MASS_STORAGE_H */ diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c index 080e577773d5..8e27a8c96444 100644 --- a/drivers/usb/gadget/mass_storage.c +++ b/drivers/usb/gadget/mass_storage.c @@ -37,16 +37,16 @@ #define DRIVER_DESC "Mass Storage Gadget" #define DRIVER_VERSION "2009/09/11" -/*-------------------------------------------------------------------------*/ - /* - * 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. + * Thanks to NetChip Technologies for donating this product ID. + * + * DO NOT REUSE THESE IDs with any other driver!! Ever!! + * Instead: allocate your own, using normal USB-IF procedures. */ -#include "f_mass_storage.c" +#define FSG_VENDOR_ID 0x0525 /* NetChip */ +#define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */ + +#include "f_mass_storage.h" /*-------------------------------------------------------------------------*/ USB_GADGET_COMPOSITE_OPTIONS(); @@ -97,11 +97,28 @@ static struct usb_gadget_strings *dev_strings[] = { NULL, }; +static struct usb_function_instance *fi_msg; +static struct usb_function *f_msg; + /****************************** Configurations ******************************/ static struct fsg_module_parameters mod_data = { .stall = 1 }; +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + +static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS; + +#else + +/* + * Number of buffers we will use. + * 2 is usually enough for good buffering pipeline + */ +#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS + +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ + FSG_MODULE_PARAMETERS(/* no prefix */, mod_data); static unsigned long msg_registered; @@ -115,13 +132,7 @@ static int msg_thread_exits(struct fsg_common *common) static int __init msg_do_config(struct usb_configuration *c) { - static const struct fsg_operations ops = { - .thread_exits = msg_thread_exits, - }; - static struct fsg_common common; - - struct fsg_common *retp; - struct fsg_config config; + struct fsg_opts *opts; int ret; if (gadget_is_otg(c->cdev->gadget)) { @@ -129,15 +140,24 @@ static int __init msg_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - fsg_config_from_params(&config, &mod_data); - config.ops = &ops; + opts = fsg_opts_from_func_inst(fi_msg); + + f_msg = usb_get_function(fi_msg); + if (IS_ERR(f_msg)) + return PTR_ERR(f_msg); + + ret = fsg_common_run_thread(opts->common); + if (ret) + goto put_func; + + ret = usb_add_function(c, f_msg); + if (ret) + goto put_func; - retp = fsg_common_init(&common, c->cdev, &config); - if (IS_ERR(retp)) - return PTR_ERR(retp); + return 0; - ret = fsg_bind_config(c->cdev, c, &common); - fsg_common_put(&common); +put_func: + usb_put_function(f_msg); return ret; } @@ -152,23 +172,79 @@ static struct usb_configuration msg_config_driver = { static int __init msg_bind(struct usb_composite_dev *cdev) { + static const struct fsg_operations ops = { + .thread_exits = msg_thread_exits, + }; + struct fsg_opts *opts; + struct fsg_config config; int status; + fi_msg = usb_get_function_instance("mass_storage"); + if (IS_ERR(fi_msg)) + return PTR_ERR(fi_msg); + + fsg_config_from_params(&config, &mod_data, fsg_num_buffers); + opts = fsg_opts_from_func_inst(fi_msg); + + opts->no_configfs = true; + status = fsg_common_set_num_buffers(opts->common, fsg_num_buffers); + if (status) + goto fail; + + status = fsg_common_set_nluns(opts->common, config.nluns); + if (status) + goto fail_set_nluns; + + fsg_common_set_ops(opts->common, &ops); + + status = fsg_common_set_cdev(opts->common, cdev, config.can_stall); + if (status) + goto fail_set_cdev; + + fsg_common_set_sysfs(opts->common, true); + status = fsg_common_create_luns(opts->common, &config); + if (status) + goto fail_set_cdev; + + fsg_common_set_inquiry_string(opts->common, config.vendor_name, + config.product_name); + status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) - return status; + goto fail_string_ids; msg_device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; status = usb_add_config(cdev, &msg_config_driver, msg_do_config); if (status < 0) - return status; + goto fail_string_ids; + usb_composite_overwrite_options(cdev, &coverwrite); dev_info(&cdev->gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); set_bit(0, &msg_registered); return 0; + +fail_string_ids: + fsg_common_remove_luns(opts->common); +fail_set_cdev: + fsg_common_free_luns(opts->common); +fail_set_nluns: + fsg_common_free_buffers(opts->common); +fail: + usb_put_function_instance(fi_msg); + return status; } +static int msg_unbind(struct usb_composite_dev *cdev) +{ + if (!IS_ERR(f_msg)) + usb_put_function(f_msg); + + if (!IS_ERR(fi_msg)) + usb_put_function_instance(fi_msg); + + return 0; +} /****************************** Some noise ******************************/ @@ -179,6 +255,7 @@ static __refdata struct usb_composite_driver msg_driver = { .needs_serial = 1, .strings = dev_strings, .bind = msg_bind, + .unbind = msg_unbind, }; MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index 23393254a8a3..4fdaa54a2a2a 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -15,6 +15,7 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/netdevice.h> #include "u_serial.h" #if defined USB_ETH_RNDIS @@ -32,22 +33,11 @@ MODULE_AUTHOR("Michal Nazarewicz"); MODULE_LICENSE("GPL"); -/***************************** All the files... *****************************/ +#include "f_mass_storage.h" -/* - * 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 "f_mass_storage.c" - -#define USBF_ECM_INCLUDED -#include "f_ecm.c" +#include "u_ecm.h" #ifdef USB_ETH_RNDIS -# define USB_FRNDIS_INCLUDED -# include "f_rndis.c" +# include "u_rndis.h" # include "rndis.h" #endif #include "u_ether.h" @@ -132,22 +122,36 @@ static struct usb_gadget_strings *dev_strings[] = { /****************************** Configurations ******************************/ static struct fsg_module_parameters fsg_mod_data = { .stall = 1 }; -FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + +static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS; -static struct fsg_common fsg_common; +#else + +/* + * Number of buffers we will use. + * 2 is usually enough for good buffering pipeline + */ +#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS -static u8 host_mac[ETH_ALEN]; +#endif /* CONFIG_USB_DEBUG */ + +FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); static struct usb_function_instance *fi_acm; -static struct eth_dev *the_dev; +static struct usb_function_instance *fi_msg; /********** RNDIS **********/ #ifdef USB_ETH_RNDIS +static struct usb_function_instance *fi_rndis; static struct usb_function *f_acm_rndis; +static struct usb_function *f_rndis; +static struct usb_function *f_msg_rndis; static __init int rndis_do_config(struct usb_configuration *c) { + struct fsg_opts *fsg_opts; int ret; if (gadget_is_otg(c->cdev->gadget)) { @@ -155,27 +159,50 @@ static __init int rndis_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - ret = rndis_bind_config(c, host_mac, the_dev); + f_rndis = usb_get_function(fi_rndis); + if (IS_ERR(f_rndis)) + return PTR_ERR(f_rndis); + + ret = usb_add_function(c, f_rndis); if (ret < 0) - return ret; + goto err_func_rndis; f_acm_rndis = usb_get_function(fi_acm); - if (IS_ERR(f_acm_rndis)) - return PTR_ERR(f_acm_rndis); + if (IS_ERR(f_acm_rndis)) { + ret = PTR_ERR(f_acm_rndis); + goto err_func_acm; + } ret = usb_add_function(c, f_acm_rndis); if (ret) goto err_conf; - ret = fsg_bind_config(c->cdev, c, &fsg_common); - if (ret < 0) + f_msg_rndis = usb_get_function(fi_msg); + if (IS_ERR(f_msg_rndis)) { + ret = PTR_ERR(f_msg_rndis); goto err_fsg; + } + + fsg_opts = fsg_opts_from_func_inst(fi_msg); + ret = fsg_common_run_thread(fsg_opts->common); + if (ret) + goto err_run; + + ret = usb_add_function(c, f_msg_rndis); + if (ret) + goto err_run; return 0; +err_run: + usb_put_function(f_msg_rndis); err_fsg: usb_remove_function(c, f_acm_rndis); err_conf: usb_put_function(f_acm_rndis); +err_func_acm: + usb_remove_function(c, f_rndis); +err_func_rndis: + usb_put_function(f_rndis); return ret; } @@ -205,10 +232,14 @@ static __ref int rndis_config_register(struct usb_composite_dev *cdev) /********** CDC ECM **********/ #ifdef CONFIG_USB_G_MULTI_CDC +static struct usb_function_instance *fi_ecm; static struct usb_function *f_acm_multi; +static struct usb_function *f_ecm; +static struct usb_function *f_msg_multi; static __init int cdc_do_config(struct usb_configuration *c) { + struct fsg_opts *fsg_opts; int ret; if (gadget_is_otg(c->cdev->gadget)) { @@ -216,28 +247,51 @@ static __init int cdc_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - ret = ecm_bind_config(c, host_mac, the_dev); + f_ecm = usb_get_function(fi_ecm); + if (IS_ERR(f_ecm)) + return PTR_ERR(f_ecm); + + ret = usb_add_function(c, f_ecm); if (ret < 0) - return ret; + goto err_func_ecm; /* implicit port_num is zero */ f_acm_multi = usb_get_function(fi_acm); - if (IS_ERR(f_acm_multi)) - return PTR_ERR(f_acm_multi); + if (IS_ERR(f_acm_multi)) { + ret = PTR_ERR(f_acm_multi); + goto err_func_acm; + } ret = usb_add_function(c, f_acm_multi); if (ret) goto err_conf; - ret = fsg_bind_config(c->cdev, c, &fsg_common); - if (ret < 0) + f_msg_multi = usb_get_function(fi_msg); + if (IS_ERR(f_msg_multi)) { + ret = PTR_ERR(f_msg_multi); goto err_fsg; + } + + fsg_opts = fsg_opts_from_func_inst(fi_msg); + ret = fsg_common_run_thread(fsg_opts->common); + if (ret) + goto err_run; + + ret = usb_add_function(c, f_msg_multi); + if (ret) + goto err_run; return 0; +err_run: + usb_put_function(f_msg_multi); err_fsg: usb_remove_function(c, f_acm_multi); err_conf: usb_put_function(f_acm_multi); +err_func_acm: + usb_remove_function(c, f_ecm); +err_func_ecm: + usb_put_function(f_ecm); return ret; } @@ -270,19 +324,67 @@ static __ref int cdc_config_register(struct usb_composite_dev *cdev) static int __ref multi_bind(struct usb_composite_dev *cdev) { struct usb_gadget *gadget = cdev->gadget; +#ifdef CONFIG_USB_G_MULTI_CDC + struct f_ecm_opts *ecm_opts; +#endif +#ifdef USB_ETH_RNDIS + struct f_rndis_opts *rndis_opts; +#endif + struct fsg_opts *fsg_opts; + struct fsg_config config; int status; if (!can_support_ecm(cdev->gadget)) { dev_err(&gadget->dev, "controller '%s' not usable\n", - gadget->name); + gadget->name); return -EINVAL; } - /* set up network link layer */ - the_dev = gether_setup(cdev->gadget, dev_addr, host_addr, host_mac, - qmult); - if (IS_ERR(the_dev)) - return PTR_ERR(the_dev); +#ifdef CONFIG_USB_G_MULTI_CDC + fi_ecm = usb_get_function_instance("ecm"); + if (IS_ERR(fi_ecm)) + return PTR_ERR(fi_ecm); + + ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst); + + gether_set_qmult(ecm_opts->net, qmult); + if (!gether_set_host_addr(ecm_opts->net, host_addr)) + pr_info("using host ethernet address: %s", host_addr); + if (!gether_set_dev_addr(ecm_opts->net, dev_addr)) + pr_info("using self ethernet address: %s", dev_addr); +#endif + +#ifdef USB_ETH_RNDIS + fi_rndis = usb_get_function_instance("rndis"); + if (IS_ERR(fi_rndis)) { + status = PTR_ERR(fi_rndis); + goto fail; + } + + rndis_opts = container_of(fi_rndis, struct f_rndis_opts, func_inst); + + gether_set_qmult(rndis_opts->net, qmult); + if (!gether_set_host_addr(rndis_opts->net, host_addr)) + pr_info("using host ethernet address: %s", host_addr); + if (!gether_set_dev_addr(rndis_opts->net, dev_addr)) + pr_info("using self ethernet address: %s", dev_addr); +#endif + +#if (defined CONFIG_USB_G_MULTI_CDC && defined USB_ETH_RNDIS) + /* + * If both ecm and rndis are selected then: + * 1) rndis borrows the net interface from ecm + * 2) since the interface is shared it must not be bound + * twice - in ecm's _and_ rndis' binds, so do it here. + */ + gether_set_gadget(ecm_opts->net, cdev->gadget); + status = gether_register_netdev(ecm_opts->net); + if (status) + goto fail0; + + rndis_borrow_net(fi_rndis, ecm_opts->net); + ecm_opts->bound = true; +#endif /* set up serial link layer */ fi_acm = usb_get_function_instance("acm"); @@ -292,57 +394,102 @@ static int __ref multi_bind(struct usb_composite_dev *cdev) } /* set up mass storage function */ - { - void *retp; - retp = fsg_common_from_params(&fsg_common, cdev, &fsg_mod_data); - if (IS_ERR(retp)) { - status = PTR_ERR(retp); - goto fail1; - } + fi_msg = usb_get_function_instance("mass_storage"); + if (IS_ERR(fi_msg)) { + status = PTR_ERR(fi_msg); + goto fail1; } + fsg_config_from_params(&config, &fsg_mod_data, fsg_num_buffers); + fsg_opts = fsg_opts_from_func_inst(fi_msg); + + fsg_opts->no_configfs = true; + status = fsg_common_set_num_buffers(fsg_opts->common, fsg_num_buffers); + if (status) + goto fail2; + + status = fsg_common_set_nluns(fsg_opts->common, config.nluns); + if (status) + goto fail_set_nluns; + + status = fsg_common_set_cdev(fsg_opts->common, cdev, config.can_stall); + if (status) + goto fail_set_cdev; + + fsg_common_set_sysfs(fsg_opts->common, true); + status = fsg_common_create_luns(fsg_opts->common, &config); + if (status) + goto fail_set_cdev; + + fsg_common_set_inquiry_string(fsg_opts->common, config.vendor_name, + config.product_name); /* allocate string IDs */ status = usb_string_ids_tab(cdev, strings_dev); if (unlikely(status < 0)) - goto fail2; + goto fail_string_ids; device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; /* register configurations */ status = rndis_config_register(cdev); if (unlikely(status < 0)) - goto fail2; + goto fail_string_ids; status = cdc_config_register(cdev); if (unlikely(status < 0)) - goto fail2; + goto fail_string_ids; usb_composite_overwrite_options(cdev, &coverwrite); /* we're done */ dev_info(&gadget->dev, DRIVER_DESC "\n"); - fsg_common_put(&fsg_common); return 0; /* error recovery */ +fail_string_ids: + fsg_common_remove_luns(fsg_opts->common); +fail_set_cdev: + fsg_common_free_luns(fsg_opts->common); +fail_set_nluns: + fsg_common_free_buffers(fsg_opts->common); fail2: - fsg_common_put(&fsg_common); + usb_put_function_instance(fi_msg); fail1: usb_put_function_instance(fi_acm); fail0: - gether_cleanup(the_dev); +#ifdef USB_ETH_RNDIS + usb_put_function_instance(fi_rndis); +fail: +#endif +#ifdef CONFIG_USB_G_MULTI_CDC + usb_put_function_instance(fi_ecm); +#endif return status; } static int __exit multi_unbind(struct usb_composite_dev *cdev) { #ifdef CONFIG_USB_G_MULTI_CDC + usb_put_function(f_msg_multi); +#endif +#ifdef USB_ETH_RNDIS + usb_put_function(f_msg_rndis); +#endif + usb_put_function_instance(fi_msg); +#ifdef CONFIG_USB_G_MULTI_CDC usb_put_function(f_acm_multi); #endif #ifdef USB_ETH_RNDIS usb_put_function(f_acm_rndis); #endif usb_put_function_instance(fi_acm); - gether_cleanup(the_dev); +#ifdef USB_ETH_RNDIS + usb_put_function(f_rndis); + usb_put_function_instance(fi_rndis); +#endif +#ifdef CONFIG_USB_G_MULTI_CDC + usb_put_function(f_ecm); + usb_put_function_instance(fi_ecm); +#endif return 0; } diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/mv_u3d_core.c index 5b06c989951c..234711eabea1 100644 --- a/drivers/usb/gadget/mv_u3d_core.c +++ b/drivers/usb/gadget/mv_u3d_core.c @@ -310,6 +310,7 @@ static struct mv_u3d_trb *mv_u3d_build_trb_one(struct mv_u3d_req *req, */ trb_hw = dma_pool_alloc(u3d->trb_pool, GFP_ATOMIC, dma); if (!trb_hw) { + kfree(trb); dev_err(u3d->dev, "%s, dma_pool_alloc fail\n", __func__); return NULL; @@ -454,6 +455,7 @@ static int mv_u3d_req_to_trb(struct mv_u3d_req *req) trb_hw = kcalloc(trb_num, sizeof(*trb_hw), GFP_ATOMIC); if (!trb_hw) { + kfree(trb); dev_err(u3d->dev, "%s, trb_hw alloc fail\n", __func__); return -ENOMEM; diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index a8a99e4748d5..9875d9c0823f 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -83,9 +83,12 @@ struct s3c_hsotg_req; * @dir_in: Set to true if this endpoint is of the IN direction, which * means that it is sending data to the Host. * @index: The index for the endpoint registers. + * @mc: Multi Count - number of transactions per microframe + * @interval - Interval for periodic endpoints * @name: The name array passed to the USB core. * @halted: Set if the endpoint has been halted. * @periodic: Set if this is a periodic ep, such as Interrupt + * @isochronous: Set if this is a isochronous ep * @sent_zlp: Set if we've sent a zero-length packet. * @total_data: The total number of data bytes done. * @fifo_size: The size of the FIFO (for periodic IN endpoints) @@ -121,9 +124,12 @@ struct s3c_hsotg_ep { unsigned char dir_in; unsigned char index; + unsigned char mc; + unsigned char interval; unsigned int halted:1; unsigned int periodic:1; + unsigned int isochronous:1; unsigned int sent_zlp:1; char name[10]; @@ -468,6 +474,7 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg, void *data; int can_write; int pkt_round; + int max_transfer; to_write -= (buf_pos - hs_ep->last_load); @@ -535,8 +542,10 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg, can_write *= 4; /* fifo size is in 32bit quantities. */ } - dev_dbg(hsotg->dev, "%s: GNPTXSTS=%08x, can=%d, to=%d, mps %d\n", - __func__, gnptxsts, can_write, to_write, hs_ep->ep.maxpacket); + max_transfer = hs_ep->ep.maxpacket * hs_ep->mc; + + dev_dbg(hsotg->dev, "%s: GNPTXSTS=%08x, can=%d, to=%d, max_transfer %d\n", + __func__, gnptxsts, can_write, to_write, max_transfer); /* * limit to 512 bytes of data, it seems at least on the non-periodic @@ -551,19 +560,21 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg, * the transfer to return that it did not run out of fifo space * doing it. */ - if (to_write > hs_ep->ep.maxpacket) { - to_write = hs_ep->ep.maxpacket; + if (to_write > max_transfer) { + to_write = max_transfer; - s3c_hsotg_en_gsint(hsotg, - periodic ? GINTSTS_PTxFEmp : - GINTSTS_NPTxFEmp); + /* it's needed only when we do not use dedicated fifos */ + if (!hsotg->dedicated_fifos) + s3c_hsotg_en_gsint(hsotg, + periodic ? GINTSTS_PTxFEmp : + GINTSTS_NPTxFEmp); } /* see if we can write data */ if (to_write > can_write) { to_write = can_write; - pkt_round = to_write % hs_ep->ep.maxpacket; + pkt_round = to_write % max_transfer; /* * Round the write down to an @@ -581,9 +592,11 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg, * is more room left. */ - s3c_hsotg_en_gsint(hsotg, - periodic ? GINTSTS_PTxFEmp : - GINTSTS_NPTxFEmp); + /* it's needed only when we do not use dedicated fifos */ + if (!hsotg->dedicated_fifos) + s3c_hsotg_en_gsint(hsotg, + periodic ? GINTSTS_PTxFEmp : + GINTSTS_NPTxFEmp); } dev_dbg(hsotg->dev, "write %d/%d, can_write %d, done %d\n", @@ -727,8 +740,16 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg, else packets = 1; /* send one packet if length is zero. */ + if (hs_ep->isochronous && length > (hs_ep->mc * hs_ep->ep.maxpacket)) { + dev_err(hsotg->dev, "req length > maxpacket*mc\n"); + return; + } + if (dir_in && index != 0) - epsize = DxEPTSIZ_MC(1); + if (hs_ep->isochronous) + epsize = DxEPTSIZ_MC(packets); + else + epsize = DxEPTSIZ_MC(1); else epsize = 0; @@ -820,6 +841,9 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg, dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", __func__, readl(hsotg->regs + epctrl_reg)); + + /* enable ep interrupts */ + s3c_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 1); } /** @@ -1091,6 +1115,7 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg, bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE); struct s3c_hsotg_ep *ep; int ret; + bool halted; dev_dbg(hsotg->dev, "%s: %s_FEATURE\n", __func__, set ? "SET" : "CLEAR"); @@ -1105,6 +1130,8 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg, switch (le16_to_cpu(ctrl->wValue)) { case USB_ENDPOINT_HALT: + halted = ep->halted; + s3c_hsotg_ep_sethalt(&ep->ep, set); ret = s3c_hsotg_send_reply(hsotg, ep0, NULL, 0); @@ -1114,7 +1141,12 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg, return ret; } - if (!set) { + /* + * we have to complete all requests for ep if it was + * halted, and the halt was cleared by CLEAR_FEATURE + */ + + if (!set && halted) { /* * If we have request in progress, * then complete it @@ -1147,6 +1179,8 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg, return 1; } +static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg); + /** * s3c_hsotg_process_control - process a control request * @hsotg: The device state @@ -1246,11 +1280,15 @@ static void s3c_hsotg_process_control(struct s3c_hsotg *hsotg, * don't believe we need to anything more to get the EP * to reply with a STALL packet */ + + /* + * complete won't be called, so we enqueue + * setup request here + */ + s3c_hsotg_enqueue_setup(hsotg); } } -static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg); - /** * s3c_hsotg_complete_setup - completion of a setup transfer * @ep: The endpoint the request was on. @@ -1698,6 +1736,7 @@ static void s3c_hsotg_set_ep_maxpacket(struct s3c_hsotg *hsotg, struct s3c_hsotg_ep *hs_ep = &hsotg->eps[ep]; void __iomem *regs = hsotg->regs; u32 mpsval; + u32 mcval; u32 reg; if (ep == 0) { @@ -1705,15 +1744,19 @@ static void s3c_hsotg_set_ep_maxpacket(struct s3c_hsotg *hsotg, mpsval = s3c_hsotg_ep0_mps(mps); if (mpsval > 3) goto bad_mps; + hs_ep->ep.maxpacket = mps; + hs_ep->mc = 1; } else { - if (mps >= DxEPCTL_MPS_LIMIT+1) + mpsval = mps & DxEPCTL_MPS_MASK; + if (mpsval > 1024) goto bad_mps; - - mpsval = mps; + mcval = ((mps >> 11) & 0x3) + 1; + hs_ep->mc = mcval; + if (mcval > 3) + goto bad_mps; + hs_ep->ep.maxpacket = mpsval; } - hs_ep->ep.maxpacket = mps; - /* * update both the in and out endpoint controldir_ registers, even * if one of the directions may not be in use. @@ -1782,8 +1825,16 @@ static int s3c_hsotg_trytx(struct s3c_hsotg *hsotg, { struct s3c_hsotg_req *hs_req = hs_ep->req; - if (!hs_ep->dir_in || !hs_req) + if (!hs_ep->dir_in || !hs_req) { + /** + * if request is not enqueued, we disable interrupts + * for endpoints, excepting ep0 + */ + if (hs_ep->index != 0) + s3c_hsotg_ctrl_epint(hsotg, hs_ep->index, + hs_ep->dir_in, 0); return 0; + } if (hs_req->req.actual < hs_req->req.length) { dev_dbg(hsotg->dev, "trying to write more for ep%d\n", @@ -1887,8 +1938,10 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx); u32 epsiz_reg = dir_in ? DIEPTSIZ(idx) : DOEPTSIZ(idx); u32 ints; + u32 ctrl; ints = readl(hsotg->regs + epint_reg); + ctrl = readl(hsotg->regs + epctl_reg); /* Clear endpoint interrupts */ writel(ints, hsotg->regs + epint_reg); @@ -1897,6 +1950,14 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, __func__, idx, dir_in ? "in" : "out", ints); if (ints & DxEPINT_XferCompl) { + if (hs_ep->isochronous && hs_ep->interval == 1) { + if (ctrl & DxEPCTL_EOFrNum) + ctrl |= DxEPCTL_SetEvenFr; + else + ctrl |= DxEPCTL_SetOddFr; + writel(ctrl, hsotg->regs + epctl_reg); + } + dev_dbg(hsotg->dev, "%s: XferCompl: DxEPCTL=0x%08x, DxEPTSIZ=%08x\n", __func__, readl(hsotg->regs + epctl_reg), @@ -1963,7 +2024,7 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, if (ints & DxEPINT_Back2BackSetup) dev_dbg(hsotg->dev, "%s: B2BSetup/INEPNakEff\n", __func__); - if (dir_in) { + if (dir_in && !hs_ep->isochronous) { /* not sure if this is important, but we'll clear it anyway */ if (ints & DIEPMSK_INTknTXFEmpMsk) { dev_dbg(hsotg->dev, "%s: ep%d: INTknTXFEmpMsk\n", @@ -2092,12 +2153,14 @@ static void kill_all_requests(struct s3c_hsotg *hsotg, } #define call_gadget(_hs, _entry) \ +do { \ if ((_hs)->gadget.speed != USB_SPEED_UNKNOWN && \ (_hs)->driver && (_hs)->driver->_entry) { \ spin_unlock(&_hs->lock); \ (_hs)->driver->_entry(&(_hs)->gadget); \ spin_lock(&_hs->lock); \ - } + } \ +} while (0) /** * s3c_hsotg_disconnect - disconnect service @@ -2241,15 +2304,19 @@ static void s3c_hsotg_core_init(struct s3c_hsotg *hsotg) GAHBCFG_HBstLen_Incr4, hsotg->regs + GAHBCFG); else - writel(GAHBCFG_GlblIntrEn, hsotg->regs + GAHBCFG); + writel(((hsotg->dedicated_fifos) ? (GAHBCFG_NPTxFEmpLvl | + GAHBCFG_PTxFEmpLvl) : 0) | + GAHBCFG_GlblIntrEn, + hsotg->regs + GAHBCFG); /* - * Enabling INTknTXFEmpMsk here seems to be a big mistake, we end - * up being flooded with interrupts if the host is polling the - * endpoint to try and read data. + * If INTknTXFEmpMsk is enabled, it's important to disable ep interrupts + * when we have no data to transfer. Otherwise we get being flooded by + * interrupts. */ - writel(((hsotg->dedicated_fifos) ? DIEPMSK_TxFIFOEmpty : 0) | + writel(((hsotg->dedicated_fifos) ? DIEPMSK_TxFIFOEmpty | + DIEPMSK_INTknTXFEmpMsk : 0) | DIEPMSK_EPDisbldMsk | DIEPMSK_XferComplMsk | DIEPMSK_TimeOUTMsk | DIEPMSK_AHBErrMsk | DIEPMSK_INTknEPMisMsk, @@ -2378,10 +2445,14 @@ irq_retry: if (gintsts & (GINTSTS_OEPInt | GINTSTS_IEPInt)) { u32 daint = readl(hsotg->regs + DAINT); - u32 daint_out = daint >> DAINT_OutEP_SHIFT; - u32 daint_in = daint & ~(daint_out << DAINT_OutEP_SHIFT); + u32 daintmsk = readl(hsotg->regs + DAINTMSK); + u32 daint_out, daint_in; int ep; + daint &= daintmsk; + daint_out = daint >> DAINT_OutEP_SHIFT; + daint_in = daint & ~(daint_out << DAINT_OutEP_SHIFT); + dev_dbg(hsotg->dev, "%s: daint=%08x\n", __func__, daint); for (ep = 0; ep < 15 && daint_out; ep++, daint_out >>= 1) { @@ -2577,16 +2648,25 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, epctrl |= DxEPCTL_SNAK; /* update the endpoint state */ - hs_ep->ep.maxpacket = mps; + s3c_hsotg_set_ep_maxpacket(hsotg, hs_ep->index, mps); /* default, set to non-periodic */ + hs_ep->isochronous = 0; hs_ep->periodic = 0; + hs_ep->halted = 0; + hs_ep->interval = desc->bInterval; + + if (hs_ep->interval > 1 && hs_ep->mc > 1) + dev_err(hsotg->dev, "MC > 1 when interval is not 1\n"); switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { case USB_ENDPOINT_XFER_ISOC: - dev_err(hsotg->dev, "no current ISOC support\n"); - ret = -EINVAL; - goto out; + epctrl |= DxEPCTL_EPType_Iso; + epctrl |= DxEPCTL_SetEvenFr; + hs_ep->isochronous = 1; + if (dir_in) + hs_ep->periodic = 1; + break; case USB_ENDPOINT_XFER_BULK: epctrl |= DxEPCTL_EPType_Bulk; @@ -2634,7 +2714,6 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, /* enable the endpoint interrupt */ s3c_hsotg_ctrl_epint(hsotg, index, dir_in, 1); -out: spin_unlock_irqrestore(&hsotg->lock, flags); return ret; } @@ -2776,6 +2855,8 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value) writel(epctl, hs->regs + epreg); + hs_ep->halted = value; + return 0; } @@ -2903,7 +2984,7 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget, int ret; if (!hsotg) { - printk(KERN_ERR "%s: called with no device\n", __func__); + pr_err("%s: called with no device\n", __func__); return -ENODEV; } @@ -3066,7 +3147,7 @@ static void s3c_hsotg_initep(struct s3c_hsotg *hsotg, hs_ep->parent = hsotg; hs_ep->ep.name = hs_ep->name; - hs_ep->ep.maxpacket = epnum ? 512 : EP0_MPS_LIMIT; + hs_ep->ep.maxpacket = epnum ? 1024 : EP0_MPS_LIMIT; hs_ep->ep.ops = &s3c_hsotg_ep_ops; /* @@ -3200,7 +3281,7 @@ static int state_show(struct seq_file *seq, void *v) readl(regs + GNPTXSTS), readl(regs + GRXSTSR)); - seq_printf(seq, "\nEndpoint status:\n"); + seq_puts(seq, "\nEndpoint status:\n"); for (idx = 0; idx < 15; idx++) { u32 in, out; @@ -3217,7 +3298,7 @@ static int state_show(struct seq_file *seq, void *v) seq_printf(seq, ", DIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x", in, out); - seq_printf(seq, "\n"); + seq_puts(seq, "\n"); } return 0; @@ -3251,7 +3332,7 @@ static int fifo_show(struct seq_file *seq, void *v) u32 val; int idx; - seq_printf(seq, "Non-periodic FIFOs:\n"); + seq_puts(seq, "Non-periodic FIFOs:\n"); seq_printf(seq, "RXFIFO: Size %d\n", readl(regs + GRXFSIZ)); val = readl(regs + GNPTXFSIZ); @@ -3259,7 +3340,7 @@ static int fifo_show(struct seq_file *seq, void *v) val >> GNPTXFSIZ_NPTxFDep_SHIFT, val & GNPTXFSIZ_NPTxFStAddr_MASK); - seq_printf(seq, "\nPeriodic TXFIFOs:\n"); + seq_puts(seq, "\nPeriodic TXFIFOs:\n"); for (idx = 1; idx <= 15; idx++) { val = readl(regs + DPTXFSIZn(idx)); @@ -3330,7 +3411,7 @@ static int ep_show(struct seq_file *seq, void *v) readl(regs + DIEPTSIZ(index)), readl(regs + DOEPTSIZ(index))); - seq_printf(seq, "\n"); + seq_puts(seq, "\n"); seq_printf(seq, "mps %d\n", ep->ep.maxpacket); seq_printf(seq, "total_data=%ld\n", ep->total_data); @@ -3341,7 +3422,7 @@ static int ep_show(struct seq_file *seq, void *v) list_for_each_entry(req, &ep->queue, queue) { if (--show_limit < 0) { - seq_printf(seq, "not showing more requests...\n"); + seq_puts(seq, "not showing more requests...\n"); break; } diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index 08a1a3210a21..ec20a1f50c2d 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -23,242 +23,17 @@ * The valid range of num_buffers is: num >= 2 && num <= 4. */ +#include <linux/module.h> +#include <linux/blkdev.h> +#include <linux/file.h> +#include <linux/fs.h> +#include <linux/usb/composite.h> -#include <linux/usb/storage.h> -#include <scsi/scsi.h> -#include <asm/unaligned.h> - - -/* - * Thanks to NetChip Technologies for donating this product ID. - * - * DO NOT REUSE THESE IDs with any other driver!! Ever!! - * Instead: allocate your own, using normal USB-IF procedures. - */ -#define FSG_VENDOR_ID 0x0525 /* NetChip */ -#define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */ - - -/*-------------------------------------------------------------------------*/ - - -#ifndef DEBUG -#undef VERBOSE_DEBUG -#undef DUMP_MSGS -#endif /* !DEBUG */ - -#ifdef VERBOSE_DEBUG -#define VLDBG LDBG -#else -#define VLDBG(lun, fmt, args...) do { } while (0) -#endif /* VERBOSE_DEBUG */ - -#define LDBG(lun, fmt, args...) dev_dbg (&(lun)->dev, fmt, ## args) -#define LERROR(lun, fmt, args...) dev_err (&(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) - - -#ifdef DUMP_MSGS - -# define dump_msg(fsg, /* const char * */ label, \ - /* const u8 * */ buf, /* unsigned */ length) do { \ - if (length < 512) { \ - DBG(fsg, "%s, length %u:\n", label, length); \ - print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, \ - 16, 1, buf, length, 0); \ - } \ -} while (0) - -# define dump_cdb(fsg) do { } while (0) - -#else - -# define dump_msg(fsg, /* const char * */ label, \ - /* const u8 * */ buf, /* unsigned */ length) do { } while (0) - -# ifdef VERBOSE_DEBUG - -# define dump_cdb(fsg) \ - print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE, \ - 16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0) \ - -# else - -# define dump_cdb(fsg) do { } while (0) - -# endif /* VERBOSE_DEBUG */ - -#endif /* DUMP_MSGS */ - -/*-------------------------------------------------------------------------*/ - -/* Length of a SCSI Command Data Block */ -#define MAX_COMMAND_SIZE 16 - -/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */ -#define SS_NO_SENSE 0 -#define SS_COMMUNICATION_FAILURE 0x040800 -#define SS_INVALID_COMMAND 0x052000 -#define SS_INVALID_FIELD_IN_CDB 0x052400 -#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100 -#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500 -#define SS_MEDIUM_NOT_PRESENT 0x023a00 -#define SS_MEDIUM_REMOVAL_PREVENTED 0x055302 -#define SS_NOT_READY_TO_READY_TRANSITION 0x062800 -#define SS_RESET_OCCURRED 0x062900 -#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900 -#define SS_UNRECOVERED_READ_ERROR 0x031100 -#define SS_WRITE_ERROR 0x030c02 -#define SS_WRITE_PROTECTED 0x072700 - -#define SK(x) ((u8) ((x) >> 16)) /* Sense Key byte, etc. */ -#define ASC(x) ((u8) ((x) >> 8)) -#define ASCQ(x) ((u8) (x)) - - -/*-------------------------------------------------------------------------*/ - - -struct fsg_lun { - struct file *filp; - loff_t file_length; - loff_t num_sectors; - - unsigned int initially_ro:1; - unsigned int ro:1; - unsigned int removable:1; - unsigned int cdrom:1; - unsigned int prevent_medium_removal:1; - unsigned int registered:1; - unsigned int info_valid:1; - unsigned int nofua:1; - - u32 sense_data; - u32 sense_data_info; - u32 unit_attention_data; - - unsigned int blkbits; /* Bits of logical block size of bound block device */ - unsigned int blksize; /* logical block size of bound block device */ - struct device dev; -}; - -static inline bool fsg_lun_is_open(struct fsg_lun *curlun) -{ - return curlun->filp != NULL; -} - -static inline struct fsg_lun *fsg_lun_from_dev(struct device *dev) -{ - return container_of(dev, struct fsg_lun, dev); -} - - -/* Big enough to hold our biggest descriptor */ -#define EP0_BUFSIZE 256 -#define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */ - -#ifdef CONFIG_USB_GADGET_DEBUG_FILES - -static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS; -module_param_named(num_buffers, fsg_num_buffers, uint, S_IRUGO); -MODULE_PARM_DESC(num_buffers, "Number of pipeline buffers"); - -#else - -/* - * Number of buffers we will use. - * 2 is usually enough for good buffering pipeline - */ -#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS - -#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ - -/* check if fsg_num_buffers is within a valid range */ -static inline int fsg_num_buffers_validate(void) -{ - if (fsg_num_buffers >= 2 && fsg_num_buffers <= 4) - return 0; - pr_err("fsg_num_buffers %u is out of range (%d to %d)\n", - fsg_num_buffers, 2 ,4); - return -EINVAL; -} - -/* Default size of buffer length. */ -#define FSG_BUFLEN ((u32)16384) - -/* Maximal number of LUNs supported in mass storage function */ -#define FSG_MAX_LUNS 8 - -enum fsg_buffer_state { - BUF_STATE_EMPTY = 0, - BUF_STATE_FULL, - BUF_STATE_BUSY -}; - -struct fsg_buffhd { - void *buf; - enum fsg_buffer_state state; - struct fsg_buffhd *next; - - /* - * The NetChip 2280 is faster, and handles some protocol faults - * better, if we don't submit any short bulk-out read requests. - * So we will record the intended request length here. - */ - unsigned int bulk_out_intended_length; - - struct usb_request *inreq; - int inreq_busy; - struct usb_request *outreq; - int outreq_busy; -}; - -enum fsg_state { - /* This one isn't used anywhere */ - FSG_STATE_COMMAND_PHASE = -10, - FSG_STATE_DATA_PHASE, - FSG_STATE_STATUS_PHASE, - - FSG_STATE_IDLE = 0, - FSG_STATE_ABORT_BULK_OUT, - FSG_STATE_RESET, - FSG_STATE_INTERFACE_CHANGE, - FSG_STATE_CONFIG_CHANGE, - FSG_STATE_DISCONNECT, - FSG_STATE_EXIT, - FSG_STATE_TERMINATED -}; - -enum data_direction { - DATA_DIR_UNKNOWN = 0, - DATA_DIR_FROM_HOST, - DATA_DIR_TO_HOST, - DATA_DIR_NONE -}; - - -/*-------------------------------------------------------------------------*/ - - -static inline u32 get_unaligned_be24(u8 *buf) -{ - return 0xffffff & (u32) get_unaligned_be32(buf - 1); -} - - -/*-------------------------------------------------------------------------*/ - - -enum { - FSG_STRING_INTERFACE -}; - +#include "storage_common.h" /* There is only one interface. */ -static struct usb_interface_descriptor -fsg_intf_desc = { +struct usb_interface_descriptor fsg_intf_desc = { .bLength = sizeof fsg_intf_desc, .bDescriptorType = USB_DT_INTERFACE, @@ -268,14 +43,14 @@ fsg_intf_desc = { .bInterfaceProtocol = USB_PR_BULK, /* Adjusted during fsg_bind() */ .iInterface = FSG_STRING_INTERFACE, }; +EXPORT_SYMBOL(fsg_intf_desc); /* * Three full-speed endpoint descriptors: bulk-in, bulk-out, and * interrupt-in. */ -static struct usb_endpoint_descriptor -fsg_fs_bulk_in_desc = { +struct usb_endpoint_descriptor fsg_fs_bulk_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -283,9 +58,9 @@ fsg_fs_bulk_in_desc = { .bmAttributes = USB_ENDPOINT_XFER_BULK, /* wMaxPacketSize set by autoconfiguration */ }; +EXPORT_SYMBOL(fsg_fs_bulk_in_desc); -static struct usb_endpoint_descriptor -fsg_fs_bulk_out_desc = { +struct usb_endpoint_descriptor fsg_fs_bulk_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -293,13 +68,15 @@ fsg_fs_bulk_out_desc = { .bmAttributes = USB_ENDPOINT_XFER_BULK, /* wMaxPacketSize set by autoconfiguration */ }; +EXPORT_SYMBOL(fsg_fs_bulk_out_desc); -static struct usb_descriptor_header *fsg_fs_function[] = { +struct usb_descriptor_header *fsg_fs_function[] = { (struct usb_descriptor_header *) &fsg_intf_desc, (struct usb_descriptor_header *) &fsg_fs_bulk_in_desc, (struct usb_descriptor_header *) &fsg_fs_bulk_out_desc, NULL, }; +EXPORT_SYMBOL(fsg_fs_function); /* @@ -310,8 +87,7 @@ static struct usb_descriptor_header *fsg_fs_function[] = { * and a "device qualifier" ... plus more construction options * for the configuration descriptor. */ -static struct usb_endpoint_descriptor -fsg_hs_bulk_in_desc = { +struct usb_endpoint_descriptor fsg_hs_bulk_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -319,9 +95,9 @@ fsg_hs_bulk_in_desc = { .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(512), }; +EXPORT_SYMBOL(fsg_hs_bulk_in_desc); -static struct usb_endpoint_descriptor -fsg_hs_bulk_out_desc = { +struct usb_endpoint_descriptor fsg_hs_bulk_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -330,17 +106,18 @@ fsg_hs_bulk_out_desc = { .wMaxPacketSize = cpu_to_le16(512), .bInterval = 1, /* NAK every 1 uframe */ }; +EXPORT_SYMBOL(fsg_hs_bulk_out_desc); -static struct usb_descriptor_header *fsg_hs_function[] = { +struct usb_descriptor_header *fsg_hs_function[] = { (struct usb_descriptor_header *) &fsg_intf_desc, (struct usb_descriptor_header *) &fsg_hs_bulk_in_desc, (struct usb_descriptor_header *) &fsg_hs_bulk_out_desc, NULL, }; +EXPORT_SYMBOL(fsg_hs_function); -static struct usb_endpoint_descriptor -fsg_ss_bulk_in_desc = { +struct usb_endpoint_descriptor fsg_ss_bulk_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -348,16 +125,17 @@ fsg_ss_bulk_in_desc = { .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(1024), }; +EXPORT_SYMBOL(fsg_ss_bulk_in_desc); -static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = { +struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = { .bLength = sizeof(fsg_ss_bulk_in_comp_desc), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, /*.bMaxBurst = DYNAMIC, */ }; +EXPORT_SYMBOL(fsg_ss_bulk_in_comp_desc); -static struct usb_endpoint_descriptor -fsg_ss_bulk_out_desc = { +struct usb_endpoint_descriptor fsg_ss_bulk_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -365,15 +143,17 @@ fsg_ss_bulk_out_desc = { .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(1024), }; +EXPORT_SYMBOL(fsg_ss_bulk_out_desc); -static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = { +struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = { .bLength = sizeof(fsg_ss_bulk_in_comp_desc), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, /*.bMaxBurst = DYNAMIC, */ }; +EXPORT_SYMBOL(fsg_ss_bulk_out_comp_desc); -static struct usb_descriptor_header *fsg_ss_function[] = { +struct usb_descriptor_header *fsg_ss_function[] = { (struct usb_descriptor_header *) &fsg_intf_desc, (struct usb_descriptor_header *) &fsg_ss_bulk_in_desc, (struct usb_descriptor_header *) &fsg_ss_bulk_in_comp_desc, @@ -381,17 +161,7 @@ static struct usb_descriptor_header *fsg_ss_function[] = { (struct usb_descriptor_header *) &fsg_ss_bulk_out_comp_desc, NULL, }; - -/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */ -static struct usb_string fsg_strings[] = { - {FSG_STRING_INTERFACE, fsg_string_interface}, - {} -}; - -static struct usb_gadget_strings fsg_stringtab = { - .language = 0x0409, /* en-us */ - .strings = fsg_strings, -}; +EXPORT_SYMBOL(fsg_ss_function); /*-------------------------------------------------------------------------*/ @@ -401,7 +171,7 @@ static struct usb_gadget_strings fsg_stringtab = { * the caller must own fsg->filesem for writing. */ -static void fsg_lun_close(struct fsg_lun *curlun) +void fsg_lun_close(struct fsg_lun *curlun) { if (curlun->filp) { LDBG(curlun, "close backing file\n"); @@ -409,9 +179,9 @@ static void fsg_lun_close(struct fsg_lun *curlun) curlun->filp = NULL; } } +EXPORT_SYMBOL(fsg_lun_close); - -static int fsg_lun_open(struct fsg_lun *curlun, const char *filename) +int fsg_lun_open(struct fsg_lun *curlun, const char *filename) { int ro; struct file *filp = NULL; @@ -508,6 +278,7 @@ out: fput(filp); return rc; } +EXPORT_SYMBOL(fsg_lun_open); /*-------------------------------------------------------------------------*/ @@ -516,7 +287,7 @@ out: * Sync the file data, don't bother with the metadata. * This code was copied from fs/buffer.c:sys_fdatasync(). */ -static int fsg_lun_fsync_sub(struct fsg_lun *curlun) +int fsg_lun_fsync_sub(struct fsg_lun *curlun) { struct file *filp = curlun->filp; @@ -524,8 +295,9 @@ static int fsg_lun_fsync_sub(struct fsg_lun *curlun) return 0; return vfs_fsync(filp, 1); } +EXPORT_SYMBOL(fsg_lun_fsync_sub); -static void store_cdrom_address(u8 *dest, int msf, u32 addr) +void store_cdrom_address(u8 *dest, int msf, u32 addr) { if (msf) { /* Convert to Minutes-Seconds-Frames */ @@ -542,34 +314,28 @@ static void store_cdrom_address(u8 *dest, int msf, u32 addr) put_unaligned_be32(addr, dest); } } - +EXPORT_SYMBOL(store_cdrom_address); /*-------------------------------------------------------------------------*/ -static ssize_t ro_show(struct device *dev, struct device_attribute *attr, - char *buf) +ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf) { - struct fsg_lun *curlun = fsg_lun_from_dev(dev); - return sprintf(buf, "%d\n", fsg_lun_is_open(curlun) ? curlun->ro : curlun->initially_ro); } +EXPORT_SYMBOL(fsg_show_ro); -static ssize_t nofua_show(struct device *dev, struct device_attribute *attr, - char *buf) +ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf) { - struct fsg_lun *curlun = fsg_lun_from_dev(dev); - return sprintf(buf, "%u\n", curlun->nofua); } +EXPORT_SYMBOL(fsg_show_nofua); -static ssize_t file_show(struct device *dev, struct device_attribute *attr, - char *buf) +ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem, + char *buf) { - struct fsg_lun *curlun = fsg_lun_from_dev(dev); - struct rw_semaphore *filesem = dev_get_drvdata(dev); char *p; ssize_t rc; @@ -591,17 +357,44 @@ static ssize_t file_show(struct device *dev, struct device_attribute *attr, up_read(filesem); return rc; } +EXPORT_SYMBOL(fsg_show_file); +ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf) +{ + return sprintf(buf, "%u\n", curlun->cdrom); +} +EXPORT_SYMBOL(fsg_show_cdrom); -static ssize_t ro_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf) +{ + return sprintf(buf, "%u\n", curlun->removable); +} +EXPORT_SYMBOL(fsg_show_removable); + +/* + * The caller must hold fsg->filesem for reading when calling this function. + */ +static ssize_t _fsg_store_ro(struct fsg_lun *curlun, bool ro) +{ + if (fsg_lun_is_open(curlun)) { + LDBG(curlun, "read-only status change prevented\n"); + return -EBUSY; + } + + curlun->ro = ro; + curlun->initially_ro = ro; + LDBG(curlun, "read-only status set to %d\n", curlun->ro); + + return 0; +} + +ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem, + const char *buf, size_t count) { ssize_t rc; - struct fsg_lun *curlun = fsg_lun_from_dev(dev); - struct rw_semaphore *filesem = dev_get_drvdata(dev); - unsigned ro; + bool ro; - rc = kstrtouint(buf, 2, &ro); + rc = strtobool(buf, &ro); if (rc) return rc; @@ -610,27 +403,21 @@ static ssize_t ro_store(struct device *dev, struct device_attribute *attr, * backing file is closed. */ down_read(filesem); - if (fsg_lun_is_open(curlun)) { - LDBG(curlun, "read-only status change prevented\n"); - rc = -EBUSY; - } else { - curlun->ro = ro; - curlun->initially_ro = ro; - LDBG(curlun, "read-only status set to %d\n", curlun->ro); + rc = _fsg_store_ro(curlun, ro); + if (!rc) rc = count; - } up_read(filesem); + return rc; } +EXPORT_SYMBOL(fsg_store_ro); -static ssize_t nofua_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count) { - struct fsg_lun *curlun = fsg_lun_from_dev(dev); - unsigned nofua; + bool nofua; int ret; - ret = kstrtouint(buf, 2, &nofua); + ret = strtobool(buf, &nofua); if (ret) return ret; @@ -642,12 +429,11 @@ static ssize_t nofua_store(struct device *dev, struct device_attribute *attr, return count; } +EXPORT_SYMBOL(fsg_store_nofua); -static ssize_t file_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem, + const char *buf, size_t count) { - struct fsg_lun *curlun = fsg_lun_from_dev(dev); - struct rw_semaphore *filesem = dev_get_drvdata(dev); int rc = 0; if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) { @@ -674,3 +460,45 @@ static ssize_t file_store(struct device *dev, struct device_attribute *attr, up_write(filesem); return (rc < 0 ? rc : count); } +EXPORT_SYMBOL(fsg_store_file); + +ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem, + const char *buf, size_t count) +{ + bool cdrom; + int ret; + + ret = strtobool(buf, &cdrom); + if (ret) + return ret; + + down_read(filesem); + ret = cdrom ? _fsg_store_ro(curlun, true) : 0; + + if (!ret) { + curlun->cdrom = cdrom; + ret = count; + } + up_read(filesem); + + return ret; +} +EXPORT_SYMBOL(fsg_store_cdrom); + +ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf, + size_t count) +{ + bool removable; + int ret; + + ret = strtobool(buf, &removable); + if (ret) + return ret; + + curlun->removable = removable; + + return count; +} +EXPORT_SYMBOL(fsg_store_removable); + +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/storage_common.h b/drivers/usb/gadget/storage_common.h new file mode 100644 index 000000000000..c74c2fdbd56e --- /dev/null +++ b/drivers/usb/gadget/storage_common.h @@ -0,0 +1,229 @@ +#ifndef USB_STORAGE_COMMON_H +#define USB_STORAGE_COMMON_H + +#include <linux/device.h> +#include <linux/usb/storage.h> +#include <scsi/scsi.h> +#include <asm/unaligned.h> + +#ifndef DEBUG +#undef VERBOSE_DEBUG +#undef DUMP_MSGS +#endif /* !DEBUG */ + +#ifdef VERBOSE_DEBUG +#define VLDBG LDBG +#else +#define VLDBG(lun, fmt, args...) do { } while (0) +#endif /* VERBOSE_DEBUG */ + +#define _LMSG(func, lun, fmt, args...) \ + do { \ + if ((lun)->name_pfx && *(lun)->name_pfx) \ + func("%s/%s: " fmt, *(lun)->name_pfx, \ + (lun)->name, ## args); \ + else \ + func("%s: " fmt, (lun)->name, ## args); \ + } while (0) + +#define LDBG(lun, fmt, args...) _LMSG(pr_debug, lun, fmt, ## args) +#define LERROR(lun, fmt, args...) _LMSG(pr_err, lun, fmt, ## args) +#define LWARN(lun, fmt, args...) _LMSG(pr_warn, lun, fmt, ## args) +#define LINFO(lun, fmt, args...) _LMSG(pr_info, lun, fmt, ## args) + + +#ifdef DUMP_MSGS + +# define dump_msg(fsg, /* const char * */ label, \ + /* const u8 * */ buf, /* unsigned */ length) \ +do { \ + if (length < 512) { \ + DBG(fsg, "%s, length %u:\n", label, length); \ + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, \ + 16, 1, buf, length, 0); \ + } \ +} while (0) + +# define dump_cdb(fsg) do { } while (0) + +#else + +# define dump_msg(fsg, /* const char * */ label, \ + /* const u8 * */ buf, /* unsigned */ length) do { } while (0) + +# ifdef VERBOSE_DEBUG + +# define dump_cdb(fsg) \ + print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE, \ + 16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0) \ + +# else + +# define dump_cdb(fsg) do { } while (0) + +# endif /* VERBOSE_DEBUG */ + +#endif /* DUMP_MSGS */ + +/* Length of a SCSI Command Data Block */ +#define MAX_COMMAND_SIZE 16 + +/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */ +#define SS_NO_SENSE 0 +#define SS_COMMUNICATION_FAILURE 0x040800 +#define SS_INVALID_COMMAND 0x052000 +#define SS_INVALID_FIELD_IN_CDB 0x052400 +#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100 +#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500 +#define SS_MEDIUM_NOT_PRESENT 0x023a00 +#define SS_MEDIUM_REMOVAL_PREVENTED 0x055302 +#define SS_NOT_READY_TO_READY_TRANSITION 0x062800 +#define SS_RESET_OCCURRED 0x062900 +#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900 +#define SS_UNRECOVERED_READ_ERROR 0x031100 +#define SS_WRITE_ERROR 0x030c02 +#define SS_WRITE_PROTECTED 0x072700 + +#define SK(x) ((u8) ((x) >> 16)) /* Sense Key byte, etc. */ +#define ASC(x) ((u8) ((x) >> 8)) +#define ASCQ(x) ((u8) (x)) + +struct fsg_lun { + struct file *filp; + loff_t file_length; + loff_t num_sectors; + + unsigned int initially_ro:1; + unsigned int ro:1; + unsigned int removable:1; + unsigned int cdrom:1; + unsigned int prevent_medium_removal:1; + unsigned int registered:1; + unsigned int info_valid:1; + unsigned int nofua:1; + + u32 sense_data; + u32 sense_data_info; + u32 unit_attention_data; + + unsigned int blkbits; /* Bits of logical block size + of bound block device */ + unsigned int blksize; /* logical block size of bound block device */ + struct device dev; + const char *name; /* "lun.name" */ + const char **name_pfx; /* "function.name" */ +}; + +static inline bool fsg_lun_is_open(struct fsg_lun *curlun) +{ + return curlun->filp != NULL; +} + +/* Big enough to hold our biggest descriptor */ +#define EP0_BUFSIZE 256 +#define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */ + +/* Default size of buffer length. */ +#define FSG_BUFLEN ((u32)16384) + +/* Maximal number of LUNs supported in mass storage function */ +#define FSG_MAX_LUNS 8 + +enum fsg_buffer_state { + BUF_STATE_EMPTY = 0, + BUF_STATE_FULL, + BUF_STATE_BUSY +}; + +struct fsg_buffhd { + void *buf; + enum fsg_buffer_state state; + struct fsg_buffhd *next; + + /* + * The NetChip 2280 is faster, and handles some protocol faults + * better, if we don't submit any short bulk-out read requests. + * So we will record the intended request length here. + */ + unsigned int bulk_out_intended_length; + + struct usb_request *inreq; + int inreq_busy; + struct usb_request *outreq; + int outreq_busy; +}; + +enum fsg_state { + /* This one isn't used anywhere */ + FSG_STATE_COMMAND_PHASE = -10, + FSG_STATE_DATA_PHASE, + FSG_STATE_STATUS_PHASE, + + FSG_STATE_IDLE = 0, + FSG_STATE_ABORT_BULK_OUT, + FSG_STATE_RESET, + FSG_STATE_INTERFACE_CHANGE, + FSG_STATE_CONFIG_CHANGE, + FSG_STATE_DISCONNECT, + FSG_STATE_EXIT, + FSG_STATE_TERMINATED +}; + +enum data_direction { + DATA_DIR_UNKNOWN = 0, + DATA_DIR_FROM_HOST, + DATA_DIR_TO_HOST, + DATA_DIR_NONE +}; + +static inline u32 get_unaligned_be24(u8 *buf) +{ + return 0xffffff & (u32) get_unaligned_be32(buf - 1); +} + +static inline struct fsg_lun *fsg_lun_from_dev(struct device *dev) +{ + return container_of(dev, struct fsg_lun, dev); +} + +enum { + FSG_STRING_INTERFACE +}; + +extern struct usb_interface_descriptor fsg_intf_desc; + +extern struct usb_endpoint_descriptor fsg_fs_bulk_in_desc; +extern struct usb_endpoint_descriptor fsg_fs_bulk_out_desc; +extern struct usb_descriptor_header *fsg_fs_function[]; + +extern struct usb_endpoint_descriptor fsg_hs_bulk_in_desc; +extern struct usb_endpoint_descriptor fsg_hs_bulk_out_desc; +extern struct usb_descriptor_header *fsg_hs_function[]; + +extern struct usb_endpoint_descriptor fsg_ss_bulk_in_desc; +extern struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc; +extern struct usb_endpoint_descriptor fsg_ss_bulk_out_desc; +extern struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc; +extern struct usb_descriptor_header *fsg_ss_function[]; + +void fsg_lun_close(struct fsg_lun *curlun); +int fsg_lun_open(struct fsg_lun *curlun, const char *filename); +int fsg_lun_fsync_sub(struct fsg_lun *curlun); +void store_cdrom_address(u8 *dest, int msf, u32 addr); +ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf); +ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf); +ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem, + char *buf); +ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf); +ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf); +ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem, + const char *buf, size_t count); +ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count); +ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem, + const char *buf, size_t count); +ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem, + const char *buf, size_t count); +ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf, + size_t count); + +#endif /* USB_STORAGE_COMMON_H */ diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index 59891b1c48fc..27768a7d986a 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -356,7 +356,8 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); return 0; err1: - dev_err(&udc->dev, "failed to start %s: %d\n", + if (ret != -EISNAM) + dev_err(&udc->dev, "failed to start %s: %d\n", udc->driver->function, ret); udc->driver = NULL; udc->dev.driver = NULL; diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index 0deb9d6cde26..0dd07ae1555d 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -95,6 +95,18 @@ unsigned autoresume = DEFAULT_AUTORESUME; module_param(autoresume, uint, S_IRUGO); MODULE_PARM_DESC(autoresume, "zero, or seconds before remote wakeup"); +/* Maximum Autoresume time */ +unsigned max_autoresume; +module_param(max_autoresume, uint, S_IRUGO); +MODULE_PARM_DESC(max_autoresume, "maximum seconds before remote wakeup"); + +/* Interval between two remote wakeups */ +unsigned autoresume_interval_ms; +module_param(autoresume_interval_ms, uint, S_IRUGO); +MODULE_PARM_DESC(autoresume_interval_ms, + "milliseconds to increase successive wakeup delays"); + +static unsigned autoresume_step_ms; /*-------------------------------------------------------------------------*/ static struct usb_device_descriptor device_desc = { @@ -183,8 +195,16 @@ static void zero_suspend(struct usb_composite_dev *cdev) return; if (autoresume) { - mod_timer(&autoresume_timer, jiffies + (HZ * autoresume)); - DBG(cdev, "suspend, wakeup in %d seconds\n", autoresume); + if (max_autoresume && + (autoresume_step_ms > max_autoresume * 1000)) + autoresume_step_ms = autoresume * 1000; + + mod_timer(&autoresume_timer, jiffies + + msecs_to_jiffies(autoresume_step_ms)); + DBG(cdev, "suspend, wakeup in %d milliseconds\n", + autoresume_step_ms); + + autoresume_step_ms += autoresume_interval_ms; } else DBG(cdev, "%s\n", __func__); } @@ -316,6 +336,7 @@ static int __init zero_bind(struct usb_composite_dev *cdev) if (autoresume) { sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + autoresume_step_ms = autoresume * 1000; } /* support OTG systems */ |