diff options
Diffstat (limited to 'drivers/usb/gadget/f_mass_storage.c')
-rw-r--r-- | drivers/usb/gadget/f_mass_storage.c | 361 |
1 files changed, 199 insertions, 162 deletions
diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index b4ec76a3e37d..5a0f5708c094 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -323,6 +323,8 @@ MODULE_PARM_DESC(cdrom, "true to emulate cdrom instead of disk"); /* Data shared by all the FSG instances. */ struct fsg_common { + struct usb_gadget *gadget; + /* filesem protects: backing files in use */ struct rw_semaphore filesem; @@ -337,6 +339,10 @@ struct fsg_common { unsigned int lun; struct fsg_lun *luns; struct fsg_lun *curlun; + + unsigned int free_storage_on_release:1; + + struct kref ref; }; @@ -347,9 +353,6 @@ struct fsg_dev { spinlock_t lock; struct usb_gadget *gadget; - /* reference counting: wait until all LUNs are released */ - struct kref ref; - struct usb_ep *ep0; // Handy copy of gadget->ep0 struct usb_request *ep0req; // For control responses unsigned int ep0_req_tag; @@ -2757,7 +2760,7 @@ static int fsg_main_thread(void *fsg_) } -/*-------------------------------------------------------------------------*/ +/*************************** DEVICE ATTRIBUTES ***************************/ /* The write permissions and store_xxx pointers are set in fsg_bind() */ @@ -2765,40 +2768,190 @@ static DEVICE_ATTR(ro, 0444, fsg_show_ro, NULL); static DEVICE_ATTR(file, 0444, fsg_show_file, NULL); -/*-------------------------------------------------------------------------*/ +/****************************** FSG COMMON ******************************/ + +static void fsg_common_release(struct kref *ref); -static void fsg_release(struct fsg_dev *fsg) +static void fsg_lun_release(struct device *dev) { - kfree(fsg->common->luns); - kfree(fsg); + /* Nothing needs to be done */ } -static void lun_release(struct device *dev) +static inline void fsg_common_get(struct fsg_common *common) { + kref_get(&common->ref); } +static inline void fsg_common_put(struct fsg_common *common) +{ + kref_put(&common->ref, fsg_common_release); +} + + +static struct fsg_common *fsg_common_init(struct fsg_common *common, + struct usb_gadget *gadget) +{ + struct fsg_buffhd *bh; + struct fsg_lun *curlun; + int nluns, i, rc; + + /* Find out how many LUNs there should be */ + nluns = mod_data.nluns; + if (nluns == 0) + nluns = max(mod_data.num_filenames, 1u); + if (nluns < 1 || nluns > FSG_MAX_LUNS) { + dev_err(&gadget->dev, "invalid number of LUNs: %u\n", nluns); + return ERR_PTR(-EINVAL); + } + + /* Allocate? */ + if (!common) { + 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; + } + common->gadget = gadget; + + /* Create the LUNs, open their backing files, and register the + * LUN devices in sysfs. */ + curlun = kzalloc(nluns * sizeof *curlun, GFP_KERNEL); + if (!curlun) { + kfree(common); + return ERR_PTR(-ENOMEM); + } + common->luns = curlun; + + init_rwsem(&common->filesem); + + for (i = 0; i < nluns; ++i, ++curlun) { + curlun->cdrom = !!mod_data.cdrom; + curlun->ro = mod_data.cdrom || mod_data.ro[i]; + curlun->removable = mod_data.removable; + curlun->dev.release = fsg_lun_release; + curlun->dev.parent = &gadget->dev; + curlun->dev.driver = &fsg_driver.driver; + dev_set_drvdata(&curlun->dev, &common->filesem); + dev_set_name(&curlun->dev,"%s-lun%d", + dev_name(&gadget->dev), i); + + rc = device_register(&curlun->dev); + if (rc) { + INFO(common, "failed to register LUN%d: %d\n", i, rc); + common->nluns = i; + goto error_release; + } + + rc = device_create_file(&curlun->dev, &dev_attr_ro); + if (rc) + goto error_luns; + rc = device_create_file(&curlun->dev, &dev_attr_file); + if (rc) + goto error_luns; + + if (mod_data.file[i] && *mod_data.file[i]) { + rc = fsg_lun_open(curlun, mod_data.file[i]); + if (rc) + goto error_luns; + } else if (!mod_data.removable) { + ERROR(common, "no file given for LUN%d\n", i); + rc = -EINVAL; + goto error_luns; + } + } + common->nluns = nluns; + + + /* Data buffers cyclic list */ + /* Buffers in buffhds are static -- no need for additional + * allocation. */ + bh = common->buffhds; + i = FSG_NUM_BUFFERS - 1; + do { + bh->next = bh + 1; + } while (++bh, --i); + bh->next = common->buffhds; + + + /* Release */ + if (mod_data.release == 0xffff) { // Parameter wasn't set + int gcnum; + + /* The sa1100 controller is not supported */ + if (gadget_is_sa1100(gadget)) + gcnum = -1; + else + gcnum = usb_gadget_controller_number(gadget); + if (gcnum >= 0) + mod_data.release = 0x0300 + gcnum; + else { + WARNING(common, "controller '%s' not recognized\n", + gadget->name); + WARNING(common, "controller '%s' not recognized\n", + gadget->name); + mod_data.release = 0x0399; + } + } + + + /* Some peripheral controllers are known not to be able to + * halt bulk endpoints correctly. If one of them is present, + * disable stalls. + */ + if (gadget_is_sh(fsg->gadget) || gadget_is_at91(fsg->gadget)) + mod_data.can_stall = 0; + + + kref_init(&common->ref); + return common; + + +error_luns: + common->nluns = i + 1; +error_release: + /* Call fsg_common_release() directly, ref is not initialised */ + fsg_common_release(&common->ref); + return ERR_PTR(rc); +} + + +static void fsg_common_release(struct kref *ref) +{ + struct fsg_common *common = + container_of(ref, struct fsg_common, ref); + unsigned i = common->nluns; + struct fsg_lun *lun = common->luns; + + /* Beware tempting for -> do-while optimization: when in error + * recovery nluns may be zero. */ + + for (; i; --i, ++lun) { + device_remove_file(&lun->dev, &dev_attr_ro); + device_remove_file(&lun->dev, &dev_attr_file); + fsg_lun_close(lun); + device_unregister(&lun->dev); + } + + kfree(common->luns); + if (common->free_storage_on_release) + kfree(common); +} + + +/*-------------------------------------------------------------------------*/ + + static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) { struct fsg_dev *fsg = get_gadget_data(gadget); - int i; - struct fsg_lun *curlun; struct usb_request *req = fsg->ep0req; DBG(fsg, "unbind\n"); clear_bit(REGISTERED, &fsg->atomic_bitflags); - /* Unregister the sysfs attribute files and the LUNs */ - for (i = 0; i < fsg->common->nluns; ++i) { - curlun = &fsg->common->luns[i]; - if (curlun->registered) { - device_remove_file(&curlun->dev, &dev_attr_ro); - device_remove_file(&curlun->dev, &dev_attr_file); - fsg_lun_close(curlun); - device_unregister(&curlun->dev); - curlun->registered = 0; - } - } - /* If the thread isn't already dead, tell it to exit now */ if (fsg->state != FSG_STATE_TERMINATED) { raise_exception(fsg, FSG_STATE_EXIT); @@ -2814,43 +2967,15 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) usb_ep_free_request(fsg->ep0, req); } + fsg_common_put(fsg->common); + kfree(fsg); set_gadget_data(gadget, NULL); } -static int __init check_parameters(struct fsg_dev *fsg) -{ - int gcnum; - - /* Some peripheral controllers are known not to be able to - * halt bulk endpoints correctly. If one of them is present, - * disable stalls. - */ - if (gadget_is_sh(fsg->gadget) || gadget_is_at91(fsg->gadget)) - mod_data.can_stall = 0; - - if (mod_data.release == 0xffff) { // Parameter wasn't set - /* The sa1100 controller is not supported */ - if (gadget_is_sa1100(fsg->gadget)) - gcnum = -1; - else - gcnum = usb_gadget_controller_number(fsg->gadget); - if (gcnum >= 0) - mod_data.release = 0x0300 + gcnum; - else { - WARNING(fsg, "controller '%s' not recognized\n", - fsg->gadget->name); - mod_data.release = 0x0399; - } - } - - return 0; -} - - static int __init fsg_bind(struct usb_gadget *gadget) { - struct fsg_dev *fsg = the_fsg; + struct fsg_dev *fsg; int rc; int i; struct fsg_lun *curlun; @@ -2858,15 +2983,27 @@ static int __init fsg_bind(struct usb_gadget *gadget) struct usb_request *req; char *pathbuf, *p; + /* Allocate */ + fsg = kzalloc(sizeof *fsg, GFP_KERNEL); + if (!fsg) + return -ENOMEM; + + /* Initialise common */ + fsg->common = fsg_common_init(0, gadget); + if (IS_ERR(fsg->common)) + return PTR_ERR(fsg->common); + + /* Basic parameters */ fsg->gadget = gadget; set_gadget_data(gadget, fsg); fsg->ep0 = gadget->ep0; fsg->ep0->driver_data = fsg; - if ((rc = check_parameters(fsg)) != 0) - goto out; + spin_lock_init(&fsg->lock); + init_completion(&fsg->thread_notifier); - if (mod_data.removable) { // Enable the store_xxx attributes + /* Enable the store_xxx attributes */ + if (mod_data.removable) { dev_attr_file.attr.mode = 0644; dev_attr_file.store = fsg_store_file; if (!mod_data.cdrom) { @@ -2875,62 +3012,6 @@ static int __init fsg_bind(struct usb_gadget *gadget) } } - /* Find out how many LUNs there should be */ - i = mod_data.nluns; - if (i == 0) - i = max(mod_data.num_filenames, 1u); - if (i > FSG_MAX_LUNS) { - ERROR(fsg, "invalid number of LUNs: %d\n", i); - rc = -EINVAL; - goto out; - } - - /* Create the LUNs, open their backing files, and register the - * LUN devices in sysfs. */ - fsg->common->luns = kzalloc(i * sizeof(struct fsg_lun), GFP_KERNEL); - if (!fsg->common->luns) { - rc = -ENOMEM; - goto out; - } - fsg->common->nluns = i; - - for (i = 0; i < fsg->common->nluns; ++i) { - curlun = &fsg->common->luns[i]; - curlun->cdrom = !!mod_data.cdrom; - curlun->ro = mod_data.cdrom || mod_data.ro[i]; - curlun->initially_ro = curlun->ro; - curlun->removable = mod_data.removable; - curlun->dev.release = lun_release; - curlun->dev.parent = &gadget->dev; - curlun->dev.driver = &fsg_driver.driver; - dev_set_drvdata(&curlun->dev, &fsg->common->filesem); - dev_set_name(&curlun->dev,"%s-lun%d", - dev_name(&gadget->dev), i); - - if ((rc = device_register(&curlun->dev)) != 0) { - INFO(fsg, "failed to register LUN%d: %d\n", i, rc); - goto out; - } - if ((rc = device_create_file(&curlun->dev, - &dev_attr_ro)) != 0 || - (rc = device_create_file(&curlun->dev, - &dev_attr_file)) != 0) { - device_unregister(&curlun->dev); - goto out; - } - curlun->registered = 1; - - if (mod_data.file[i] && *mod_data.file[i]) { - if ((rc = fsg_lun_open(curlun, - mod_data.file[i])) != 0) - goto out; - } else if (!mod_data.removable) { - ERROR(fsg, "no file given for LUN%d\n", i); - rc = -EINVAL; - goto out; - } - } - /* Find all the endpoints we will use */ usb_ep_autoconfig_reset(gadget); ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc); @@ -3028,6 +3109,8 @@ static int __init fsg_bind(struct usb_gadget *gadget) /* Tell the thread to start working */ wake_up_process(fsg->thread_task); + + the_fsg = fsg; return 0; autoconf_fail: @@ -3066,64 +3149,18 @@ static struct usb_gadget_driver fsg_driver = { }; -static int __init fsg_alloc(void) -{ - struct fsg_dev *fsg; - struct fsg_buffhd *bh; - unsigned i; - - fsg = kzalloc(sizeof *fsg, GFP_KERNEL); - if (!fsg) - return -ENOMEM; - - fsg->common = kzalloc(sizeof *fsg->common, GFP_KERNEL); - if (!fsg->common) { - kfree(fsg); - return -ENOMEM; - } - - bh = fsg->common->buffhds; - i = FSG_NUM_BUFFERS - 1; - do { - bh->next = bh + 1; - } while (++bh, --i); - bh->next = fsg->common->buffhds; - - spin_lock_init(&fsg->lock); - init_rwsem(&fsg->common->filesem); - init_completion(&fsg->thread_notifier); - - the_fsg = fsg; - return 0; -} - - static int __init fsg_init(void) { - int rc; - struct fsg_dev *fsg; - - if ((rc = fsg_alloc()) != 0) - return rc; - fsg = the_fsg; - if ((rc = usb_gadget_register_driver(&fsg_driver)) != 0) - fsg_release(fsg); - return rc; + return usb_gadget_register_driver(&fsg_driver); } module_init(fsg_init); static void __exit fsg_cleanup(void) { - struct fsg_dev *fsg = the_fsg; - /* Unregister the driver iff the thread hasn't already done so */ - if (test_and_clear_bit(REGISTERED, &fsg->atomic_bitflags)) + if (the_fsg && + test_and_clear_bit(REGISTERED, &the_fsg->atomic_bitflags)) usb_gadget_unregister_driver(&fsg_driver); - - /* Wait for the thread to finish up */ - wait_for_completion(&fsg->thread_notifier); - - fsg_release(fsg); } module_exit(fsg_cleanup); |