An example port of a usb function to the USB functions gadget.
Signed-off-by: Andrzej Pietrasiewicz <[email protected]>
Signed-off-by: Kyungmin Park <[email protected]>
---
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 [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html