Demonstrate a USB gadget configured entirely through configfs.
Signed-off-by: Andrzej Pietrasiewicz <[email protected]>
Signed-off-by: Kyungmin Park <[email protected]>
---
Documentation/usb/ufg.txt | 2 +
drivers/usb/gadget/Kconfig | 13 +
drivers/usb/gadget/Makefile | 5 +-
drivers/usb/gadget/udc-core.c | 27 +-
drivers/usb/gadget/usb_functions.c | 1142 ++++++++++++++++++++++++++++++++++++
drivers/usb/gadget/usb_functions.h | 188 ++++++
include/linux/usb/gadget.h | 5 +
7 files changed, 1379 insertions(+), 3 deletions(-)
create mode 100644 Documentation/usb/ufg.txt
create mode 100644 drivers/usb/gadget/usb_functions.c
create mode 100644 drivers/usb/gadget/usb_functions.h
diff --git a/Documentation/usb/ufg.txt b/Documentation/usb/ufg.txt
new file mode 100644
index 0000000..c4691e0
--- /dev/null
+++ b/Documentation/usb/ufg.txt
@@ -0,0 +1,2 @@
+USB Functions Gadget
+
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 14625fd..80ab9a5 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -15,6 +15,7 @@
menuconfig USB_GADGET
tristate "USB Gadget Support"
+ select USB_LIBCOMPOSITE
select NLS
help
USB is a master/slave protocol, organized with one master
@@ -521,6 +522,18 @@ choice
# this first set of drivers all depend on bulk-capable hardware.
+config USB_FG
+ tristate "USB Functions Gadget"
+ depends on 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.
+ For more information, see Documentation/usb/ufg.txt
+
config USB_ZERO
tristate "Gadget Zero (DEVELOPMENT)"
select USB_LIBCOMPOSITE
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index fef41f5..378296b 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -3,10 +3,11 @@
#
ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG
-obj-$(CONFIG_USB_GADGET) += udc-core.o
+obj-$(CONFIG_USB_GADGET) += udc.o
obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o
libcomposite-y := usbstring.o config.o epautoconf.o
-libcomposite-y += composite.o functions.o
+libcomposite-y += composite.o
+udc-y += udc-core.o functions.o usb_functions.o
obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o
obj-$(CONFIG_USB_NET2272) += net2272.o
obj-$(CONFIG_USB_NET2280) += net2280.o
diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c
index 4d90a80..5d5bf37 100644
--- a/drivers/usb/gadget/udc-core.c
+++ b/drivers/usb/gadget/udc-core.c
@@ -40,6 +40,7 @@
struct usb_udc {
struct usb_gadget_driver *driver;
struct usb_gadget *gadget;
+ struct config_group *group;
struct device dev;
struct list_head list;
};
@@ -196,6 +197,7 @@ static void usb_udc_release(struct device *dev)
}
static const struct attribute_group *usb_udc_attr_groups[];
+
/**
* usb_add_gadget_udc - adds a new gadget to the udc class driver list
* @parent: the parent device to this udc. Usually the controller
@@ -231,6 +233,7 @@ int usb_add_gadget_udc(struct device *parent, struct
usb_gadget *gadget)
if (ret)
goto err3;
+ udc->group = udc_configfs_register(&udc->dev);
mutex_unlock(&udc_lock);
return 0;
@@ -305,6 +308,7 @@ found:
usb_gadget_remove_driver(udc);
kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE);
+ udc_configfs_unregister(udc->group);
device_unregister(&udc->dev);
}
EXPORT_SYMBOL_GPL(usb_del_gadget_udc);
@@ -504,6 +508,8 @@ static int usb_udc_uevent(struct device *dev, struct
kobj_uevent_env *env)
static int __init usb_udc_init(void)
{
+ int rc = 0;
+
udc_class = class_create(THIS_MODULE, "udc");
if (IS_ERR(udc_class)) {
pr_err("failed to create udc class --> %ld\n",
@@ -512,12 +518,31 @@ static int __init usb_udc_init(void)
}
udc_class->dev_uevent = usb_udc_uevent;
- return 0;
+
+#ifdef MODULE
+ rc = ufg_init();
+ if (rc)
+ class_destroy(udc_class);
+#endif
+
+ return rc;
}
subsys_initcall(usb_udc_init);
+#ifndef MODULE
+static int __init usb_udc_init_module(void)
+{
+ if (IS_ERR(udc_class))
+ return PTR_ERR(udc_class);
+
+ return ufg_init();
+}
+module_init(usb_udc_init_module);
+#endif
+
static void __exit usb_udc_exit(void)
{
+ ufg_cleanup();
class_destroy(udc_class);
}
module_exit(usb_udc_exit);
diff --git a/drivers/usb/gadget/usb_functions.c
b/drivers/usb/gadget/usb_functions.c
new file mode 100644
index 0000000..7f52eda
--- /dev/null
+++ b/drivers/usb/gadget/usb_functions.c
@@ -0,0 +1,1142 @@
+/*
+ * USB Functions Gadget
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * Author: Andrzej Pietrasiewicz <[email protected]>
+ *
+ * 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/usb/composite.h>
+#include <linux/configfs.h>
+#include <linux/dcache.h>
+#include <linux/fs.h>
+
+#include "usb_functions.h"
+
+#define ufg_for_each_child(cursor, grp) \
+ list_for_each_entry((cursor), &(grp)->cg_children, ci_entry)
+
+#define ufg_for_each_child_safe(cursor, tmp, grp) \
+ list_for_each_entry_safe((cursor), (tmp), &(grp)->cg_children, ci_entry)
+
+#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) \
+{ \
+ u16 tmp; \
+ int res; \
+ char *p = (char *)buf; \
+ \
+ res = kstrtou16(p, 16, &tmp); \
+ if (res < 0) \
+ return res; \
+ grp->_member = (ushort)tmp; \
+ 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) \
+{ \
+ const char *end = strchr(buf, '\n'); \
+ char *tmp; \
+ \
+ if (!end) \
+ end = buf + count; \
+ tmp = kzalloc(end - buf + 1, GFP_KERNEL); \
+ if (!tmp) \
+ return -ENOMEM; \
+ kfree(grp->_member); \
+ grp->_member = tmp; \
+ memcpy(grp->_member, buf, end - buf); \
+ grp->_member[end - buf] = 0; \
+ \
+ 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)
+
+/*
+ * USB endpoint-level configfs group
+ *
+ *
/usb-function-gadget/gadgets/<gadget>/functions/<name>.#/interface#/endpoint#
+ *
+ */
+
+UFG_SHOW_USHORT_ATTR(endpoint_address, ufg_endpoint_grp, endpoint_address);
+UFG_SHOW_USHORT_ATTR(attributes, ufg_endpoint_grp, attributes);
+UFG_SHOW_USHORT_ATTR(max_packet_sz, ufg_endpoint_grp, max_packet_sz);
+UFG_SHOW_USHORT_ATTR(interval, ufg_endpoint_grp, interval);
+
+CONFIGFS_ATTR_STRUCT(ufg_endpoint_grp);
+
+UFG_ATTR_RO(endpoint_address, endpoint_address, ufg_endpoint_grp);
+UFG_ATTR_RO(attributes, attributes, ufg_endpoint_grp);
+UFG_ATTR_RO(max_packet_sz, max_packet_size, ufg_endpoint_grp);
+UFG_ATTR_RO(interval, interval, ufg_endpoint_grp);
+
+static struct configfs_attribute *ufg_endpoint_grp_attrs[] = {
+ &ufg_endpoint_grp_endpoint_address.attr,
+ &ufg_endpoint_grp_attributes.attr,
+ &ufg_endpoint_grp_max_packet_sz.attr,
+ &ufg_endpoint_grp_interval.attr,
+ NULL,
+};
+
+CONFIGFS_ATTR_OPS(ufg_endpoint_grp);
+
+static void ufg_endpoint_grp_release(struct config_item *item)
+{
+ kfree(to_ufg_endpoint_grp(item));
+}
+
+static struct configfs_item_operations ufg_endpoint_grp_item_ops = {
+ .show_attribute = ufg_endpoint_grp_attr_show,
+ .release = ufg_endpoint_grp_release,
+};
+
+static struct config_item_type ufg_endpoint_grp_type = {
+ .ct_attrs = ufg_endpoint_grp_attrs,
+ .ct_item_ops = &ufg_endpoint_grp_item_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct ufg_endpoint_grp *make_ufg_endpoint(struct config_group *group,
+ const char *name)
+{
+ struct ufg_endpoint_grp *endpoint;
+
+ endpoint = kzalloc(sizeof(*endpoint), GFP_KERNEL);
+ if (!endpoint)
+ return ERR_PTR(-ENOMEM);
+
+ config_group_init_type_name(&endpoint->group, name,
+ &ufg_endpoint_grp_type);
+
+ return endpoint;
+}
+
+/*
+ * USB interface-level configfs group
+ *
+ * /usb-function-gadget/gadgets/<gadget>/functions/<name>.#/interface#
+ */
+
+UFG_SHOW_USHORT_ATTR(interface_nr, ufg_interface_grp, interface_nr);
+UFG_SHOW_USHORT_ATTR(alt_setting, ufg_interface_grp, alt_setting);
+UFG_SHOW_USHORT_ATTR(num_endpoints, ufg_interface_grp, num_endpoints);
+UFG_SHOW_USHORT_ATTR(intf_class, ufg_interface_grp, intf_class);
+UFG_SHOW_USHORT_ATTR(intf_subclass, ufg_interface_grp, intf_subclass);
+UFG_SHOW_USHORT_ATTR(intf_protocol, ufg_interface_grp, intf_protocol);
+
+CONFIGFS_ATTR_STRUCT(ufg_interface_grp);
+
+UFG_ATTR_RO(interface_nr, interface_number, ufg_interface_grp);
+UFG_ATTR_RO(alt_setting, altsetting, ufg_interface_grp);
+UFG_ATTR_RO(num_endpoints, n_endpoints, ufg_interface_grp);
+UFG_ATTR_RO(intf_class, interface_class, ufg_interface_grp);
+UFG_ATTR_RO(intf_subclass, interface_subclass, ufg_interface_grp);
+UFG_ATTR_RO(intf_protocol, interface_protocol, ufg_interface_grp);
+
+static struct configfs_attribute *ufg_interface_grp_attrs[] = {
+ &ufg_interface_grp_interface_nr.attr,
+ &ufg_interface_grp_alt_setting.attr,
+ &ufg_interface_grp_num_endpoints.attr,
+ &ufg_interface_grp_intf_class.attr,
+ &ufg_interface_grp_intf_subclass.attr,
+ &ufg_interface_grp_intf_protocol.attr,
+ NULL,
+};
+
+CONFIGFS_ATTR_OPS(ufg_interface_grp);
+
+static void ufg_interface_grp_release(struct config_item *item)
+{
+ kfree(to_ufg_interface_grp(item));
+}
+
+static struct configfs_item_operations ufg_interface_grp_item_ops = {
+ .show_attribute = ufg_interface_grp_attr_show,
+ .release = ufg_interface_grp_release,
+};
+
+static struct config_item_type ufg_interface_grp_type = {
+ .ct_attrs = ufg_interface_grp_attrs,
+ .ct_item_ops = &ufg_interface_grp_item_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct ufg_interface_grp *make_ufg_interface(struct config_group *group,
+ const char *name)
+{
+ struct ufg_interface_grp *interface;
+
+ interface = kzalloc(sizeof(*interface), GFP_KERNEL);
+ if (!interface)
+ return ERR_PTR(-ENOMEM);
+
+ interface->type = UFG_INTERFACE;
+ config_group_init_type_name(&interface->group, name,
+ &ufg_interface_grp_type);
+
+ return interface;
+}
+
+/*
+ * USB function-level configfs group
+ *
+ * /usb-function-gadget/gadgets/<gadget>/functions/<name>.#
+ */
+
+static struct config_group *make_ufg_function(struct config_group *group,
+ const char *name)
+{
+ struct usb_function *f;
+ struct config_group *new;
+ struct ufg_function_grp *ufg_function_grp;
+ char name_buf[UFG_STR_LEN];
+ char *func_name;
+ char *instance_name;
+ int rc;
+
+ rc = snprintf(name_buf, UFG_STR_LEN, "%s", name);
+ if (rc >= UFG_STR_LEN)
+ return ERR_PTR(-ENAMETOOLONG);
+
+ func_name = name_buf;
+ instance_name = strchr(func_name, '.');
+ if (!instance_name) {
+ pr_err("name must be of <function>.<instance> form\n");
+ return ERR_PTR(-EINVAL);
+ }
+ *instance_name = '\0';
+
+ f = usb_get_function(func_name);
+
+ if (IS_ERR_OR_NULL(f))
+ return ERR_PTR(-ENODEV);
+
+ *instance_name = '.';
+ new = f->make_group(group, func_name);
+ if (IS_ERR_OR_NULL(new)) {
+ usb_put_function(f);
+
+ return new;
+ }
+
+ ufg_function_grp = container_of(new, struct ufg_function_grp, group);
+ ufg_function_grp->f = f;
+
+ return new;
+}
+
+/*
+ * USB gadget's functions level configfs group
+ *
+ * /usb-function-gadget/gadgets/<gadget>/functions
+ */
+
+static struct configfs_group_operations ufg_functions_group_ops = {
+ .make_group = make_ufg_function,
+};
+
+static struct config_item_type ufg_functions_type = {
+ .ct_group_ops = &ufg_functions_group_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group ufg_functions_group = {
+ .cg_item = {
+ .ci_namebuf = "functions",
+ .ci_type = &ufg_functions_type,
+ },
+};
+
+/*
+ * USB configuration's functions level configfs group
+ *
+ * /usb-function-gadget/gadgets/<gadget>/configs/#/functions
+ */
+
+static struct ufg_function_grp *ufg_check_link(struct config_item *src,
+ struct config_item *target)
+{
+ struct config_group *src_grp;
+ struct ufg_config_functions *src_fun_grp;
+ struct config_group *target_grp;
+ struct ufg_function_grp *target_fun_grp;
+
+ src_grp = to_config_group(src);
+ if (!src_grp)
+ return ERR_PTR(-EPERM);
+
+ src_fun_grp = container_of(src_grp, struct ufg_config_functions, group);
+ if (!src_fun_grp)
+ return ERR_PTR(-EPERM);
+
+ target_grp = to_config_group(target);
+ if (!target_grp)
+ return ERR_PTR(-EPERM);
+
+ if (!src->ci_parent || !src->ci_parent->ci_parent ||
+ !src->ci_parent->ci_parent->ci_parent ||
+ !target->ci_parent || !target->ci_parent->ci_parent)
+ return ERR_PTR(-EPERM);
+
+ if (src->ci_parent->ci_parent->ci_parent !=
+ target->ci_parent->ci_parent)
+ return ERR_PTR(-EPERM);
+
+ target_fun_grp = to_ufg_function_grp(&target_grp->cg_item);
+ if (!target_fun_grp)
+ return ERR_PTR(-EPERM);
+
+ if (target_fun_grp->used)
+ return ERR_PTR(-EBUSY);
+
+ return target_fun_grp;
+}
+
+static int ufg_config_functions_allow_link(struct config_item *src,
+ struct config_item *target)
+{
+ struct config_group *src_grp;
+ struct ufg_config_functions *src_fun_grp;
+ struct ufg_function_grp *target_fun_grp;
+
+ target_fun_grp = ufg_check_link(src, target);
+ if (IS_ERR(target_fun_grp))
+ return PTR_ERR(target_fun_grp);
+
+ src_grp = to_config_group(src);
+ src_fun_grp = container_of(src_grp, struct ufg_config_functions, group);
+
+ target_fun_grp->used = true;
+ list_add_tail(&target_fun_grp->entry, &src_fun_grp->list);
+
+ return 0;
+}
+
+static int ufg_config_functions_drop_link(struct config_item *src,
+ struct config_item *target)
+{
+ struct ufg_function_grp *target_fun_grp;
+
+ target_fun_grp = ufg_check_link(src, target);
+ if (IS_ERR(target_fun_grp))
+ return PTR_ERR(target_fun_grp);
+
+ target_fun_grp->used = false;
+ list_del(&target_fun_grp->entry);
+
+ return 0;
+}
+
+static struct configfs_item_operations ufg_config_functions_item_ops = {
+ .allow_link = ufg_config_functions_allow_link,
+ .drop_link = ufg_config_functions_drop_link,
+};
+
+static struct config_item_type ufg_config_functions_type = {
+ .ct_item_ops = &ufg_config_functions_item_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct ufg_config_functions ufg_config_functions_group = {
+ .group = {
+ .cg_item = {
+ .ci_namebuf = "functions",
+ .ci_type = &ufg_config_functions_type,
+ },
+ },
+};
+
+/*
+ * USB configuration-level configfs group
+ *
+ * /usb-function-gadget/gadgets/<gadget>/configs/#
+ */
+
+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,
+};
+
+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 config_item_type ufg_config_grp_type = {
+ .ct_attrs = ufg_config_grp_attrs,
+ .ct_item_ops = &ufg_config_grp_item_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *ufg_config_default_groups[] = {
+ &ufg_config_functions_group.group,
+ NULL
+};
+
+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);
+
+ INIT_LIST_HEAD(&ufg_config_functions_group.list);
+ config->group.default_groups = ufg_config_default_groups;
+
+ return &config->group;
+}
+
+/*
+ * USB gadget's configurations level configfs group
+ *
+ * /usb-function-gadget/gadgets/<gadget>/configs
+ */
+
+static struct configfs_group_operations ufg_configs_group_ops = {
+ .make_group = make_ufg_config,
+};
+
+static struct config_item_type ufg_configs_type = {
+ .ct_group_ops = &ufg_configs_group_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group ufg_configs_group = {
+ .cg_item = {
+ .ci_namebuf = "configs",
+ .ci_type = &ufg_configs_type,
+ },
+};
+
+/*
+ * USB gadget-level configfs group
+ *
+ * /usb-function-gadget/gadgets/<gadget>
+ */
+
+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);
+
+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);
+
+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,
+ NULL,
+};
+
+CONFIGFS_ATTR_OPS(ufg_gadget_grp);
+
+static void ufg_gadget_grp_release(struct config_item *item)
+{
+ struct ufg_gadget_grp *ufg_gadget_grp;
+
+ ufg_gadget_grp = to_ufg_gadget_grp(item);
+ kfree(ufg_gadget_grp->iManufacturer);
+ kfree(ufg_gadget_grp->iProduct);
+ kfree(ufg_gadget_grp->iSerialNumber);
+ kfree(ufg_gadget_grp);
+}
+
+static int ufg_gadget_ready(struct ufg_gadget_grp *g_grp);
+
+static int ufg_gadget_grp_allow_link(struct config_item *src,
+ struct config_item *target)
+{
+ struct ufg_gadget_grp *gadget_grp;
+ struct config_group *udc;
+ struct ufg_udc *udc_grp;
+ int ret;
+
+ gadget_grp = to_ufg_gadget_grp(src);
+ if (!gadget_grp)
+ return -EPERM;
+
+ if (gadget_grp->ready)
+ return -EBUSY;
+
+ udc = to_config_group(target);
+ udc_grp = container_of(udc, struct ufg_udc, group);
+ if (!udc_grp)
+ return -EPERM;
+
+ if (udc_grp->used)
+ return -EBUSY;
+
+ mutex_lock(&gadget_grp->lock);
+
+ gadget_grp->ready = 1;
+ udc_grp->used = 1;
+
+ ret = ufg_gadget_ready(gadget_grp);
+ if (ret) {
+ gadget_grp->ready = 0;
+ udc_grp->used = 0;
+
+ ret = -EBUSY;
+ goto end;
+ }
+
+end:
+ mutex_unlock(&gadget_grp->lock);
+
+ return ret;
+}
+
+static int ufg_gadget_grp_drop_link(struct config_item *src,
+ struct config_item *target)
+{
+ struct ufg_gadget_grp *gadget_grp;
+ struct config_group *udc;
+ struct ufg_udc *udc_grp;
+ int ret;
+
+ gadget_grp = to_ufg_gadget_grp(src);
+ if (!gadget_grp)
+ return -EPERM;
+
+ udc = to_config_group(target);
+ udc_grp = container_of(udc, struct ufg_udc, group);
+ if (!udc_grp)
+ return -EPERM;
+
+ mutex_lock(&gadget_grp->lock);
+
+ gadget_grp->ready = 0;
+ udc_grp->used = 0;
+
+ ret = ufg_gadget_ready(gadget_grp);
+
+ mutex_unlock(&gadget_grp->lock);
+
+ return ret;
+}
+
+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,
+ .allow_link = ufg_gadget_grp_allow_link,
+ .drop_link = ufg_gadget_grp_drop_link,
+};
+
+static struct config_item_type ufg_gadget_grp_type = {
+ .ct_attrs = ufg_gadget_grp_attrs,
+ .ct_item_ops = &ufg_gadget_grp_item_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *ufg_gadget_default_groups[] = {
+ &ufg_configs_group,
+ &ufg_functions_group,
+ NULL
+};
+
+static struct config_group *make_ufg_gadget(struct config_group *group,
+ const char *name)
+{
+ struct ufg_gadget_grp *ufg_gadget_grp;
+
+ ufg_gadget_grp = kzalloc(sizeof(*ufg_gadget_grp), GFP_KERNEL);
+ if (!ufg_gadget_grp)
+ return ERR_PTR(-ENOMEM);
+
+ config_group_init_type_name(&ufg_gadget_grp->group, name,
+ &ufg_gadget_grp_type);
+
+ ufg_gadget_grp->group.default_groups = ufg_gadget_default_groups;
+
+ mutex_init(&ufg_gadget_grp->lock);
+
+ return &ufg_gadget_grp->group;
+}
+
+/*
+ * USB gadgets level configfs group
+ *
+ * /usb-function-gadget/gadgets
+ */
+
+static struct configfs_group_operations ufg_gadgets_group_ops = {
+ .make_group = make_ufg_gadget,
+};
+
+static struct config_item_type ufg_gadgets_type = {
+ .ct_group_ops = &ufg_gadgets_group_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group ufg_gadgets_group = {
+ .cg_item = {
+ .ci_namebuf = "gadgets",
+ .ci_type = &ufg_gadgets_type,
+ },
+};
+
+/*
+ * USB udcs level configfs group
+ *
+ * /usb-function-gadget/udcs
+ */
+
+static struct config_item_type ufg_udcs_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group ufg_udcs_group = {
+ .cg_item = {
+ .ci_namebuf = "udcs",
+ .ci_type = &ufg_udcs_type,
+ },
+};
+
+static struct config_item_type ufg_udc_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+struct config_group *udc_configfs_register(struct device *dev)
+{
+ struct ufg_udc *ufg_udc;
+ int ret;
+
+ ufg_udc = kzalloc(sizeof(*ufg_udc), GFP_KERNEL);
+ if (!ufg_udc)
+ return ERR_PTR(-ENOMEM);
+
+ config_group_init_type_name(&ufg_udc->group, kobject_name(&dev->kobj),
+ &ufg_udc_type);
+ ret = configfs_create_group(&ufg_udcs_group, &ufg_udc->group);
+ if (ret) {
+ kfree(ufg_udc);
+ return ERR_PTR(ret);
+ }
+ return &ufg_udc->group;
+}
+
+void udc_configfs_unregister(struct config_group *group)
+{
+ struct ufg_udc *ufg_udc;
+
+ ufg_udc = container_of(group, struct ufg_udc, group);
+
+ configfs_remove_group(group);
+ kfree(ufg_udc);
+}
+
+/*
+ * configfs subsystem for ufg
+ *
+ * /usb-function-gadget
+ */
+
+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;
+}
+
+static struct config_item_type ufg_subsys_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *ufg_root_groups[] = {
+ &ufg_gadgets_group,
+ &ufg_udcs_group,
+ NULL,
+};
+
+struct ufg_subsys ufg_subsystem = {
+ .subsys = {
+ .su_group = {
+ .cg_item = {
+ .ci_namebuf = "usb-function-gadget",
+ .ci_type = &ufg_subsys_type,
+ },
+ .default_groups = ufg_root_groups,
+ },
+ },
+};
+
+/*------------------- USB composite handling code -----------------------*/
+
+static struct usb_composite_overwrite coverwrite;
+
+static struct usb_string strings_dev[] = {
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = "",
+ [USB_GADGET_SERIAL_IDX].s = "",
+ { } /* 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 void ufg_unbind_config(struct usb_configuration *c)
+{
+ kfree(c);
+}
+
+static void ufg_rmdirs(struct ufg_gadget_grp *ufg_gadget_grp)
+{
+ struct config_item *ci;
+ struct config_group *grp;
+
+ ci = config_group_find_item(&ufg_gadget_grp->group, "functions");
+ if (!ci)
+ return;
+ grp = to_config_group(ci);
+ if (!grp)
+ return;
+
+ /* functions' main groups */
+ ufg_for_each_child(ci, grp) {
+ struct config_group *f_grp = to_config_group(ci);
+ struct config_item *i, *i_tmp;
+
+ /* interfaces */
+ ufg_for_each_child_safe(i, i_tmp, f_grp) {
+ struct config_group *i_grp = to_config_group(i);
+ struct config_item *e, *e_tmp;
+ struct ufg_grp_hdr *h = ufg_hdr(i_grp);
+ if (h->type != UFG_INTERFACE)
+ continue;
+
+ /* endpoints */
+ ufg_for_each_child_safe(e, e_tmp, i_grp)
+ configfs_remove_group(to_config_group(e));
+
+ configfs_remove_group(to_config_group(i));
+ }
+ }
+}
+
+static struct usb_descriptor_header **ufg_get_function_descriptors(
+ struct usb_configuration *u_cfg, struct ufg_dev *ufg_dev)
+{
+ struct usb_function *u_fn;
+
+ /* last added function */
+ u_fn = list_entry(u_cfg->functions.prev, struct usb_function, list);
+ if (!u_fn)
+ return NULL;
+
+ switch (ufg_dev->cdev->gadget->speed) {
+ case USB_SPEED_SUPER:
+ if (gadget_is_superspeed(ufg_dev->cdev->gadget))
+ return u_fn->ss_descriptors;
+ /* else: fall trough */
+ case USB_SPEED_HIGH:
+ if (gadget_is_dualspeed(ufg_dev->cdev->gadget))
+ return u_fn->hs_descriptors;
+ /* else: fall through */
+ default:
+ return u_fn->fs_descriptors;
+ }
+
+ return NULL;
+}
+
+static struct config_group *ufg_process_interface_desc(
+ struct usb_descriptor_header *d, struct config_group *f_grp)
+{
+ struct usb_interface_descriptor *id =
+ (struct usb_interface_descriptor *)d;
+ struct ufg_interface_grp *new;
+ char *name_templ = "interface00";
+ int rc;
+
+ sprintf(name_templ, "interface%02x", id->bInterfaceNumber);
+ new = make_ufg_interface(f_grp, name_templ);
+ if (IS_ERR(new))
+ return ERR_PTR(PTR_ERR(new));
+
+ rc = configfs_create_group(f_grp, &new->group);
+ if (rc) {
+ ufg_interface_grp_release(&new->group.cg_item);
+
+ return ERR_PTR(rc);
+ }
+
+ new->interface_nr = id->bInterfaceNumber;
+ new->alt_setting = id->bAlternateSetting;
+ new->num_endpoints = id->bNumEndpoints;
+ new->intf_class = id->bInterfaceClass;
+ new->intf_subclass = id->bInterfaceSubClass;
+ new->intf_protocol = id->bInterfaceProtocol;
+
+ return &new->group;
+}
+
+static struct config_group *ufg_process_endpoint_desc(
+ struct usb_descriptor_header *d, struct config_group *interface_grp)
+{
+ struct usb_endpoint_descriptor *ed =
+ (struct usb_endpoint_descriptor *)d;
+ struct ufg_endpoint_grp *new;
+ char *name_templ = "endpoint00";
+ int rc;
+
+ sprintf(name_templ, "endpoint%02x", ed->bEndpointAddress);
+ new = make_ufg_endpoint(interface_grp, name_templ);
+ if (IS_ERR(new))
+ return ERR_PTR(PTR_ERR(new));
+
+ rc = configfs_create_group(interface_grp, &new->group);
+ if (rc) {
+ ufg_endpoint_grp_release(&new->group.cg_item);
+
+ return ERR_PTR(rc);
+ }
+
+ new->endpoint_address = ed->bEndpointAddress;
+ new->attributes = ed->bmAttributes;
+ new->max_packet_sz = ed->wMaxPacketSize;
+ new->interval = ed->bInterval;
+
+ return &new->group;
+
+}
+
+static int ufg_add_f(struct ufg_dev *ufg_dev, struct usb_configuration *u_cfg,
+ struct config_item *f, ushort *num_interfaces)
+{
+ struct config_group *f_grp = to_config_group(f);
+ struct ufg_function_grp *function_grp;
+ struct usb_function *fn;
+
+ function_grp = container_of(f_grp, struct ufg_function_grp, group);
+ fn = function_grp->f;
+ if (!fn)
+ return -ENODEV;
+
+ if (fn->add_function) {
+ struct usb_descriptor_header **descriptors;
+ struct usb_descriptor_header **d;
+ struct config_group *i_grp = NULL;
+ struct config_group *e_grp = NULL;
+ int r;
+
+ r = fn->add_function(u_cfg, fn, f, ufg_dev->cdev);
+ if (r)
+ return -EPERM;
+
+ descriptors = ufg_get_function_descriptors(u_cfg, ufg_dev);
+ if (!descriptors)
+ return -1;
+
+ for (d = descriptors; *d; d++)
+ switch ((*d)->bDescriptorType) {
+ case USB_DT_INTERFACE:
+ i_grp = ufg_process_interface_desc(*d, f_grp);
+ if (IS_ERR_OR_NULL(i_grp))
+ return -1;
+ (*num_interfaces)++;
+ break;
+ case USB_DT_ENDPOINT:
+ e_grp = ufg_process_endpoint_desc(*d, i_grp);
+ if (IS_ERR_OR_NULL(e_grp))
+ return -1;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static void ufg_fill_config_driver(struct usb_configuration *u_cfg,
+ struct ufg_config_grp *c_grp, int n)
+{
+ u_cfg->label = "ufg";
+ u_cfg->unbind = ufg_unbind_config;
+ u_cfg->bConfigurationValue = c_grp->conf_number = n;
+ u_cfg->bmAttributes = USB_CONFIG_ATT_ONE |
+ USB_CONFIG_ATT_SELFPOWER;
+ u_cfg->bMaxPower = c_grp->max_power;
+}
+
+static int ufg_bind(struct usb_composite_dev *cdev)
+{
+ struct ufg_dev *ufg_dev;
+ struct usb_gadget *gadget = cdev->gadget;
+ struct config_item *ci;
+ struct config_group *configs;
+ int status;
+ int n = 1;
+
+ 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);
+
+ usb_gadget_set_selfpowered(gadget);
+
+ usb_composite_overwrite_options(cdev, &coverwrite);
+
+ ci = config_group_find_item(&ufg_dev->g_grp->group, "configs");
+ if (!ci)
+ return -ENODEV;
+ configs = to_config_group(ci);
+ if (!configs)
+ return -ENODEV;
+
+ /* configurations */
+ ufg_for_each_child(ci, configs) {
+ struct ufg_config_grp *c_grp = to_ufg_config_grp(ci);
+ struct ufg_function_grp *function;
+ struct usb_configuration *u_cfg;
+ struct config_item *f;
+ struct config_group *f_grp;
+ struct ufg_config_functions *cfg_functions;
+
+ u_cfg = kzalloc(sizeof(*u_cfg), GFP_KERNEL);
+ if (!u_cfg)
+ goto unbind;
+
+ ufg_fill_config_driver(u_cfg, c_grp, n++);
+ status = usb_add_config_only(ufg_dev->cdev, u_cfg);
+ if (status)
+ goto unbind;
+
+ f = config_group_find_item(&c_grp->group, "functions");
+ if (!f)
+ goto unbind;
+ f_grp = to_config_group(f);
+ if (!f_grp)
+ goto unbind;
+ cfg_functions = container_of(f_grp, struct ufg_config_functions,
+ group);
+ if (!cfg_functions)
+ goto unbind;
+
+ list_for_each_entry(function, &cfg_functions->list, entry) {
+ status = ufg_add_f(ufg_dev, u_cfg,
+ &function->group.cg_item,
+ &c_grp->num_interfaces);
+ if (status)
+ goto unbind;
+ }
+ }
+
+ ufg_dev->ufg_device_desc.bNumConfigurations = n - 1;
+ return 0;
+
+unbind:
+ ufg_rmdirs(ufg_dev->g_grp);
+ return status;
+}
+
+static int ufg_unbind(struct usb_composite_dev *cdev)
+{
+ struct ufg_dev *ufg_dev;
+
+ ufg_dev = container_of(cdev->driver, struct ufg_dev, ufg_driver);
+
+ if (!mutex_trylock(&ufg_dev->g_grp->lock))
+ return 0;
+
+ ufg_dev->g_grp->ready = false;
+ ufg_gadget_ready(ufg_dev->g_grp);
+ mutex_unlock(&ufg_dev->g_grp->lock);
+
+ return 0;
+}
+
+static void ufg_fill_ufg_driver(struct usb_composite_driver *ufgd,
+ struct ufg_gadget_grp *g_grp)
+{
+ struct usb_device_descriptor *desc;
+
+ desc = &container_of(ufgd, struct ufg_dev, ufg_driver)->ufg_device_desc;
+ desc->bLength = sizeof(desc); /* TODO: *desc ? */
+ desc->bDescriptorType = USB_DT_DEVICE;
+ desc->bcdUSB = cpu_to_le16(0x0200);
+ desc->bDeviceClass = USB_CLASS_PER_INTERFACE;
+
+ ufgd->bind = ufg_bind;
+ ufgd->unbind = ufg_unbind;
+ ufgd->strings = dev_strings;
+ ufgd->name = "usb_function_gadget";
+ ufgd->dev = desc;
+ ufgd->needs_serial = 1;
+
+ if (g_grp->idVendor_set)
+ coverwrite.idVendor = g_grp->idVendor;
+ if (g_grp->idProduct_set)
+ coverwrite.idProduct = g_grp->idProduct;
+ if (g_grp->bcdDevice_set)
+ coverwrite.bcdDevice = g_grp->bcdDevice;
+ if (g_grp->iManufacturer)
+ coverwrite.manufacturer = g_grp->iManufacturer;
+ if (g_grp->iProduct)
+ coverwrite.product = g_grp->iProduct;
+ if (g_grp->iSerialNumber)
+ coverwrite.serial_number = g_grp->iSerialNumber;
+}
+
+static int ufg_gadget_ready(struct ufg_gadget_grp *g_grp)
+{
+ struct ufg_dev *ufg_dev;
+ int r;
+
+ if (!g_grp->ready) {
+ ufg_dev = g_grp->gadget_grp_data;
+
+ ufg_rmdirs(g_grp);
+ 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;
+
+ ufg_fill_ufg_driver(&ufg_dev->ufg_driver, g_grp);
+ g_grp->gadget_grp_data = ufg_dev;
+ ufg_dev->g_grp = g_grp;
+ r = usb_composite_probe(&ufg_dev->ufg_driver);
+ if (r) {
+ usb_composite_unregister(&ufg_dev->ufg_driver);
+ kfree(ufg_dev);
+ }
+
+ return r;
+}
+
+/*---------------------- general stuff ---------------------------*/
+
+struct ufg_subsys *UFG_SUBSYSTEM;
+EXPORT_SYMBOL(UFG_SUBSYSTEM);
+
+int __init ufg_init(void)
+{
+ config_group_init(&ufg_subsystem.subsys.su_group);
+ config_group_init(&ufg_config_functions_group.group);
+ config_group_init(&ufg_functions_group);
+ config_group_init(&ufg_configs_group);
+ config_group_init(&ufg_gadgets_group);
+ config_group_init(&ufg_udcs_group);
+ mutex_init(&ufg_subsystem.subsys.su_mutex);
+ UFG_SUBSYSTEM = &ufg_subsystem;
+
+ return configfs_register_subsystem(&ufg_subsystem.subsys);
+}
+
+void ufg_cleanup(void)
+{
+ configfs_unregister_subsystem(&ufg_subsystem.subsys);
+}
diff --git a/drivers/usb/gadget/usb_functions.h
b/drivers/usb/gadget/usb_functions.h
new file mode 100644
index 0000000..1548ede
--- /dev/null
+++ b/drivers/usb/gadget/usb_functions.h
@@ -0,0 +1,188 @@
+/*
+ * USB Functions Gadget
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * Author: Andrzej Pietrasiewicz <[email protected]>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __LINUX_USB_USB_FUNCTIONS__
+#define __LINUX_USB_USB_FUNCTIONS__
+
+#include <linux/configfs.h>
+#include <linux/usb/composite.h>
+
+#define UFG_STR_LEN 256
+#define UFG_FUNC_NAMES_BUF_LEN 256
+#define UFG_NAME_LEN 256
+#define UFG_FUNC_NAME_LEN 256
+
+enum ufg_hdr_type {
+ UFG_FUNCTION,
+ UFG_INTERFACE,
+};
+
+struct ufg_grp_hdr {
+ struct config_group group;
+
+ enum ufg_hdr_type type;
+};
+
+struct ufg_function_grp {
+ /* This group needs children */
+ struct config_group group;
+
+ enum ufg_hdr_type type;
+
+ struct usb_function *f;
+
+ struct list_head entry;
+ bool used;
+};
+
+struct ufg_endpoint_grp {
+ /* This group needs children */
+ struct config_group group;
+
+ /* attributes' values */
+ int endpoint_address;
+ int attributes;
+ int max_packet_sz;
+ int interval;
+};
+
+struct ufg_interface_grp {
+ /* This group needs children */
+ struct config_group group;
+
+ enum ufg_hdr_type type;
+
+ /* attributes' values */
+ int interface_nr;
+ int alt_setting;
+ int num_endpoints;
+ int intf_class;
+ int intf_subclass;
+ int intf_protocol;
+};
+
+struct ufg_config_functions {
+ struct config_group group;
+
+ struct list_head list;
+};
+
+struct ufg_config_grp {
+ /* This group needs children */
+ struct config_group group;
+
+ struct usb_configuration *usb_configuration;
+
+ /* attributes' values */
+ ushort max_power;
+ bool max_power_set;
+ ushort num_interfaces;
+ ushort conf_number;
+};
+
+struct ufg_gadget_grp {
+ /* This group needs children */
+ struct config_group group;
+
+ /* attributes' values */
+ ushort idVendor;
+ ushort idProduct;
+ ushort bcdDevice;
+ char *iManufacturer;
+ char *iProduct;
+ char *iSerialNumber;
+
+ ushort idVendor_set:1;
+ ushort idProduct_set:1;
+ ushort bcdDevice_set:1;
+ ushort ready:1;
+
+ struct mutex lock;
+
+ void *gadget_grp_data;
+};
+
+struct ufg_udc {
+ struct config_group group;
+
+ bool used;
+};
+
+struct ufg_subsys {
+ /* This is the root of the subsystem */
+ struct configfs_subsystem subsys;
+
+ /* attributes' values */
+ char avail_func[UFG_FUNC_NAMES_BUF_LEN];
+};
+
+struct ufg_dev {
+ struct usb_device_descriptor ufg_device_desc;
+ struct usb_composite_driver ufg_driver;
+ struct usb_composite_dev *cdev;
+ struct ufg_gadget_grp *g_grp;
+};
+
+extern struct usb_composite_driver *UFG_COMPOSITE;
+extern struct ufg_subsys *UFG_SUBSYSTEM;
+
+static inline struct ufg_grp_hdr *ufg_hdr(struct config_group *config_group)
+{
+ return container_of(config_group, struct ufg_grp_hdr, group);
+}
+
+static inline 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;
+}
+
+static inline struct ufg_endpoint_grp
+*to_ufg_endpoint_grp(struct config_item *item)
+{
+ return item ? container_of(to_config_group(item),
+ struct ufg_endpoint_grp, group) : NULL;
+}
+
+static inline struct ufg_interface_grp
+*to_ufg_interface_grp(struct config_item *item)
+{
+ return item ? container_of(to_config_group(item),
+ struct ufg_interface_grp, group) : NULL;
+}
+
+static inline 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;
+}
+
+static inline 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;
+}
+
+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);
+}
+
+#endif /* __LINUX_USB_USB_FUNCTIONS__ */
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index 0af6569..4a5d57a 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -881,6 +881,11 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver
*driver);
extern int usb_add_gadget_udc(struct device *parent, struct usb_gadget
*gadget);
extern void usb_del_gadget_udc(struct usb_gadget *gadget);
+struct config_group *udc_configfs_register(struct device *dev);
+void udc_configfs_unregister(struct config_group *group);
+int __init ufg_init(void);
+void ufg_cleanup(void);
+
/*-------------------------------------------------------------------------*/
/* utility to simplify dealing with string descriptors */
--
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