This is an example of how to create an adapter module for an old-style gadget.
It provides the legacy interface while internally operates on configfs.

Usage is like g_mass_storage.ko, but instead the user needs to do e.g.:

insmod libcomposite.ko

insmod f_mass_storage.ko (not required if request_module works)
insmod g_usb_functions.ko (not required if request_module works)

insmod g_mass_storage_adapter.ko removable=1

Then the user can do something on the lines of:

echo <some file>.img > /sys/devices/platform/s3c-hsotg/gadget/lun0/file

and they can enjoy the mass storage gadget functionality.

Signed-off-by: Andrzej Pietrasiewicz <andrze...@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.p...@samsung.com>
---
 drivers/usb/gadget/Kconfig            |    9 +
 drivers/usb/gadget/Makefile           |    4 +
 drivers/usb/gadget/ufg_mass_storage.c |  608 +++++++++++++++++++++++++++++++++
 3 files changed, 621 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/gadget/ufg_mass_storage.c

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 61fa2a7..6f6aebd 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -537,6 +537,15 @@ config USB_FG
          a USB configuration is composed of and enabling/disabling
          the gadget.
 
+config USB_MASS_STORAGE_TO_UFG
+       tristate "Mass Storage Gadget to USB Functions Gadget adapter"
+       depends on USB_FG
+       help
+         The Mass Storage Gadget used to act as a USB Mass Storage disk drive.
+         Its implementation is now considered obsolete. In order to maintain
+         compatibility an adapter module is used to provide the old interface
+         while using the new gadget infrastructure.
+
 config USB_ZERO
        tristate "Gadget Zero (DEVELOPMENT)"
        select USB_LIBCOMPOSITE
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index db8281a..6be8d8e 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -56,6 +56,7 @@ g_webcam-y                    := webcam.o
 g_ncm-y                                := ncm.o
 g_acm_ms-y                     := acm_ms.o
 g_tcm_usb_gadget-y             := tcm_usb_gadget.o
+g_mass_storage_adapter-y       := ufg_mass_storage.o
 
 obj-$(CONFIG_USB_FG)           += g_usb_functions.o
 obj-$(CONFIG_USB_ZERO)         += g_zero.o
@@ -79,3 +80,6 @@ obj-$(CONFIG_USB_GADGET_TARGET)       += tcm_usb_gadget.o
 
 # USB Functions
 obj-$(CONFIG_USB_F_MASS_STORAGE) += f_mass_storage.o
+
+# Gadget adapters
+obj-$(CONFIG_USB_MASS_STORAGE_TO_UFG) += g_mass_storage_adapter.o
diff --git a/drivers/usb/gadget/ufg_mass_storage.c 
b/drivers/usb/gadget/ufg_mass_storage.c
new file mode 100644
index 0000000..3a66b8a
--- /dev/null
+++ b/drivers/usb/gadget/ufg_mass_storage.c
@@ -0,0 +1,608 @@
+/*
+ * ufg_mass_storage.c -- Mass Storage USB Gadget to UFG adapter
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * Author: Andrzej Pietrasiewicz <andrze...@samsung.com>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+
+#include "usb_functions.h"
+#include "storage_common.h"
+
+#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")
+
+#define UFG_MODULE     
(UFG_SUBSYSTEM->subsys.su_group.cg_item.ci_type->ct_owner)
+#define LUN(i)         config->luns[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];
+
+       unsigned int    file_count, ro_count, removable_count, cdrom_count;
+       unsigned int    nofua_count;
+       unsigned int    luns;   /* nluns */
+       bool            stall;  /* can_stall */
+};
+
+struct fsg_config {
+       unsigned nluns;
+       unsigned configfs_nluns;
+       struct fsg_lun_config {
+               const char *filename;
+               char ro;
+               char removable;
+               char cdrom;
+               char nofua;
+               struct device dev;
+               struct dentry *dentry;
+       } luns[FSG_MAX_LUNS];
+
+       char                    can_stall;
+
+       struct dentry           *root;
+       struct dentry           *gadget;
+       struct dentry           *conf;
+       struct dentry           *func;
+       struct dentry           *f;
+};
+
+/*
+ * ATTENTION:
+ *
+ * struct configfs_dirent is "borrowed" from fs/configfs/configfs_internal.h.
+ *
+ * The adapters by default will be phased out, so this _should_ be acceptable.
+ */
+struct configfs_dirent {
+       atomic_t                s_count;
+       int                     s_dependent_count;
+       struct list_head        s_sibling;
+       struct list_head        s_children;
+       struct list_head        s_links;
+       void                    * s_element;
+       int                     s_type;
+       umode_t                 s_mode;
+       struct dentry           * s_dentry;
+       struct iattr            * s_iattr;
+#ifdef CONFIG_LOCKDEP
+       int                     s_depth;
+#endif
+};
+
+static struct fsg_module_parameters mod_data = {
+       .stall = 1
+};
+
+FSG_MODULE_PARAMETERS(/* no prefix */, mod_data);
+
+static struct fsg_config config_struct;
+
+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;
+       }
+
+       /* Finalise */
+       cfg->can_stall = params->stall;
+}
+
+static inline void init_name(struct qstr *n, const char *s)
+{
+       n->name = s;
+       n->len = strlen(n->name);
+       n->hash = full_name_hash(n->name, n->len);
+}
+
+/*
+ * ATTENTION:
+ *
+ * to_item is "borrowed" from fs/configfs/configfs_internal.h.
+ *
+ * The adapters by default will be phased out, so this _should_ be acceptable.
+ */
+static inline struct config_item *to_item(struct dentry * dentry)
+{
+       struct configfs_dirent *sd = dentry->d_fsdata;
+       return ((struct config_item *) sd->s_element);
+}
+
+static inline struct config_item *mkdir(struct dentry *parent, const char *s,
+                                       struct dentry **new)
+{
+       struct qstr name;
+       struct config_item *ci;
+       int rc;
+
+       init_name(&name, s);
+       *new = d_alloc(parent, &name);
+       if (IS_ERR_OR_NULL(*new))
+               return ERR_PTR(-ENOMEM);
+
+       d_add(*new, NULL);
+       rc = ufg_mkdir(parent, *new);
+       if (rc) {
+               d_drop(*new);
+               dput(*new);
+
+               return ERR_PTR(rc);
+       }
+       dput(*new);
+
+       ci = to_item(*new);
+       if (!ci) {
+               ufg_rmdir(parent, *new);
+
+               return ERR_PTR(-ENODEV);
+       }
+       return ci;
+}
+
+static int do_store(struct config_item *ci, const char *a, const char *s, int 
n)
+{
+       struct configfs_attribute *attr;
+       int i;
+       bool found = false;
+
+       if (!ci)
+               return -ENODEV;
+
+       for (i = 0; (attr = ci->ci_type->ct_attrs[i]) != NULL; i++)
+               if (!strcmp(attr->ca_name, a)) {
+                       found = true;
+                       break;
+               }
+       if (!found)
+               return -ENODEV;
+       
+       return ci->ci_type->ct_item_ops->store_attribute(ci, attr, s, n);
+}
+
+static inline int str_store(struct config_item *ci, const char *a, const char 
*s)
+{
+       return do_store(ci, a, s, strlen(s));
+}
+
+static inline int formatted_store(struct config_item *ci, const char *a, int 
i, const char *f)
+{
+       char buf[UFG_STR_LEN];
+
+       snprintf(buf, UFG_STR_LEN, f, i);
+       return str_store(ci, a, buf);
+}
+
+static inline int hex_store(struct config_item *ci, const char *a, int i)
+{
+       return formatted_store(ci, a, i, "%x");
+}
+
+static inline int int_store(struct config_item *ci, const char *a, int i)
+{
+       return formatted_store(ci, a, i, "%d");
+}
+
+static int do_show(struct config_item *ci, const char *a, char *s)
+{
+       struct configfs_attribute *attr;
+       int i;
+       bool found = false;
+
+       if (!ci)
+               return -ENODEV;
+
+       for (i = 0; (attr = ci->ci_type->ct_attrs[i]) != NULL; i++)
+               if (!strcmp(attr->ca_name, a)) {
+                       found = true;
+                       break;
+               }
+       if (!found)
+               return -ENODEV;
+       
+       return ci->ci_type->ct_item_ops->show_attribute(ci, attr, s);
+}
+
+static struct device *configfs_prepare(struct fsg_config *config)
+{
+       struct config_item *gadget_ci, *conf_ci, *func_ci, *f_ci;
+       struct ufg_gadget_grp *gadget_grp;
+       struct ufg_dev *ufg_dev;
+       int rc, i;
+
+       if (!UFG_SUBSYSTEM)
+               return ERR_PTR(-ENODEV);
+
+       config->root = UFG_SUBSYSTEM->subsys.su_group.cg_item.ci_dentry;
+
+       /* mkdir the gadget and fill its attributes */
+       gadget_ci = mkdir(config->root, "msg", &config->gadget);
+       if (IS_ERR_OR_NULL(gadget_ci))
+               return ERR_PTR(PTR_ERR(gadget_ci));
+       rc = hex_store(gadget_ci, "idVendor", 0x0525);
+       if (rc < 0)
+               goto remove_gadget;
+       rc = hex_store(gadget_ci, "idProduct", 0xa4a5);
+       if (rc < 0)
+               goto remove_gadget;
+       rc = hex_store(gadget_ci, "bcdDevice", 0xff);
+       if (rc < 0)
+               goto remove_gadget;
+       rc = str_store(gadget_ci, "iManufacturer", "Linux");
+       if (rc < 0)
+               goto remove_gadget;
+       rc = str_store(gadget_ci, "iProduct",
+                 LUN(0).cdrom ? "File-Stor Gadget" : "File-CD Gadget");
+       if (rc < 0)
+               goto remove_gadget;
+
+       /* mkdir the config and fill its attributes */
+       conf_ci = mkdir(config->gadget, "conf", &config->conf);
+       if (IS_ERR_OR_NULL(conf_ci)) {
+               rc = PTR_ERR(conf_ci);
+               goto remove_gadget;
+       }
+       
+       /* mkdir the function and fill its attributes */
+       func_ci = mkdir(config->conf, "func", &config->func);
+       if (IS_ERR_OR_NULL(func_ci)) {
+               rc = PTR_ERR(func_ci);
+               goto remove_conf;
+       }
+       rc = str_store(func_ci, "name", "MassStorage");
+       if (rc < 0)
+               goto remove_func;
+       /* mkdir the mass storage function and fill its attributes */
+       f_ci = config_group_find_item(to_config_group(to_item(config->func)), 
"MassStorage");
+       if (IS_ERR_OR_NULL(f_ci)) {
+               rc = PTR_ERR(f_ci);
+               goto remove_func;
+       }
+       config->f = f_ci->ci_dentry;
+       rc = int_store(f_ci, "luns", config->nluns);
+       if (rc < 0)
+               goto remove_f;
+
+       for (i = 0; i < config->nluns; i++) {
+               struct config_item *lun_ci;
+               char buf[UFG_STR_LEN];
+
+               /* mkdir the lun and fill its attributes */
+               snprintf(buf, UFG_STR_LEN, "lun%d", i);
+               lun_ci = 
config_group_find_item(to_config_group(to_item(config->f)), buf);
+               if (IS_ERR_OR_NULL(lun_ci)) {
+                       rc = PTR_ERR(lun_ci);
+                       if (i--)
+                               goto remove_luns;
+                       else
+                               goto remove_f;
+               }
+               LUN(i).dentry = lun_ci->ci_dentry;
+               if (LUN(i).filename) {
+                       rc = str_store(lun_ci, "file", LUN(i).filename);
+                       if (rc < 0)
+                               goto remove_luns;
+               }
+               if (LUN(i).ro) {
+                       rc = int_store(lun_ci, "ro", 1);
+                       if (rc < 0)
+                               goto remove_luns;
+               }
+               if (LUN(i).nofua) {
+                       rc = int_store(lun_ci, "nofua", 1);
+                       if (rc < 0)
+                               goto remove_luns;
+               }
+               if (LUN(i).removable) {
+                       rc = int_store(lun_ci, "removable", 1);
+                       if (rc < 0)
+                               goto remove_luns;
+               }
+       }
+       i--;
+
+       rc = -ENODEV;
+       gadget_grp = to_ufg_gadget_grp(gadget_ci);
+       if (!gadget_grp)
+               goto remove_luns;
+       
+       rc = int_store(gadget_ci, "ready", 1);
+       if (rc < 0)
+               goto remove_luns;
+       ufg_dev = gadget_grp->gadget_grp_data;
+       if (!ufg_dev)
+               goto remove_luns;
+
+       return &ufg_dev->cdev->gadget->dev;
+
+remove_luns:
+       while (i >= 0)
+               ufg_rmdir(config->f, LUN(i--).dentry);
+
+remove_f:
+       ufg_rmdir(config->func, config->f);
+
+remove_func:
+       ufg_rmdir(config->conf, config->func);
+
+remove_conf:
+       ufg_rmdir(config->gadget, config->conf);
+
+remove_gadget:
+       ufg_rmdir(config->root, config->gadget);
+       return ERR_PTR(rc);
+}
+
+static void lun_release(struct device *dev)
+{
+}
+
+static inline struct fsg_lun_config *dev_to_fsg_lun_config(struct device *dev)
+{
+       return container_of(dev, struct fsg_lun_config, dev);
+}
+
+static ssize_t fsg_show(struct device *dev, struct device_attribute *attr,
+                       const char *configfs_name, char *buf)
+{
+       struct fsg_lun_config *lun = dev_to_fsg_lun_config(dev);
+       struct config_item *ci;
+
+       if (!lun)
+               return -ENODEV;
+
+       ci = to_item(lun->dentry);
+       if (!ci)
+               return -ENODEV;
+
+       return do_show(ci, configfs_name, buf);
+}
+
+static inline ssize_t fsg_show_ro(struct device *dev, struct device_attribute 
*attr,
+                          char *buf)
+{
+       return fsg_show(dev, attr, "ro", buf);
+}
+
+static inline ssize_t fsg_show_nofua(struct device *dev, struct 
device_attribute *attr,
+                             char *buf)
+{
+       return fsg_show(dev, attr, "nofua", buf);
+}
+
+static inline ssize_t fsg_show_file(struct device *dev, struct 
device_attribute *attr,
+                            char *buf)
+{
+       return fsg_show(dev, attr, "file", buf);
+}
+
+static ssize_t fsg_store(struct device *dev, struct device_attribute *attr,
+                        const char *configfs_name, const char *buf, size_t 
count)
+{
+       struct fsg_lun_config *lun = dev_to_fsg_lun_config(dev);
+       struct config_item *ci;
+
+       if (!lun)
+               return -ENODEV;
+
+       ci = to_item(lun->dentry);
+       if (!ci)
+               return -ENODEV;
+
+       return do_store(ci, configfs_name, buf, count);
+}
+
+static inline ssize_t fsg_store_ro(struct device *dev, struct device_attribute 
*attr,
+                           const char *buf, size_t count)
+{
+       return fsg_store(dev, attr, "ro", buf, count);
+}
+
+static inline ssize_t fsg_store_nofua(struct device *dev,
+                              struct device_attribute *attr,
+                              const char *buf, size_t count)
+{
+       return fsg_store(dev, attr, "nofua", buf, count);
+}
+
+static inline ssize_t fsg_store_file(struct device *dev, struct 
device_attribute *attr,
+                             const char *buf, size_t count)
+{
+       return fsg_store(dev, attr, "file", buf, count);
+}
+
+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);
+
+static void devices_teardown(struct fsg_config *config)
+{
+       int i = config->nluns;
+
+       while (i--) {
+               device_remove_file(&LUN(i).dev, &dev_attr_nofua);
+               device_remove_file(&LUN(i).dev,
+                                  LUN(i).cdrom
+                                ? &dev_attr_ro_cdrom
+                                : &dev_attr_ro);
+               device_remove_file(&LUN(i).dev,
+                                  LUN(i).removable
+                                ? &dev_attr_file
+                                : &dev_attr_file_nonremovable);
+               device_unregister(&LUN(i).dev);
+       }
+}
+
+static void configfs_teardown(struct fsg_config *config)
+{
+       struct config_item *ci;
+       int i = config->configfs_nluns;
+
+       ci = to_item(config->gadget);
+       if (ci)
+               int_store(ci, "ready", 0);
+       while (i--)     
+               ufg_rmdir(config->f, LUN(i).dentry);
+       
+       ufg_rmdir(config->func, config->f);
+       ufg_rmdir(config->conf, config->func);
+       ufg_rmdir(config->gadget, config->conf);
+       ufg_rmdir(config->root, config->gadget);
+}
+
+static int __init msg_init(void)
+{
+       struct device *gadget_dev;
+       struct fsg_config *config = &config_struct;
+       int i = 0, rc;
+
+       rc = request_module("usb_functions");
+       if (rc < 0)
+               return rc;
+       rc = try_module_get(UFG_MODULE);
+       if (rc < 0)
+               return rc;
+ 
+       fsg_config_from_params(config, &mod_data);
+       gadget_dev = configfs_prepare(config);
+       if (IS_ERR_OR_NULL(gadget_dev)) {
+               rc = PTR_ERR(gadget_dev);
+               goto error_configfs;
+       }
+       config->configfs_nluns = config->nluns;
+       for (i = 0; i < config->nluns; ++i) {
+               dev_set_name(&LUN(i).dev, "lun%d", i);
+               LUN(i).dev.release = lun_release;
+               LUN(i).dev.parent = gadget_dev;
+
+               rc = device_register(&LUN(i).dev);
+               if (rc) {
+                       pr_info("failed to register LUN%d: %d\n", i, rc);
+                       put_device(&LUN(i).dev);
+                       goto error_release;
+               }
+
+               rc = device_create_file(&LUN(i).dev,
+                                       LUN(i).cdrom
+                                     ? &dev_attr_ro_cdrom
+                                     : &dev_attr_ro);
+               if (rc)
+                       goto error_ro;
+               rc = device_create_file(&LUN(i).dev,
+                                       LUN(i).removable
+                                     ? &dev_attr_file
+                                     : &dev_attr_file_nonremovable);
+               if (rc)
+                       goto error_file;
+               rc = device_create_file(&LUN(i).dev, &dev_attr_nofua);
+               if (rc)
+                       goto error_nofua;
+
+               if (!LUN(i).filename && !LUN(i).removable) {
+                       pr_err("no file given for LUN%d\n", i);
+                       rc = -EINVAL;
+                       goto error_filename;
+               }
+       }
+
+       return 0;
+
+error_filename:
+       device_remove_file(&LUN(i).dev, &dev_attr_nofua);
+
+error_nofua:
+       device_remove_file(&LUN(i).dev,
+                          LUN(i).removable
+                        ? &dev_attr_file
+                        : &dev_attr_file_nonremovable);
+
+error_file:
+       device_remove_file(&LUN(i).dev,
+                          LUN(i).cdrom
+                        ? &dev_attr_ro_cdrom
+                        : &dev_attr_ro);
+
+error_ro:
+       device_unregister(&LUN(i).dev);
+
+error_release:
+       config->nluns = i;
+       devices_teardown(config);
+       configfs_teardown(config);
+
+error_configfs:
+       module_put(UFG_MODULE);
+       return rc;
+}
+module_init(msg_init);
+
+static void msg_cleanup(void)
+{
+       struct fsg_config *config = &config_struct;
+
+       devices_teardown(config);
+       configfs_teardown(config);
+       module_put(UFG_MODULE);
+}
+module_exit(msg_cleanup);
+
+MODULE_DESCRIPTION("Mass Storage USB Gadget to UFG adapter");
+MODULE_AUTHOR("Andrzej Pietrasiewicz");
+MODULE_LICENSE("GPL");
+
-- 
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