An example port of a usb function to the USB functions gadget.

Signed-off-by: Andrzej Pietrasiewicz <andrze...@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.p...@samsung.com>
---
 drivers/usb/gadget/f_mass_storage.c |  635 +++++++++++++++++++----------------
 drivers/usb/gadget/storage_common.c |  337 +++++++++++--------
 2 files changed, 545 insertions(+), 427 deletions(-)

diff --git a/drivers/usb/gadget/f_mass_storage.c 
b/drivers/usb/gadget/f_mass_storage.c
index 3a7668b..abdcf0d 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -55,11 +55,6 @@
  *
  *     nluns           Number of LUNs function have (anywhere from 1
  *                             to FSG_MAX_LUNS which is 8).
- *     luns            An array of LUN configuration values.  This
- *                             should be filled for each LUN that
- *                             function will include (ie. for "nluns"
- *                             LUNs).  Each element of the array has
- *                             the following fields:
  *     ->filename      The path to the backing file for the LUN.
  *                             Required if LUN is not marked as
  *                             removable.
@@ -219,6 +214,7 @@
 #include <linux/usb/composite.h>
 
 #include "gadget_chips.h"
+#include "usb_functions.h"
 
 
 /*------------------------------------------------------------------------*/
@@ -268,6 +264,9 @@ struct fsg_operations {
 
 /* Data shared by all the FSG instances. */
 struct fsg_common {
+       struct config_group     group;
+       enum ufg_hdr_type       type;
+
        struct usb_gadget       *gadget;
        struct usb_composite_dev *cdev;
        struct fsg_dev          *fsg, *new_fsg;
@@ -292,7 +291,6 @@ struct fsg_common {
 
        unsigned int            nluns;
        unsigned int            lun;
-       struct fsg_lun          *luns;
        struct fsg_lun          *curlun;
 
        unsigned int            bulk_out_maxpacket;
@@ -307,7 +305,6 @@ struct fsg_common {
        u32                     usb_amount_left;
 
        unsigned int            can_stall:1;
-       unsigned int            free_storage_on_release:1;
        unsigned int            phase_error:1;
        unsigned int            short_packet_received:1;
        unsigned int            bad_lun_okay:1;
@@ -329,6 +326,8 @@ struct fsg_common {
        char inquiry_string[8 + 16 + 4 + 1];
 
        struct kref             ref;
+
+       const char              *lun_name_format;
 };
 
 struct fsg_config {
@@ -341,6 +340,8 @@ struct fsg_config {
                char nofua;
        } luns[FSG_MAX_LUNS];
 
+       const char              *lun_name_format;
+
        /* Callback functions. */
        const struct fsg_operations     *ops;
        /* Gadget's private data. */
@@ -350,6 +351,10 @@ struct fsg_config {
        const char *product_name;               /* 16 characters or less */
 
        char                    can_stall;
+       struct usb_configuration *usb_config;
+
+       /* configfs-related */
+       struct config_group     group;
 };
 
 struct fsg_dev {
@@ -1380,8 +1385,7 @@ static int do_start_stop(struct fsg_common *common)
 
        /* Simulate an unload/eject */
        if (common->ops && common->ops->pre_eject) {
-               int r = common->ops->pre_eject(common, curlun,
-                                              curlun - common->luns);
+               int r = common->ops->pre_eject(common, curlun, curlun->n_lun);
                if (unlikely(r < 0))
                        return r;
                else if (r)
@@ -1395,8 +1399,7 @@ static int do_start_stop(struct fsg_common *common)
        down_read(&common->filesem);
 
        return common->ops && common->ops->post_eject
-               ? min(0, common->ops->post_eject(common, curlun,
-                                                curlun - common->luns))
+               ? min(0, common->ops->post_eject(common, curlun, curlun->n_lun))
                : 0;
 }
 
@@ -2200,9 +2203,22 @@ static int received_cbw(struct fsg_dev *fsg, struct 
fsg_buffhd *bh)
        if (common->data_size == 0)
                common->data_dir = DATA_DIR_NONE;
        common->lun = cbw->Lun;
-       if (common->lun >= 0 && common->lun < common->nluns)
-               common->curlun = &common->luns[common->lun];
-       else
+       if (common->lun >= 0 && common->lun < common->nluns) {
+               struct config_item *it;
+
+               mutex_lock(&common->group.cg_subsys->su_mutex);
+               list_for_each_entry(it, &common->group.cg_children, ci_entry) {
+                       struct fsg_lun *lun;
+
+                       lun = to_fsg_lun(it);
+                       if (lun->n_lun == common->lun) {
+                               common->curlun = lun;
+
+                               break;
+                       }
+               }
+               mutex_unlock(&common->group.cg_subsys->su_mutex);
+       } else
                common->curlun = NULL;
        common->tag = cbw->Tag;
        return 0;
@@ -2262,6 +2278,7 @@ static int alloc_request(struct fsg_common *common, 
struct usb_ep *ep,
 /* Reset interface setting and re-init endpoint state (toggle etc). */
 static int do_set_interface(struct fsg_common *common, struct fsg_dev *new_fsg)
 {
+       struct config_item *item;
        struct fsg_dev *fsg;
        int i, rc = 0;
 
@@ -2346,8 +2363,14 @@ reset:
        }
 
        common->running = 1;
-       for (i = 0; i < common->nluns; ++i)
-               common->luns[i].unit_attention_data = SS_RESET_OCCURRED;
+       mutex_lock(&common->group.cg_subsys->su_mutex);
+       list_for_each_entry(item, &common->group.cg_children, ci_entry) {
+               struct fsg_lun *lun;
+
+               lun = to_fsg_lun(item);
+               lun->unit_attention_data = SS_RESET_OCCURRED;
+       }
+       mutex_unlock(&common->group.cg_subsys->su_mutex);
        return rc;
 }
 
@@ -2378,7 +2401,6 @@ static void handle_exception(struct fsg_common *common)
        int                     i;
        struct fsg_buffhd       *bh;
        enum fsg_state          old_state;
-       struct fsg_lun          *curlun;
        unsigned int            exception_req_tag;
 
        /*
@@ -2446,14 +2468,20 @@ static void handle_exception(struct fsg_common *common)
        if (old_state == FSG_STATE_ABORT_BULK_OUT)
                common->state = FSG_STATE_STATUS_PHASE;
        else {
-               for (i = 0; i < common->nluns; ++i) {
-                       curlun = &common->luns[i];
+               struct config_item *it;
+
+               mutex_lock(&common->group.cg_subsys->su_mutex);
+               list_for_each_entry(it, &common->group.cg_children, ci_entry) {
+                       struct fsg_lun *curlun;
+
+                       curlun = to_fsg_lun(it);
                        curlun->prevent_medium_removal = 0;
                        curlun->sense_data = SS_NO_SENSE;
                        curlun->unit_attention_data = SS_NO_SENSE;
                        curlun->sense_data_info = 0;
                        curlun->info_valid = 0;
                }
+               mutex_unlock(&common->group.cg_subsys->su_mutex);
                common->state = FSG_STATE_IDLE;
        }
        spin_unlock_irq(&common->lock);
@@ -2586,17 +2614,25 @@ 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;
-               unsigned i = common->nluns;
+               struct list_head *cursor;
 
                down_write(&common->filesem);
-               for (; i--; ++curlun) {
+
+               mutex_lock(&common->group.cg_subsys->su_mutex);
+               list_for_each_prev(cursor, &common->group.cg_children) {
+                       struct config_item *item;
+                       struct fsg_lun *curlun;
+
+                       item = list_entry(cursor, struct config_item, ci_entry);
+
+                       curlun = to_fsg_lun(item);
                        if (!fsg_lun_is_open(curlun))
                                continue;
 
                        fsg_lun_close(curlun);
                        curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
                }
+               mutex_unlock(&common->group.cg_subsys->su_mutex);
                up_write(&common->filesem);
        }
 
@@ -2604,28 +2640,10 @@ static int fsg_main_thread(void *common_)
        complete_and_exit(&common->thread_notifier, 0);
 }
 
-
-/*************************** DEVICE ATTRIBUTES ***************************/
-
-static DEVICE_ATTR(ro, 0644, fsg_show_ro, fsg_store_ro);
-static DEVICE_ATTR(nofua, 0644, fsg_show_nofua, fsg_store_nofua);
-static DEVICE_ATTR(file, 0644, fsg_show_file, fsg_store_file);
-
-static struct device_attribute dev_attr_ro_cdrom =
-       __ATTR(ro, 0444, fsg_show_ro, NULL);
-static struct device_attribute dev_attr_file_nonremovable =
-       __ATTR(file, 0444, fsg_show_file, NULL);
-
-
 /****************************** FSG COMMON ******************************/
 
 static void fsg_common_release(struct kref *ref);
 
-static void fsg_lun_release(struct device *dev)
-{
-       /* Nothing needs to be done */
-}
-
 static inline void fsg_common_get(struct fsg_common *common)
 {
        kref_get(&common->ref);
@@ -2636,49 +2654,192 @@ 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_composite_dev *cdev,
-                                         struct fsg_config *cfg)
+#define DIGITS         "0123456789"
+static struct config_item *alloc_fsg_lun(struct config_group *group,
+                                        const char *name)
 {
-       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);
+       struct fsg_common *common;
+       struct fsg_lun *lun;
+       struct config_item *item;
+       const char *p, *r, *s;
+       int n;
+       char buf[256];
+       unsigned long tmp;
+
+       common = group ? container_of(group, struct fsg_common, group) : NULL;
+       if (!common)
+               return ERR_PTR(-ENOMEM);
 
-       /* 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);
+       /*
+        * TODO: some of the checks should be done when
+        *common->lun_name_format is assigned
+        */
+       /* check if first part of the name format is good */
+       p = strchr(common->lun_name_format, '%');
+       if (!p)
+               return ERR_PTR(-EINVAL);
+       if (*(p + 1) != 'd')
+               return ERR_PTR(-EINVAL);
+       n = p - common->lun_name_format;
+       /* check if the first part of the name matches the format */
+       if (strncmp(name, common->lun_name_format, n))
+               return ERR_PTR(-EINVAL);
+       /* interpret the %d part */
+       /*
+        * TODO: improve. Now e.g. 01 and 1 are considered equal,
+        * which means lun1 cannot be created after lun01 is created.
+        * Probably lun01 (number parts with leading zeros) should be
+        * disallowed.
+        */
+       r = name + n;
+       s = strpbrk(r, DIGITS);
+       if (s != r)
+               return ERR_PTR(-EINVAL);
+       n = strspn(s, DIGITS);
+       while (n--) {
+               buf[s - r] = *s;
+               s++;
+       }
+       buf[s - r] = '\0';
+       tmp = simple_strtoul(buf, NULL, 10);
+       if (tmp >= common->nluns)
+               return ERR_PTR(-EINVAL);
+       /* check if the second part of the name meatches the format */
+       if (strcmp(p + 2, s))
                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;
+       list_for_each_entry(item, &common->group.cg_children, ci_entry) {
+               lun = to_fsg_lun(item);
+               if (tmp == lun->n_lun)
+                       return ERR_PTR(-EBUSY);
        }
 
-       common->buffhds = kcalloc(fsg_num_buffers,
-                                 sizeof *(common->buffhds), GFP_KERNEL);
-       if (!common->buffhds) {
-               if (common->free_storage_on_release)
-                       kfree(common);
+       lun = kzalloc(sizeof *lun, GFP_KERNEL);
+       if (!lun)
                return ERR_PTR(-ENOMEM);
-       }
 
-       common->ops = cfg->ops;
-       common->private_data = cfg->private_data;
+       lun->filesem = &common->filesem;
+       lun->n_lun = tmp;
+
+       config_item_init_type_name(&lun->item, name, &fsg_lun_item_type);
+
+       LINFO(lun, "LUN: %s%s%sfile: %s\n",
+             lun->removable ? "removable " : "",
+             lun->ro ? "read only " : "",
+             lun->cdrom ? "CD-ROM " : "",
+             "(no medium)");
+
+       return &lun->item;
+}
+
+static ssize_t fsg_common_show_luns(struct fsg_common *common, char *buf)
+{
+       return sprintf(buf, "%d\n", common->nluns);
+}
+
+static ssize_t fsg_common_store_luns(struct fsg_common *common, const char 
*buf,
+                                    size_t count)
+{
+       struct config_item *function, *config, *gadget;
+       unsigned long tmp;
+       char *p = (char *)buf;
+       int res;
+
+       function = common->group.cg_item.ci_parent;
+       if (!function)
+               return -EBUSY;
+
+       config = function->ci_parent;
+       if (!config)
+               return -EBUSY;
+
+       gadget = config->ci_parent;
+       if (!gadget)
+               return -EBUSY;
+
+       res = kstrtoul(p, 10, &tmp);
+       if (res)
+               return -EINVAL;
+
+       if (tmp > 16383)
+               return -ERANGE;
+
+       common->nluns = tmp;
+
+       return count;
+}
+
+static ssize_t fsg_common_show_stall(struct fsg_common *common, char *buf)
+{
+       return sprintf(buf, "%d\n", common->can_stall);
+}
+
+static ssize_t fsg_common_store_stall(struct fsg_common *common,
+                                     const char *buf, size_t count)
+{
+       if (buf[0] != '0' && buf[0] != '1')
+               return -EINVAL;
+
+       common->can_stall = buf[0] == '1';
+
+       return count;
+}
+
+CONFIGFS_ATTR_STRUCT(fsg_common);
+
+#define FSG_CONFIG_ATTR_RW(_name)                                      \
+static struct fsg_common_attribute fsg_common_##_name =                        
\
+       __CONFIGFS_ATTR(_name, S_IRUGO | S_IWUSR, fsg_common_show_##_name,\
+                       fsg_common_store_##_name)
+
+#define FSG_CONFIG_ATTR_RO(_name)                                      \
+static struct fsg_common_attribute fsg_common_##_name =                        
\
+       __CONFIGFS_ATTR(_name, S_IRUGO , fsg_common_show_##_name, NULL)
+
+FSG_CONFIG_ATTR_RW(luns);
+FSG_CONFIG_ATTR_RW(stall);
+
+static struct configfs_attribute *fsg_common_attrs[] = {
+       &fsg_common_luns.attr,
+       &fsg_common_stall.attr,
+       NULL,
+};
+
+static struct fsg_common *to_fsg_common(struct config_item *item)
+{
+       return item ? container_of(to_config_group(item),
+                                  struct fsg_common, group) : NULL;
+}
+
+CONFIGFS_ATTR_OPS(fsg_common);
+
+static void fsg_common_release_item(struct config_item *item)
+{
+       kfree(to_fsg_common(item));
+}
+
+static struct configfs_item_operations fsg_common_item_ops = {
+       .show_attribute         = fsg_common_attr_show,
+       .store_attribute        = fsg_common_attr_store,
+       .release                = fsg_common_release_item,
+};
+
+static struct configfs_group_operations fsg_common_group_ops = {
+       .make_item      = alloc_fsg_lun,
+};
+
+static struct config_item_type fsg_common_item_type = {
+       .ct_attrs       = fsg_common_attrs,
+       .ct_item_ops    = &fsg_common_item_ops,
+       .ct_group_ops   = &fsg_common_group_ops,
+       .ct_owner       = THIS_MODULE,
+};
+
+static struct fsg_common *fsg_common_init_cdev(struct fsg_common *common,
+                                              struct usb_composite_dev *cdev)
+{
+       struct usb_gadget *gadget = cdev->gadget;
+       int rc, i;
 
        common->gadget = gadget;
        common->ep0 = gadget->ep0;
@@ -2694,65 +2855,58 @@ static struct fsg_common *fsg_common_init(struct 
fsg_common *common,
                fsg_intf_desc.iInterface = rc;
        }
 
+       /* Prepare inquiryString */
+       i = get_default_bcdDevice();
+       snprintf(common->inquiry_string, sizeof common->inquiry_string,
+                "%-8s%-16s%04x", "Linux",
+                /* Assume product name dependent on the first LUN */
+                /* TODO: actually check first child's "cdrom" flag */
+                       "USB mass storage", i);
+
        /*
-        * Create the LUNs, open their backing files, and register the
-        * LUN devices in sysfs.
+        * Some peripheral controllers are known not to be able to
+        * halt bulk endpoints correctly.  If one of them is present,
+        * disable stalls.
         */
-       curlun = kcalloc(nluns, sizeof(*curlun), GFP_KERNEL);
-       if (unlikely(!curlun)) {
-               rc = -ENOMEM;
-               goto error_release;
-       }
-       common->luns = curlun;
+       common->can_stall = common->can_stall &&
+               !(gadget_is_at91(common->gadget));
 
-       init_rwsem(&common->filesem);
+       return common;
 
-       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;
-               }
+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);
+}
 
-               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;
+static struct fsg_common *fsg_common_init(struct fsg_common *common)
+{
+       struct fsg_buffhd *bh;
+       int i, rc;
 
-               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;
-               }
-       }
-       common->nluns = nluns;
+       rc = fsg_num_buffers_validate();
+       if (rc != 0)
+               return ERR_PTR(rc);
+
+       /* TODO: move it somewhere else */
+       /*if (common->nluns < 1 || common->nluns > FSG_MAX_LUNS) {
+               printk("invalid number of LUNs: %u\n", nluns);
+               return ERR_PTR(-EINVAL);
+       }*/
+
+       common->buffhds = kcalloc(fsg_num_buffers,
+                                 sizeof *(common->buffhds), GFP_KERNEL);
+       if (!common->buffhds)
+               return ERR_PTR(-ENOMEM);
+
+       common->ops = NULL;
+       common->private_data = NULL;
+
+       init_rwsem(&common->filesem);
+
+       common->lun_name_format = common->lun_name_format ?
+                       common->lun_name_format : "lun%d";
 
        /* Data buffers cyclic list */
        bh = common->buffhds;
@@ -2770,24 +2924,6 @@ buffhds_first_it:
        } while (--i);
        bh->next = common->buffhds;
 
-       /* 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-Stor Gadget"
-                                    : "File-CD Gadget"),
-                i);
-
-       /*
-        * 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));
-
        spin_lock_init(&common->lock);
        kref_init(&common->ref);
 
@@ -2802,39 +2938,15 @@ buffhds_first_it:
        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);
-
-       pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
-       for (i = 0, nluns = common->nluns, curlun = common->luns;
-            i < nluns;
-            ++curlun, ++i) {
-               char *p = "(no medium)";
-               if (fsg_lun_is_open(curlun)) {
-                       p = "(error)";
-                       if (pathbuf) {
-                               p = d_path(&curlun->filp->f_path,
-                                          pathbuf, PATH_MAX);
-                               if (IS_ERR(p))
-                                       p = "(error)";
-                       }
-               }
-               LINFO(curlun, "LUN: %s%s%sfile: %s\n",
-                     curlun->removable ? "removable " : "",
-                     curlun->ro ? "read only " : "",
-                     curlun->cdrom ? "CD-ROM " : "",
-                     p);
-       }
-       kfree(pathbuf);
+       pr_info(FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n");
+       pr_info("Number of LUNs=%d\n", common->nluns);
 
-       DBG(common, "I/O thread pid: %d\n", task_pid_nr(common->thread_task));
+       pr_info("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. */
@@ -2842,9 +2954,38 @@ error_release:
        return ERR_PTR(rc);
 }
 
+static struct config_group *alloc_fsg_common(struct config_group *group,
+                                            const char *n)
+{
+       struct config_item *item;
+       struct fsg_common *common, *ret;
+
+       if (strcmp(n, "f_mass_storage"))
+               return ERR_PTR(-EINVAL);
+
+       list_for_each_entry(item, &group->cg_children, ci_entry)
+               if (!strcmp(n, item->ci_name))
+                       return ERR_PTR(-EBUSY);
+
+       common = kzalloc(sizeof *common, GFP_KERNEL);
+       if (!common)
+               return ERR_PTR(-ENOMEM);
+
+       ret = fsg_common_init(common);
+       if (IS_ERR(ret)) {
+               kfree(common);
+               return (struct config_group *)ret;
+       }
+
+       config_group_init_type_name(&common->group, n, &fsg_common_item_type);
+
+       return &common->group;
+}
+
 static void fsg_common_release(struct kref *ref)
 {
        struct fsg_common *common = container_of(ref, struct fsg_common, ref);
+       struct config_item *item;
 
        /* If the thread isn't already dead, tell it to exit now */
        if (common->state != FSG_STATE_TERMINATED) {
@@ -2852,26 +2993,9 @@ static void fsg_common_release(struct kref *ref)
                wait_for_completion(&common->thread_notifier);
        }
 
-       if (likely(common->luns)) {
-               struct fsg_lun *lun = 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);
-                       fsg_lun_close(lun);
-                       device_unregister(&lun->dev);
-               }
-
-               kfree(common->luns);
+       list_for_each_entry(item, &common->group.cg_children, ci_entry) {
+               struct fsg_lun *lun = to_fsg_lun(item);
+               fsg_lun_close(lun);
        }
 
        {
@@ -2883,11 +3007,8 @@ static void fsg_common_release(struct kref *ref)
        }
 
        kfree(common->buffhds);
-       if (common->free_storage_on_release)
-               kfree(common);
 }
 
-
 /*-------------------------------------------------------------------------*/
 
 static void fsg_unbind(struct usb_configuration *c, struct usb_function *f)
@@ -2903,6 +3024,8 @@ static void fsg_unbind(struct usb_configuration *c, 
struct usb_function *f)
                wait_event(common->fsg_wait, common->fsg != fsg);
        }
 
+       common->curlun = NULL;
+       common->lun = 0;
        fsg_common_put(common);
        usb_free_descriptors(fsg->function.descriptors);
        usb_free_descriptors(fsg->function.hs_descriptors);
@@ -2994,13 +3117,25 @@ static struct usb_gadget_strings *fsg_strings_array[] = 
{
        NULL,
 };
 
-static int fsg_bind_config(struct usb_composite_dev *cdev,
-                          struct usb_configuration *c,
-                          struct fsg_common *common)
+static int fsg_bind_function(struct usb_configuration *c,
+                            struct config_item *item, void *data)
 {
        struct fsg_dev *fsg;
+       struct usb_composite_dev *cdev;
+       struct fsg_common *common;
+       struct list_head *cursor;
+       int luns;
        int rc;
 
+       common = to_fsg_common(item);
+
+       /* refuse bind if some luns are not yet created */
+       luns = 0;
+       list_for_each(cursor, &common->group.cg_children)
+               luns++;
+       if (luns != common->nluns)
+               return -EAGAIN;
+
        fsg = kzalloc(sizeof *fsg, GFP_KERNEL);
        if (unlikely(!fsg))
                return -ENOMEM;
@@ -3012,8 +3147,11 @@ static int fsg_bind_config(struct usb_composite_dev 
*cdev,
        fsg->function.setup       = fsg_setup;
        fsg->function.set_alt     = fsg_set_alt;
        fsg->function.disable     = fsg_disable;
+       fsg->common             = common;
+
+       cdev = data;
+       fsg_common_init_cdev(fsg->common, cdev);
 
-       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
@@ -3025,97 +3163,14 @@ static int fsg_bind_config(struct usb_composite_dev 
*cdev,
        rc = usb_add_function(c, &fsg->function);
        if (unlikely(rc))
                kfree(fsg);
-       else
-               fsg_common_get(fsg->common);
-       return rc;
-}
-
-
-/************************* Module parameters *************************/
+       else {
+               struct configfs_attribute *attr;
+               struct dentry *fs_entry;
+               int i;
 
-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];
+               fsg_common_get(fsg->common);
 
-       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)
-{
-       struct fsg_lun_config *lun;
-       unsigned i;
-
-       /* Configure LUNs */
-       cfg->nluns =
-               min(params->luns ?: (params->file_count ?: 1u),
-                   (unsigned)FSG_MAX_LUNS);
-       for (i = 0, lun = cfg->luns; i < cfg->nluns; ++i, ++lun) {
-               lun->ro = !!params->ro[i];
-               lun->cdrom = !!params->cdrom[i];
-               lun->removable = !!params->removable[i];
-               lun->filename =
-                       params->file_count > i && params->file[i][0]
-                       ? params->file[i]
-                       : 0;
-       }
-
-       /* Let MSF use defaults */
-       cfg->vendor_name = 0;
-       cfg->product_name = 0;
-
-       cfg->ops = NULL;
-       cfg->private_data = NULL;
-
-       /* Finalise */
-       cfg->can_stall = params->stall;
-}
-
-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);
+       }
+       return rc;
 }
diff --git a/drivers/usb/gadget/storage_common.c 
b/drivers/usb/gadget/storage_common.c
index 8d9bcd8..d1c80e5 100644
--- a/drivers/usb/gadget/storage_common.c
+++ b/drivers/usb/gadget/storage_common.c
@@ -45,6 +45,7 @@
 
 
 #include <linux/usb/storage.h>
+#include <linux/configfs.h>
 #include <scsi/scsi.h>
 #include <asm/unaligned.h>
 
@@ -73,10 +74,9 @@
 #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)
+#define LERROR(lun, fmt, args...)  pr_err(fmt, ## args)
+#define LDBG(lun, fmt, args...)   pr_debug(fmt, ## args)
+#define LINFO(lun, fmt, args...)  pr_info(fmt, ## args)
 
 /*
  * Keep those macros in sync with those in
@@ -179,7 +179,6 @@ struct interrupt_data {
 
 /*-------------------------------------------------------------------------*/
 
-
 struct fsg_lun {
        struct file     *filp;
        loff_t          file_length;
@@ -200,16 +199,203 @@ struct fsg_lun {
 
        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;
+
+       /* configfs-related section */
+       struct config_item      item;
+       struct rw_semaphore     *filesem;
+       unsigned int            n_lun;
 };
 
 #define fsg_lun_is_open(curlun)        ((curlun)->filp != NULL)
 
-static struct fsg_lun *fsg_lun_from_dev(struct device *dev)
+static ssize_t fsg_lun_show_ro(struct fsg_lun *curlun, char *buf)
+{
+       return sprintf(buf, "%d\n", fsg_lun_is_open(curlun)
+                                 ? curlun->ro
+                                 : curlun->initially_ro);
+}
+
+static ssize_t fsg_lun_show_nofua(struct fsg_lun *curlun, char *buf)
+{
+       return sprintf(buf, "%u\n", curlun->nofua);
+}
+
+static ssize_t fsg_lun_show_file(struct fsg_lun *curlun, char *buf)
+{
+       struct rw_semaphore     *filesem = curlun->filesem;
+       char            *p;
+       ssize_t         rc;
+
+       down_read(filesem);
+       if (fsg_lun_is_open(curlun)) {  /* Get the complete pathname */
+               p = d_path(&curlun->filp->f_path, buf, PAGE_SIZE - 1);
+               if (IS_ERR(p))
+                       rc = PTR_ERR(p);
+               else {
+                       rc = strlen(p);
+                       memmove(buf, p, rc);
+                       buf[rc] = '\n';         /* Add a newline */
+                       buf[++rc] = 0;
+               }
+       } else {                                /* No file, return 0 bytes */
+               *buf = 0;
+               rc = 0;
+       }
+       up_read(filesem);
+       return rc;
+}
+
+
+static ssize_t fsg_lun_store_ro(struct fsg_lun *curlun, const char *buf,
+                               size_t count)
+{
+       ssize_t         rc;
+       struct rw_semaphore     *filesem = curlun->filesem;
+       unsigned        ro;
+
+       rc = kstrtouint(buf, 2, &ro);
+       if (rc)
+               return rc;
+
+       /*
+        * Allow the write-enable status to change only while the
+        * 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 = count;
+       }
+       up_read(filesem);
+       return rc;
+}
+
+static int fsg_lun_fsync_sub(struct fsg_lun *curlun);
+
+static ssize_t fsg_lun_store_nofua(struct fsg_lun *curlun, const char *buf,
+                                  size_t count)
+{
+       unsigned        nofua;
+       int             ret;
+
+       ret = kstrtouint(buf, 2, &nofua);
+       if (ret)
+               return ret;
+
+       /* Sync data when switching from async mode to sync */
+       if (!nofua && curlun->nofua)
+               fsg_lun_fsync_sub(curlun);
+
+       curlun->nofua = nofua;
+
+       return count;
+}
+
+static int fsg_lun_open(struct fsg_lun *curlun, const char *filename);
+static void fsg_lun_close(struct fsg_lun *curlun);
+
+static ssize_t fsg_lun_store_file(struct fsg_lun *curlun, const char *buf,
+                                 size_t count)
+{
+       struct rw_semaphore     *filesem = curlun->filesem;
+       int             rc = 0;
+
+       if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) {
+               LDBG(curlun, "eject attempt prevented\n");
+               return -EBUSY;                          /* "Door is locked" */
+       }
+
+       /* Remove a trailing newline */
+       if (count > 0 && buf[count-1] == '\n')
+               ((char *) buf)[count-1] = 0;            /* Ugh! */
+
+       /* Eject current medium */
+       down_write(filesem);
+       if (fsg_lun_is_open(curlun)) {
+               fsg_lun_close(curlun);
+               curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
+       }
+
+       /* Load new medium */
+       if (count > 0 && buf[0]) {
+               rc = fsg_lun_open(curlun, buf);
+               if (rc == 0)
+                       curlun->unit_attention_data =
+                                       SS_NOT_READY_TO_READY_TRANSITION;
+       }
+       up_write(filesem);
+       return (rc < 0 ? rc : count);
+}
+
+static ssize_t fsg_lun_show_removable(struct fsg_lun *curlun, char *buf)
+{
+       return sprintf(buf, "%d\n", curlun->removable);
+}
+
+static ssize_t fsg_lun_store_removable(struct fsg_lun *curlun, const char *buf,
+                                      size_t count)
+{
+       if (fsg_lun_is_open(curlun)) {
+               LDBG(curlun, "media type change prevented\n");
+               return -EBUSY;
+       }
+
+       if (buf[0] != '0' && buf[0] != '1')
+               return -EINVAL;
+
+       curlun->removable = buf[0] == '1';
+
+       return count;
+}
+
+CONFIGFS_ATTR_STRUCT(fsg_lun);
+
+#define FSG_LUN_ATTR_RW(_name)                                         \
+static struct fsg_lun_attribute fsg_lun_##_name =                      \
+       __CONFIGFS_ATTR(_name, S_IRUGO | S_IWUSR, fsg_lun_show_##_name, \
+                       fsg_lun_store_##_name)
+
+FSG_LUN_ATTR_RW(ro);
+FSG_LUN_ATTR_RW(nofua);
+FSG_LUN_ATTR_RW(file);
+FSG_LUN_ATTR_RW(removable);
+
+static struct configfs_attribute *fsg_lun_attrs[] = {
+       &fsg_lun_ro.attr,
+       &fsg_lun_nofua.attr,
+       &fsg_lun_file.attr,
+       &fsg_lun_removable.attr,
+       NULL,
+};
+
+static struct fsg_lun *to_fsg_lun(struct config_item *item)
+{
+       return item ? container_of(item, struct fsg_lun, item) : NULL;
+}
+
+CONFIGFS_ATTR_OPS(fsg_lun);
+
+static void fsg_lun_item_release(struct config_item *item)
 {
-       return container_of(dev, struct fsg_lun, dev);
+       kfree(to_fsg_lun(item));
 }
 
+static struct configfs_item_operations fsg_lun_ops = {
+       .show_attribute         = fsg_lun_attr_show,
+       .store_attribute        = fsg_lun_attr_store,
+       .release                = fsg_lun_item_release,
+};
+
+static struct config_item_type fsg_lun_item_type = {
+       .ct_attrs       = fsg_lun_attrs,
+       .ct_item_ops    = &fsg_lun_ops,
+       .ct_owner       = THIS_MODULE,
+};
 
 /* Big enough to hold our biggest descriptor */
 #define EP0_BUFSIZE    256
@@ -624,6 +810,7 @@ static void fsg_lun_close(struct fsg_lun *curlun)
                fput(curlun->filp);
                curlun->filp = NULL;
        }
+       configfs_undepend_item(curlun->item.ci_group->cg_subsys, &curlun->item);
 }
 
 
@@ -639,6 +826,8 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char 
*filename)
        unsigned int                    blkbits;
        unsigned int                    blksize;
 
+       configfs_depend_item(curlun->item.ci_group->cg_subsys, &curlun->item);
+
        /* R/W if we can, R/O if we must */
        ro = curlun->initially_ro;
        if (!ro) {
@@ -722,6 +911,9 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char 
*filename)
 
 out:
        fput(filp);
+       if (rc)
+               configfs_undepend_item(curlun->item.ci_group->cg_subsys,
+                                      &curlun->item);
        return rc;
 }
 
@@ -762,132 +954,3 @@ static void store_cdrom_address(u8 *dest, int msf, u32 
addr)
 
 /*-------------------------------------------------------------------------*/
 
-
-static ssize_t fsg_show_ro(struct device *dev, struct device_attribute *attr,
-                          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);
-}
-
-static ssize_t fsg_show_nofua(struct device *dev, struct device_attribute 
*attr,
-                             char *buf)
-{
-       struct fsg_lun  *curlun = fsg_lun_from_dev(dev);
-
-       return sprintf(buf, "%u\n", curlun->nofua);
-}
-
-static ssize_t fsg_show_file(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);
-       char            *p;
-       ssize_t         rc;
-
-       down_read(filesem);
-       if (fsg_lun_is_open(curlun)) {  /* Get the complete pathname */
-               p = d_path(&curlun->filp->f_path, buf, PAGE_SIZE - 1);
-               if (IS_ERR(p))
-                       rc = PTR_ERR(p);
-               else {
-                       rc = strlen(p);
-                       memmove(buf, p, rc);
-                       buf[rc] = '\n';         /* Add a newline */
-                       buf[++rc] = 0;
-               }
-       } else {                                /* No file, return 0 bytes */
-               *buf = 0;
-               rc = 0;
-       }
-       up_read(filesem);
-       return rc;
-}
-
-
-static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr,
-                           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;
-
-       rc = kstrtouint(buf, 2, &ro);
-       if (rc)
-               return rc;
-
-       /*
-        * Allow the write-enable status to change only while the
-        * 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 = count;
-       }
-       up_read(filesem);
-       return rc;
-}
-
-static ssize_t fsg_store_nofua(struct device *dev,
-                              struct device_attribute *attr,
-                              const char *buf, size_t count)
-{
-       struct fsg_lun  *curlun = fsg_lun_from_dev(dev);
-       unsigned        nofua;
-       int             ret;
-
-       ret = kstrtouint(buf, 2, &nofua);
-       if (ret)
-               return ret;
-
-       /* Sync data when switching from async mode to sync */
-       if (!nofua && curlun->nofua)
-               fsg_lun_fsync_sub(curlun);
-
-       curlun->nofua = nofua;
-
-       return count;
-}
-
-static ssize_t fsg_store_file(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);
-       int             rc = 0;
-
-       if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) {
-               LDBG(curlun, "eject attempt prevented\n");
-               return -EBUSY;                          /* "Door is locked" */
-       }
-
-       /* Remove a trailing newline */
-       if (count > 0 && buf[count-1] == '\n')
-               ((char *) buf)[count-1] = 0;            /* Ugh! */
-
-       /* Load new medium */
-       down_write(filesem);
-       if (count > 0 && buf[0]) {
-               /* fsg_lun_open() will close existing file if any. */
-               rc = fsg_lun_open(curlun, buf);
-               if (rc == 0)
-                       curlun->unit_attention_data =
-                                       SS_NOT_READY_TO_READY_TRANSITION;
-       } else if (fsg_lun_is_open(curlun)) {
-               fsg_lun_close(curlun);
-               curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
-       }
-       up_write(filesem);
-       return (rc < 0 ? rc : count);
-}
-- 
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to