Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@xxxxxxxxxxx> Signed-off-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx> --- 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 majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html