[RFCv4 PATCH 13/13] usb: gadget: ufg: add Mass Storage Gadget adapter to UFG

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux