An example port of a usb function to the USB functions gadget. Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@xxxxxxxxxxx> Signed-off-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx> --- 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 majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html