Signed-off-by: Andrzej Pietrasiewicz <[email protected]>
Signed-off-by: Kyungmin Park <[email protected]>
---
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 [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html