]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/usb/gadget/f_mass_storage.c
USB: serial: fix race in generic write
[karo-tx-linux.git] / drivers / usb / gadget / f_mass_storage.c
index a01d7d38c01685135a503885342be7f9ec63ebbd..a03ba2c83589ee15f9c1880d17f1aa133eb47f29 100644 (file)
 #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"
 
 
 /*------------------------------------------------------------------------*/
 
 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, "%s", 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);
-}