Signed-off-by: Andrzej Pietrasiewicz <andrze...@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.p...@samsung.com>
---
 drivers/usb/gadget/f_mass_storage.c |  225 +++++++++++++++++++++++++++++++++++
 1 files changed, 225 insertions(+), 0 deletions(-)

diff --git a/drivers/usb/gadget/f_mass_storage.c 
b/drivers/usb/gadget/f_mass_storage.c
index b244ddc..c9b9d06 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -2521,6 +2521,205 @@ void fsg_common_put(struct fsg_common *common)
 }
 EXPORT_SYMBOL(fsg_common_put);
 
+static struct config_item *alloc_fsg_lun(struct config_group *group,
+                                        const char *name)
+{
+       struct fsg_common *common;
+       struct fsg_lun *lun;
+       struct config_item *item;
+       unsigned int tmp;
+       unsigned int leading_zeros;
+       int len;
+       const char *p;
+
+       common = group ? container_of(group, struct fsg_common, group) : NULL;
+       if (!common)
+               return ERR_PTR(-ENOMEM);
+
+       if (strncmp(name, "lun", 3))
+               return ERR_PTR(-EINVAL);
+       p = name + 3;
+       if (sscanf(p, "%d%n", &tmp, &len) < 1)
+               return ERR_PTR(-EINVAL);
+       leading_zeros = 0;
+       while (*p++ == '0')
+               leading_zeros++;
+       if (!tmp)
+               leading_zeros--;
+       if (leading_zeros > 0)
+               return ERR_PTR(-EINVAL);
+       if (strlen(name) != len + 3)
+               return ERR_PTR(-EINVAL);
+
+       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);
+       }
+
+       lun = kzalloc(sizeof *lun, GFP_KERNEL);
+       if (!lun)
+               return ERR_PTR(-ENOMEM);
+       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;
+       struct dentry *parent, *new;
+       char n[UFG_STR_LEN];
+       u16 tmp;
+       char *p = (char *)buf;
+       int rc;
+
+       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;
+
+       rc = kstrtou16(p, 10, &tmp);
+       if (rc < 0)
+               return rc;
+       if (tmp > FSG_MAX_LUNS)
+               return -ERANGE;
+
+       common->nluns = tmp;
+       parent = common->group.cg_item.ci_dentry;
+       for (tmp = 0; tmp < common->nluns; tmp++) {
+               struct qstr name;
+
+               sprintf(n, "lun%d", tmp);
+               name.name = n;
+               name.len = strlen(name.name);
+               name.hash = full_name_hash(name.name, name.len);
+
+               new = d_alloc(parent, &name);
+               if (IS_ERR_OR_NULL(new)) {
+                       rc = -ENOMEM;
+                       goto rollback;
+               }
+               d_add(new, NULL);
+               rc = ufg_mkdir(parent, new);
+               if (rc) {
+                       d_drop(new);
+                       dput(new);
+
+                       goto rollback;
+               }
+               dput(new); /* make the refcount 1 */
+       }
+
+       return count;
+
+rollback:
+       while (tmp--) {
+               struct config_item *child;
+
+               sprintf(n, "lun%d", tmp);
+               child = config_group_find_item(&common->group, n);
+               if (child)
+                       ufg_rmdir(parent, child->ci_dentry);
+               
+       }
+       return rc;
+}
+
+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 (count > 2)
+               return -EINVAL;
+
+       if (buf[0] != '0' && buf[0] != '1')
+               return -EINVAL;
+       
+       if (count > 1 && buf[1] != '\0')
+               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,
+};
+
 struct fsg_common *fsg_common_init(struct fsg_common *common)
 {
        struct fsg_buffhd *bh;
@@ -2638,6 +2837,32 @@ error_release:
        return ERR_PTR(rc);
 }
 
+struct config_group *alloc_fsg_common(struct config_group *group,
+                                            const char *n)
+{
+       struct config_item *item;
+       struct fsg_common *common, *ret;
+
+       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;
+}
+EXPORT_SYMBOL(alloc_fsg_common);
+
 static void fsg_common_release(struct kref *ref)
 {
        struct fsg_common *common = container_of(ref, struct fsg_common, ref);
-- 
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