diff options
Diffstat (limited to 'drivers/usb/gadget')
28 files changed, 2063 insertions, 814 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 48cddf3cd6b..a91e6422f93 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 386db9daf1d..f1af39603d4 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 4b947bb50f6..7bfa134fe0e 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/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index a9a4346c83a..54a1e2954ce 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -3078,8 +3078,6 @@ static void udc_pci_remove(struct pci_dev *pdev) if (dev->active) pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); - udc_remove(dev); } diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index d4f0f330575..3e7ae707f69 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -354,7 +354,7 @@ static u8 encode_bMaxPower(enum usb_device_speed speed, return DIV_ROUND_UP(val, 8); default: return DIV_ROUND_UP(val, 2); - }; + } } static int config_buf(struct usb_configuration *config, diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 8f0d6141e5e..25885112fa3 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 00000000000..a7b564a913d --- /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/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index b8a2376971a..8f4dae31092 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -544,7 +544,7 @@ static int dummy_enable(struct usb_ep *_ep, default: val = "ctrl"; break; - }; val; }), + } val; }), max, ep->stream_en ? "enabled" : "disabled"); /* at this point real hardware should be NAKing transfers @@ -2271,7 +2271,7 @@ static inline ssize_t show_urb(char *buf, size_t size, struct urb *urb) default: s = "?"; break; - }; s; }), + } s; }), ep, ep ? (usb_pipein(urb->pipe) ? "in" : "out") : "", ({ char *s; \ switch (usb_pipetype(urb->pipe)) { \ @@ -2287,7 +2287,7 @@ static inline ssize_t show_urb(char *buf, size_t size, struct urb *urb) default: \ s = "-iso"; \ break; \ - }; s; }), + } s; }), urb->actual_length, urb->transfer_buffer_length); } diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index 0658908d896..774e8b89cdb 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -373,7 +373,7 @@ static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len) if (req->buf == NULL) req->buf = (void *)0xDEADBABE; - INIT_COMPLETION(ffs->ep0req_completion); + reinit_completion(&ffs->ep0req_completion); ret = usb_ep_queue(ffs->gadget->ep0, req, GFP_ATOMIC); if (unlikely(ret < 0)) @@ -2256,6 +2256,8 @@ static int ffs_func_bind(struct usb_configuration *c, data->raw_descs + ret, (sizeof data->raw_descs) - ret, __ffs_func_bind_do_descs, func); + if (unlikely(ret < 0)) + goto error; } /* diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index a01d7d38c01..a03ba2c8358 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 th |