[PATCH 2/3] usb: gadget: Add USB functions gadget

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

 



Add USB functions gadget configured entirely through configfs.
This is the base for adding USB functions to it. The next patch
in the series demonstrates how to add functions.

Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@xxxxxxxxxxx>
Signed-off-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx>
---
 drivers/usb/gadget/Kconfig         |   12 +
 drivers/usb/gadget/Makefile        |    2 +
 drivers/usb/gadget/usb_functions.c |  780 ++++++++++++++++++++++++++++++++++++
 3 files changed, 794 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/gadget/usb_functions.c

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index a53be32..0ccade5 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -521,6 +521,18 @@ choice
 
 # this first set of drivers all depend on bulk-capable hardware.
 
+config USB_FG
+	tristate "USB Functions Gadget (EXPERIMENTAL)"
+	select USB_LIBCOMPOSITE
+	depends on EXPERIMENTAL && CONFIGFS_FS
+	help
+	  USB Functions Gadget is a device which aggregates a number of
+	  USB functions. The gadget is composed by userspace through a
+	  configfs interface, which enables specifying what USB
+	  configurations the gadget is composed of, what USB functions
+	  a USB configuration is composed of and enabling/disabling
+	  the gadget.
+
 config USB_ZERO
 	tristate "Gadget Zero (DEVELOPMENT)"
 	select USB_LIBCOMPOSITE
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 307be5f..0f8b421 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_USB_MV_U3D)	+= mv_u3d_core.o
 #
 # USB gadget drivers
 #
+g_usb_functions-y		:= usb_functions.o
 g_zero-y			:= zero.o
 g_audio-y			:= audio.o
 g_ether-y			:= ether.o
@@ -57,6 +58,7 @@ g_ncm-y				:= ncm.o
 g_acm_ms-y			:= acm_ms.o
 g_tcm_usb_gadget-y		:= tcm_usb_gadget.o
 
+obj-$(CONFIG_USB_FG)		+= g_usb_functions.o
 obj-$(CONFIG_USB_ZERO)		+= g_zero.o
 obj-$(CONFIG_USB_AUDIO)		+= g_audio.o
 obj-$(CONFIG_USB_ETH)		+= g_ether.o
diff --git a/drivers/usb/gadget/usb_functions.c b/drivers/usb/gadget/usb_functions.c
new file mode 100644
index 0000000..ae15719
--- /dev/null
+++ b/drivers/usb/gadget/usb_functions.c
@@ -0,0 +1,780 @@
+/*
+ * USB Functions Gadget
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * Author: Andrzej Pietrasiewicz <andrzej.p@xxxxxxxxxxx>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb/ch9.h>
+#include <linux/configfs.h>
+
+/*
+ * Supported functions
+ */
+
+/*-------------------------------------------------------------------------*/
+
+#define DRIVER_DESC		"USB Functions Gadget"
+#define DRIVER_VERSION		"Pawelek"
+
+#define VENDOR_ID		0x1d6b /* Linux Foundation */
+#define PRODUCT_ID		0x0108
+#define BCD_DEVICE		0xffff
+
+USB_GADGET_COMPOSITE_OPTIONS();
+
+/*-----------  helper functions and macros for configfs support  ----------*/
+
+#define UFG_STR_LEN		256
+
+#define UFG_SHOW_USHORT_ATTR(_name, _type, _member)			\
+static ssize_t _type##_show_##_name(struct _type *grp, char *buf)	\
+{									\
+	return sprintf(buf, "%d\n", grp->_member);			\
+}
+
+#define UFG_STORE_USHORT_ATTR(_name, _type, _member)			\
+static ssize_t _type##_store_##_name(struct _type *grp,			\
+				     const char *buf, size_t count)	\
+{									\
+	grp->_member = (ushort)ufg_read_twobyte(buf);			\
+	grp->_member##_set = true;					\
+									\
+	return count;							\
+}
+
+#define UFG_SHOW_STR_ATTR(_name, _type, _member)			\
+static ssize_t _type##_show_##_name(struct _type *grp, char *buf)	\
+{									\
+	return strlcpy(buf, grp->_member, UFG_STR_LEN);			\
+}
+
+#define UFG_STORE_STR_ATTR(_name, _type, _member)			\
+static ssize_t _type##_store_##_name(struct _type *grp,			\
+				      const char *buf, size_t count)	\
+{									\
+	strlcpy(grp->_member, buf, UFG_STR_LEN);			\
+	grp->_member##_set = true;					\
+									\
+	return count;							\
+}
+
+#define UFG_ATTR_RW(_name, _attr, _type)				\
+static struct _type##_attribute _type##_##_name =			\
+	__CONFIGFS_ATTR(_attr, S_IRUGO | S_IWUSR, _type##_show_##_name,	\
+			_type##_store_##_name)
+
+#define UFG_ATTR_RO(_name, _attr, _type)				\
+static struct _type##_attribute _type##_##_name =			\
+	__CONFIGFS_ATTR(_attr, S_IRUGO | S_IWUSR, _type##_show_##_name, NULL)
+
+typedef struct config_group *(*make_group_fn)(struct config_group *group,
+					      const char *name);
+typedef int (*bind_function_fn)(struct usb_configuration *c,
+				struct config_item *item, void *data);
+
+static ssize_t ufg_read_twobyte(const char *buf)
+{
+	unsigned long tmp;
+	int res;
+	char *p = (char *)buf;
+
+	res = kstrtoul(p, 10, &tmp);
+	if (res)
+		return -EINVAL;
+
+	if (tmp > 16383)
+		return -ERANGE;
+
+	return tmp;
+}
+
+/*--------------------  handling of dynamic make_group --------------------*/
+
+struct ufg_fn {
+	const char		*name;
+	struct config_group	*group;
+	make_group_fn		make_group;
+	bind_function_fn	bind;
+};
+
+static struct ufg_fn *available_functions[] = {
+	NULL,
+};
+
+struct ufg_fn *ufg_fn_by_name(const char *name)
+{
+	struct ufg_fn **f;
+
+	f = available_functions;
+
+	while (*f) {
+		if (sysfs_streq((*f)->name, name))
+			return *f;
+		f++;
+	}
+
+	return NULL;
+}
+
+struct ufg_fn *ufg_fn_by_group(struct config_group *group)
+{
+	struct ufg_fn **f;
+
+	f = available_functions;
+
+	while (*f) {
+		if ((*f)->group == group)
+			return *f;
+		f++;
+	}
+
+	return NULL;
+}
+
+static struct config_group *ufg_make_function_subgroup(
+	struct config_group *group, const char *name)
+{
+	struct ufg_fn *fn;
+
+	fn = ufg_fn_by_name(name);
+	if (!fn || !fn->make_group)
+		return ERR_PTR(-EINVAL);
+
+	if (fn->group)
+		return ERR_PTR(-EBUSY);
+
+	fn->group = group;
+
+	return fn->make_group(group, name);
+}
+
+/*------------------  USB function-level configfs group  ------------------*/
+
+#define UFG_FUNC_NAME_LEN	UFG_STR_LEN
+
+struct ufg_function_grp {
+	/* This group needs children */
+	struct config_group group;
+
+	/* attributes' values */
+	char name[UFG_FUNC_NAME_LEN];
+};
+
+UFG_SHOW_STR_ATTR(name, ufg_function_grp, name);
+
+static ssize_t ufg_function_grp_store_name(struct ufg_function_grp *grp,
+					   const char *buf, size_t count)
+{
+	struct ufg_fn *fn;
+
+	fn = ufg_fn_by_name(buf);
+	if (!fn)
+		return -EINVAL;
+
+	strlcpy(grp->name, buf, UFG_FUNC_NAME_LEN);
+
+	return count;
+}
+
+CONFIGFS_ATTR_STRUCT(ufg_function_grp);
+
+UFG_ATTR_RW(name, name, ufg_function_grp);
+
+static struct configfs_attribute *ufg_function_grp_attrs[] = {
+	&ufg_function_grp_name.attr,
+	NULL,
+};
+
+static struct ufg_function_grp *to_ufg_function_grp(struct config_item *item)
+{
+	return item ? container_of(to_config_group(item),
+				   struct ufg_function_grp, group) : NULL;
+}
+
+CONFIGFS_ATTR_OPS(ufg_function_grp);
+
+static void ufg_function_grp_release(struct config_item *item)
+{
+	kfree(to_ufg_function_grp(item));
+}
+
+
+static struct configfs_item_operations ufg_function_grp_item_ops = {
+	.show_attribute		= ufg_function_grp_attr_show,
+	.store_attribute	= ufg_function_grp_attr_store,
+	.release		= ufg_function_grp_release,
+};
+
+static struct configfs_group_operations ufg_function_grp_group_ops = {
+	.make_group	= ufg_make_function_subgroup,
+};
+
+static struct config_item_type ufg_function_grp_type = {
+	.ct_attrs	= ufg_function_grp_attrs,
+	.ct_item_ops	= &ufg_function_grp_item_ops,
+	.ct_group_ops	= &ufg_function_grp_group_ops,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct config_group *make_ufg_function(struct config_group *group,
+					      const char *name)
+{
+	struct ufg_function_grp *function;
+
+	function = kzalloc(sizeof(*function), GFP_KERNEL);
+	if (!function)
+		return ERR_PTR(-ENOMEM);
+
+	config_group_init_type_name(&function->group, name,
+				    &ufg_function_grp_type);
+
+	return &function->group;
+}
+
+/*---------------  USB configuration-level configfs group  ----------------*/
+
+struct ufg_config_grp {
+	/* This group needs children */
+	struct config_group group;
+
+	bool	added;
+
+	/* attributes' values */
+	ushort	max_power;
+	bool	max_power_set;
+	ushort	num_interfaces;
+	ushort	conf_number;
+};
+
+UFG_SHOW_USHORT_ATTR(max_power, ufg_config_grp, max_power);
+UFG_STORE_USHORT_ATTR(max_power, ufg_config_grp, max_power);
+
+UFG_SHOW_USHORT_ATTR(num_interfaces, ufg_config_grp, num_interfaces);
+
+UFG_SHOW_USHORT_ATTR(conf_number, ufg_config_grp, conf_number);
+
+CONFIGFS_ATTR_STRUCT(ufg_config_grp);
+
+UFG_ATTR_RW(max_power, maximum_power, ufg_config_grp);
+
+UFG_ATTR_RO(num_interfaces, number_of_interfaces, ufg_config_grp);
+
+UFG_ATTR_RO(conf_number, configuration_number, ufg_config_grp);
+
+static struct configfs_attribute *ufg_config_grp_attrs[] = {
+	&ufg_config_grp_max_power.attr,
+	&ufg_config_grp_num_interfaces.attr,
+	&ufg_config_grp_conf_number.attr,
+	NULL,
+};
+
+static struct ufg_config_grp *to_ufg_config_grp(struct config_item *item)
+{
+	return item ? container_of(to_config_group(item), struct ufg_config_grp,
+				   group) : NULL;
+}
+
+CONFIGFS_ATTR_OPS(ufg_config_grp);
+
+static void ufg_config_grp_release(struct config_item *item)
+{
+	kfree(to_ufg_config_grp(item));
+}
+
+static struct configfs_item_operations ufg_config_grp_item_ops = {
+	.show_attribute		= ufg_config_grp_attr_show,
+	.store_attribute	= ufg_config_grp_attr_store,
+	.release		= ufg_config_grp_release,
+};
+
+static struct configfs_group_operations ufg_config_grp_group_ops = {
+	.make_group	= make_ufg_function,
+};
+
+static struct config_item_type ufg_config_grp_type = {
+	.ct_attrs	= ufg_config_grp_attrs,
+	.ct_item_ops	= &ufg_config_grp_item_ops,
+	.ct_group_ops	= &ufg_config_grp_group_ops,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct config_group *make_ufg_config(struct config_group *group,
+					   const char *name)
+{
+	struct ufg_config_grp *config;
+
+	config = kzalloc(sizeof(*config), GFP_KERNEL);
+	if (!config)
+		return ERR_PTR(-ENOMEM);
+
+	config_group_init_type_name(&config->group, name, &ufg_config_grp_type);
+
+	return &config->group;
+}
+
+/*------------------  USB gadget-level configfs group  --------------------*/
+
+#define UFG_NAME_LEN		UFG_STR_LEN
+
+struct ufg_gadget_grp {
+	/* This group needs children */
+	struct config_group group;
+
+	/* attributes' values */
+	ushort	idVendor;
+	bool	idVendor_set;
+	ushort	idProduct;
+	bool	idProduct_set;
+	ushort	bcdDevice;
+	bool	bcdDevice_set;
+	char	iManufacturer[UFG_NAME_LEN];
+	bool	iManufacturer_set;
+	char	iProduct[UFG_NAME_LEN];
+	bool	iProduct_set;
+	char	iSerialNumber[UFG_NAME_LEN];
+	bool	iSerialNumber_set;
+	bool	connect;
+
+	void	*gadget_grp_data;
+};
+
+static int ufg_gadget_bind(struct ufg_gadget_grp *group);
+
+UFG_SHOW_USHORT_ATTR(id_vendor, ufg_gadget_grp, idVendor);
+UFG_STORE_USHORT_ATTR(id_vendor, ufg_gadget_grp, idVendor);
+
+UFG_SHOW_USHORT_ATTR(id_product, ufg_gadget_grp, idProduct);
+UFG_STORE_USHORT_ATTR(id_product, ufg_gadget_grp, idProduct);
+
+UFG_SHOW_USHORT_ATTR(bcd_device, ufg_gadget_grp, bcdDevice);
+UFG_STORE_USHORT_ATTR(bcd_device, ufg_gadget_grp, bcdDevice);
+
+UFG_SHOW_STR_ATTR(i_manufacturer, ufg_gadget_grp, iManufacturer);
+UFG_STORE_STR_ATTR(i_manufacturer, ufg_gadget_grp, iManufacturer);
+
+UFG_SHOW_STR_ATTR(i_product, ufg_gadget_grp, iProduct);
+UFG_STORE_STR_ATTR(i_product, ufg_gadget_grp, iProduct);
+
+UFG_SHOW_STR_ATTR(i_serial_number, ufg_gadget_grp, iSerialNumber);
+UFG_STORE_STR_ATTR(i_serial_number, ufg_gadget_grp, iSerialNumber);
+
+UFG_SHOW_USHORT_ATTR(connect, ufg_gadget_grp, connect);
+static ssize_t ufg_gadget_grp_store_connect(struct ufg_gadget_grp *grp,
+					    const char *buf, size_t count)
+{
+	bool connect;
+	int ret;
+
+	if (buf[0] != '0' && buf[0] != '1')
+		return -EINVAL;
+
+	connect = grp->connect;
+	grp->connect = buf[0] == '1';
+
+	if (connect && grp->connect)
+		return -EBUSY;
+
+	ret = ufg_gadget_bind(grp);
+	if (ret) {
+		grp->connect = connect;
+		return ret;
+	}
+
+	return count;
+}
+
+CONFIGFS_ATTR_STRUCT(ufg_gadget_grp);
+
+UFG_ATTR_RW(id_vendor, idVendor, ufg_gadget_grp);
+UFG_ATTR_RW(id_product, idProduct, ufg_gadget_grp);
+UFG_ATTR_RW(bcd_device, bcdDevice, ufg_gadget_grp);
+UFG_ATTR_RW(i_manufacturer, iManufacturer, ufg_gadget_grp);
+UFG_ATTR_RW(i_product, iProduct, ufg_gadget_grp);
+UFG_ATTR_RW(i_serial_number, iSerialNumber, ufg_gadget_grp);
+UFG_ATTR_RW(connect, connect, ufg_gadget_grp);
+
+static struct configfs_attribute *ufg_gadget_grp_attrs[] = {
+	&ufg_gadget_grp_id_vendor.attr,
+	&ufg_gadget_grp_id_product.attr,
+	&ufg_gadget_grp_bcd_device.attr,
+	&ufg_gadget_grp_i_manufacturer.attr,
+	&ufg_gadget_grp_i_product.attr,
+	&ufg_gadget_grp_i_serial_number.attr,
+	&ufg_gadget_grp_connect.attr,
+	NULL,
+};
+
+static struct ufg_gadget_grp *to_ufg_gadget_grp(struct config_item *item)
+{
+	return item ? container_of(to_config_group(item), struct ufg_gadget_grp,
+				   group) : NULL;
+}
+
+CONFIGFS_ATTR_OPS(ufg_gadget_grp);
+
+static void ufg_gadget_grp_release(struct config_item *item)
+{
+	kfree(to_ufg_gadget_grp(item));
+}
+
+static struct configfs_item_operations ufg_gadget_grp_item_ops = {
+	.show_attribute		= ufg_gadget_grp_attr_show,
+	.store_attribute	= ufg_gadget_grp_attr_store,
+	.release		= ufg_gadget_grp_release,
+};
+
+static struct configfs_group_operations ufg_gadget_grp_group_ops = {
+	.make_group	= make_ufg_config,
+};
+
+static struct config_item_type ufg_gadget_grp_type = {
+	.ct_attrs	= ufg_gadget_grp_attrs,
+	.ct_item_ops	= &ufg_gadget_grp_item_ops,
+	.ct_group_ops	= &ufg_gadget_grp_group_ops,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct config_group *make_ufg_gadget(struct config_group *group,
+					   const char *name)
+{
+	struct ufg_gadget_grp *gadget;
+
+	gadget = kzalloc(sizeof(*gadget), GFP_KERNEL);
+	if (!gadget)
+		return ERR_PTR(-ENOMEM);
+
+	config_group_init_type_name(&gadget->group, name, &ufg_gadget_grp_type);
+
+	return &gadget->group;
+}
+
+/*-------------------  configfs subsystem for ufg  ------------------------*/
+
+#define UFG_FUNC_NAMES_BUF_LEN	UFG_STR_LEN
+
+struct ufg_subsys {
+	/* This is the root of the subsystem */
+	struct configfs_subsystem subsys;
+
+	/* attributes' values */
+	char	avail_func[UFG_FUNC_NAMES_BUF_LEN];
+};
+
+static ssize_t ufg_subsys_show_avail_func(struct ufg_subsys *grp, char *buf)
+{
+	return sprintf(buf, "%s\n", grp->avail_func);
+}
+
+CONFIGFS_ATTR_STRUCT(ufg_subsys);
+
+UFG_ATTR_RO(avail_func, available_functions, ufg_subsys);
+
+static struct configfs_attribute *ufg_subsys_attrs[] = {
+	&ufg_subsys_avail_func.attr,
+	NULL,
+};
+
+static struct ufg_subsys *to_ufg_subsys(struct config_item *item)
+{
+	return item ? container_of(to_configfs_subsystem(to_config_group(item)),
+				   struct ufg_subsys, subsys) : NULL;
+}
+
+CONFIGFS_ATTR_OPS(ufg_subsys);
+
+static struct configfs_item_operations ufg_subsys_item_ops = {
+	.show_attribute = ufg_subsys_attr_show,
+};
+
+static struct configfs_group_operations ufg_subsys_group_ops = {
+	.make_group	= make_ufg_gadget,
+};
+
+static struct config_item_type ufg_subsys_type = {
+	.ct_attrs	= ufg_subsys_attrs,
+	.ct_item_ops	= &ufg_subsys_item_ops,
+	.ct_group_ops	= &ufg_subsys_group_ops,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct ufg_subsys ufg_subsystem = {
+	.subsys = {
+		.su_group = {
+			.cg_item = {
+				.ci_namebuf	= "usb-function-gadget",
+				.ci_type	= &ufg_subsys_type,
+			},
+		},
+	},
+};
+
+/*-------------------  USB composite handling code  -----------------------*/
+
+static const char longname[] = "USB Functions Gadget";
+static const char serial[] = "0123456789.0123456789.0123456789";
+static struct usb_string strings_dev[] = {
+	[USB_GADGET_MANUFACTURER_IDX].s = "",
+	[USB_GADGET_PRODUCT_IDX].s = longname,
+	[USB_GADGET_SERIAL_IDX].s = serial,
+	{  }			/* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+	.language	= 0x0409,	/* en-us */
+	.strings	= strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+	&stringtab_dev,
+	NULL,
+};
+
+static int ufg_bind(struct usb_composite_dev *cdev);
+
+
+struct ufg_dev {
+	struct usb_device_descriptor	ufg_device_desc;
+	struct usb_composite_driver	ufg_driver;
+	struct usb_composite_dev	*cdev; /* rename to tmp_... */
+	struct usb_configuration	*config; /* rename to tmp_... */
+};
+
+static void fill_device_desc(struct usb_device_descriptor *udd)
+{
+	udd->bLength              = sizeof(udd);
+	udd->bDescriptorType      = USB_DT_DEVICE;
+	udd->bcdUSB               = __constant_cpu_to_le16(0x0200);
+	udd->bDeviceClass         = USB_CLASS_PER_INTERFACE;
+	udd->idVendor             = __constant_cpu_to_le16(VENDOR_ID);
+	udd->idProduct            = __constant_cpu_to_le16(PRODUCT_ID);
+	udd->bcdDevice            = __constant_cpu_to_le16(BCD_DEVICE);
+	udd->bNumConfigurations   = 1;
+}
+
+static void fill_ufg_driver(struct usb_composite_driver *ufgd,
+			    struct ufg_gadget_grp *grp)
+{
+	struct usb_device_descriptor *desc;
+
+	desc = &container_of(ufgd, struct ufg_dev, ufg_driver)->ufg_device_desc;
+	fill_device_desc(desc);
+	/* TODO: copy something from grp */
+
+	ufgd->bind		= ufg_bind;
+	ufgd->strings		= dev_strings;
+	ufgd->name		= "usb_function_gadget";
+	ufgd->dev		= desc;
+	ufgd->needs_serial	= 1;
+}
+
+static void ufg_unbind_config(struct usb_configuration *c);
+
+static void fill_config_driver(struct usb_configuration *ucd)
+{
+	ucd->label		= "ufg";
+	ucd->unbind		= ufg_unbind_config;
+	ucd->bConfigurationValue = 1;
+	ucd->bmAttributes	= USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER;
+	ucd->bMaxPower		= 0xFA; /* 500ma */
+}
+
+static int ufg_bind(struct usb_composite_dev *cdev)
+{
+	struct ufg_dev		*ufg_dev;
+	int			gcnum;
+	struct usb_gadget	*gadget = cdev->gadget;
+	int			status;
+
+	ufg_dev = container_of(cdev->driver, struct ufg_dev, ufg_driver);
+	ufg_dev->cdev = cdev;
+	status = usb_string_ids_tab(cdev, strings_dev);
+	if (status < 0)
+		return status;
+
+	ufg_dev->ufg_device_desc.iManufacturer =
+		strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+	ufg_dev->ufg_device_desc.iProduct =
+		strings_dev[USB_GADGET_PRODUCT_IDX].id;
+	ufg_dev->ufg_device_desc.iSerialNumber =
+		strings_dev[USB_GADGET_SERIAL_IDX].id;
+
+	/*
+	 * Start disconnected. Userspace will connect the gadget once
+	 * it is done configuring the functions.
+	 */
+	usb_gadget_disconnect(gadget);
+
+	gcnum = get_default_bcdDevice();
+	if (gcnum >= 0)
+		ufg_dev->ufg_device_desc.bcdDevice =
+			cpu_to_le16(0x0200 + gcnum);
+	else {
+		pr_warn("%s: controller '%s' not recognized\n",
+			DRIVER_DESC, gadget->name);
+		ufg_dev->ufg_device_desc.bcdDevice =
+			__constant_cpu_to_le16(0x9999);
+	}
+
+	usb_gadget_set_selfpowered(gadget);
+
+	usb_composite_overwrite_options(cdev, &coverwrite);
+	pr_info("%s: version: %s\n", DRIVER_DESC, DRIVER_VERSION);
+
+	return 0;
+}
+
+static int ufg_bind_config(struct usb_configuration *c)
+{
+	struct ufg_dev		*ufg_dev;
+
+	ufg_dev = container_of(c->cdev->driver, struct ufg_dev, ufg_driver);
+	ufg_dev->config = c;
+
+	return 0;
+}
+
+static void ufg_unbind_config(struct usb_configuration *c)
+{
+	kfree(c);
+}
+
+static void update_ufg_driver(struct ufg_gadget_grp *ufg_gadget)
+{
+	if (ufg_gadget->idVendor_set)
+		coverwrite.idVendor = ufg_gadget->idVendor;
+	if (ufg_gadget->idProduct_set)
+		coverwrite.idProduct = ufg_gadget->idProduct;
+	if (ufg_gadget->bcdDevice_set)
+		coverwrite.bcdDevice = ufg_gadget->bcdDevice;
+	if (ufg_gadget->iManufacturer_set)
+		coverwrite.manufacturer = ufg_gadget->iManufacturer;
+	if (ufg_gadget->iProduct_set)
+		coverwrite.product = ufg_gadget->iProduct;
+	if (ufg_gadget->iSerialNumber_set)
+		coverwrite.serial_number = ufg_gadget->iSerialNumber;
+}
+
+static int ufg_gadget_bind(struct ufg_gadget_grp *ufg_gadget)
+{
+	struct ufg_dev *ufg_dev;
+	struct config_item *ci;
+	struct usb_configuration *ufg_config;
+	int r;
+
+	if (!ufg_gadget->connect) {
+		ufg_dev = ufg_gadget->gadget_grp_data;
+		usb_composite_unregister(&ufg_dev->ufg_driver);
+		kfree(ufg_dev);
+		return 0;
+	}
+
+	ufg_dev = kzalloc(sizeof(*ufg_dev), GFP_KERNEL);
+	if (!ufg_dev)
+		return -ENOMEM;
+
+	fill_ufg_driver(&ufg_dev->ufg_driver, ufg_gadget);
+	update_ufg_driver(ufg_gadget);
+	ufg_gadget->gadget_grp_data = ufg_dev;
+	r = usb_composite_probe(&ufg_dev->ufg_driver);
+	if (r)
+		goto release;
+
+	list_for_each_entry(ci, &ufg_gadget->group.cg_children, ci_entry) {
+		struct ufg_config_grp *config;
+		struct config_item *f;
+
+		config = to_ufg_config_grp(ci);
+
+		ufg_config = kzalloc(sizeof(*ufg_config), GFP_KERNEL);
+		if (!ufg_config)
+			goto unbind;
+		fill_config_driver(ufg_config);
+
+		ufg_config->bMaxPower = config->max_power;
+		r = usb_add_config(ufg_dev->cdev, ufg_config, ufg_bind_config);
+		if (r)
+			goto unbind;
+		config->added = true;
+
+		list_for_each_entry(f, &config->group.cg_children, ci_entry) {
+			struct ufg_fn *ufg_fn;
+
+			ufg_fn = ufg_fn_by_group(to_config_group(f));
+
+			if (ufg_fn && ufg_fn->bind) {
+				struct config_item *subgroup;
+				struct config_group *group;
+
+				group = to_config_group(f);
+				subgroup = container_of(group->cg_children.next,
+						struct config_item, ci_entry);
+				r = ufg_fn->bind(ufg_dev->config, subgroup,
+						 ufg_dev->cdev);
+				if (r)
+					goto unbind;
+			}
+		}
+
+	}
+
+	return 0;
+
+unbind:
+	list_for_each_entry(ci, &ufg_gadget->group.cg_children, ci_entry) {
+		struct ufg_config_grp *config;
+
+		config = to_ufg_config_grp(ci);
+		if (!config->added)
+			continue;
+		usb_remove_config(ufg_dev->cdev, ufg_config);
+	}
+	usb_composite_unregister(&ufg_dev->ufg_driver);
+release:
+	kfree(ufg_dev);
+	return r;
+}
+
+
+/*----------------------  general module stuff  ---------------------------*/
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Andrzej Pietrasiewicz");
+MODULE_LICENSE("GPL");
+
+static int __init ufg_init(void)
+{
+	int ret;
+
+	config_group_init(&ufg_subsystem.subsys.su_group);
+	mutex_init(&ufg_subsystem.subsys.su_mutex);
+	ret = configfs_register_subsystem(&ufg_subsystem.subsys);
+	if (ret)
+		goto unregister;
+
+	return 0;
+
+unregister:
+	configfs_unregister_subsystem(&ufg_subsystem.subsys);
+
+	return ret;
+}
+module_init(ufg_init);
+
+static void ufg_cleanup(void)
+{
+	configfs_unregister_subsystem(&ufg_subsystem.subsys);
+}
+module_exit(ufg_cleanup);
-- 
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