|# modprobe dummy_hcd num=2 |# find /sys/kernel/config/ -ls | 557 0 drwxr-xr-x 3 root root 0 Nov 29 17:26 /sys/kernel/config/ | 558 0 drwxr-xr-x 5 root root 0 Nov 29 17:26 /sys/kernel/config/usb_gadget | 561 0 drwxr-xr-x 4 root root 0 Nov 29 17:26 /sys/kernel/config/usb_gadget/udcs | 569 0 drwxr-xr-x 2 root root 0 Nov 29 17:26 /sys/kernel/config/usb_gadget/udcs/dummy_udc.1 | 568 0 drwxr-xr-x 2 root root 0 Nov 29 17:26 /sys/kernel/config/usb_gadget/udcs/dummy_udc.0 | 560 0 drwxr-xr-x 2 root root 0 Nov 29 17:26 /sys/kernel/config/usb_gadget/gadgets | # lsmod | Module Size Used by | dummy_hcd 20287 0 | udc 10219 1 dummy_hcd |# mkdir /sys/kernel/config/usb_gadget/gadgets/oha |# cd /sys/kernel/config/usb_gadget/gadgets/oha |# mkdir configs/def.1 |# mkdir configs/def.2 |# mkdir functions/acm.ttyS1 |# find . -ls root@squsb:/sys/kernel/config/usb_gadget/gadgets/oha# find . -ls 9323 0 drwxr-xr-x 4 root root 0 Dec 5 12:37 . 9325 0 drwxr-xr-x 4 root root 0 Dec 5 12:35 ./configs 9328 0 drwxr-xr-x 2 root root 0 Dec 5 12:37 ./configs/def.2 868 0 -rw-r--r-- 1 root root 4096 Dec 5 12:39 ./configs/def.2/sConfiguration 869 0 -rw-r--r-- 1 root root 4096 Dec 5 12:39 ./configs/def.2/bmAttributes 870 0 -rw-r--r-- 1 root root 4096 Dec 5 12:39 ./configs/def.2/MaxPower 10293 0 drwxr-xr-x 2 root root 0 Dec 5 12:37 ./configs/def.1 871 0 -rw-r--r-- 1 root root 4096 Dec 5 12:39 ./configs/def.1/sConfiguration 872 0 -rw-r--r-- 1 root root 4096 Dec 5 12:39 ./configs/def.1/bmAttributes 873 0 -rw-r--r-- 1 root root 4096 Dec 5 12:39 ./configs/def.1/MaxPower 9324 0 drwxr-xr-x 4 root root 0 Dec 5 12:35 ./functions 8398 0 drwxr-xr-x 2 root root 0 Dec 5 12:37 ./functions/acm.ttyS1 874 0 -r--r--r-- 1 root root 4096 Dec 5 12:39 ./functions/acm.ttyS1/port_num 10294 0 drwxr-xr-x 2 root root 0 Dec 5 12:37 ./functions/acm.ttyS0 875 0 -r--r--r-- 1 root root 4096 Dec 5 12:39 ./functions/acm.ttyS0/port_num 876 0 -rw-r--r-- 1 root root 4096 Dec 5 12:39 ./sSerialNumber 877 0 -rw-r--r-- 1 root root 4096 Dec 5 12:39 ./sProduct 878 0 -rw-r--r-- 1 root root 4096 Dec 5 12:39 ./sManufacturer 879 0 -rw-r--r-- 1 root root 4096 Dec 5 12:39 ./bcdUSB 880 0 -rw-r--r-- 1 root root 4096 Dec 5 12:39 ./idProduct 881 0 -rw-r--r-- 1 root root 4096 Dec 5 12:39 ./idVendor 882 0 -rw-r--r-- 1 root root 4096 Dec 5 12:39 ./bMaxPacketSize0 883 0 -rw-r--r-- 1 root root 4096 Dec 5 12:39 ./bDeviceProtocol 884 0 -rw-r--r-- 1 root root 4096 Dec 5 12:39 ./bDeviceSubClass 885 0 -rw-r--r-- 1 root root 4096 Dec 5 12:39 ./bDeviceClass |# mkdir default.0 |mkdir: cannot create directory `default.0': Invalid argument |oha# mkdir another.2 |mkdir: cannot create directory `another.1': File exists v3…v4 - moved functions from the root folde down to the gadget as suggested by Michał - configs have now their own configs folder as suggested by Michał. The folder is still name.bConfigurationValue where name becomes the sConfiguration. Is this usefull should we just stilc configs/bConfigurationValue/ ? - added configfs support to the ACM function. The port_num attribute is exported by f_acm. An argument has been added to the USB alloc function to distinguish between "old" (use facm_configure() to configure and configfs interface (expose a config_node). The port_num is currently a dumb counter. It will require some function re-work to make it work. scheduled for v5: - sym linking function into config. - string rework. This will add a strings folder incl. language code like strings/409/manufacturer as suggested by Alan. v2…v3 - replaced one ifndef by ifdef as suggested by Micahał - strstr()/strchr() function_make as suggested by Micahł - replace [iSerialNumber|iProduct|iManufacturer] with [sSerialNumber|sProduct|sManufacturer] as suggested by Alan - added creation of config descriptors v1…v2 - moved gadgets from configfs' root directory into /udcs/ within our "usb_gadget" folder. Requested by Andrzej & Michał - use a dot as a delimiter between function's name and its instance's name as suggested by Michał - renamed all config_item_type, configfs_group_operations, make_group, drop_item as suggested by suggested by Andrzej to remain consisten within this file and within other configfs users - Since configfs.c and functions.c are now part of the udc-core module, the module itself is now called udc. Also added a tiny ifdef around init code becuase udc-core is subsys init and this is too early for configfs in the built-in case. In the module case, we can only have one init function. Signed-off-by: Sebastian Andrzej Siewior <bigeasy@xxxxxxxxxxxxx> --- drivers/usb/gadget/Makefile | 5 +- drivers/usb/gadget/configfs.c | 604 +++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/f_acm.c | 63 +++- drivers/usb/gadget/f_loopback.c | 2 +- drivers/usb/gadget/f_sourcesink.c | 2 +- drivers/usb/gadget/functions.c | 21 +- drivers/usb/gadget/udc-core.c | 24 ++ include/linux/usb/composite.h | 6 +- include/linux/usb/gadget.h | 5 + 9 files changed, 719 insertions(+), 13 deletions(-) create mode 100644 drivers/usb/gadget/configfs.c diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 97a13c3..8171093 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 +udc-y += udc-core.o configfs.o functions.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 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/configfs.c b/drivers/usb/gadget/configfs.c new file mode 100644 index 0000000..e4c2eca --- /dev/null +++ b/drivers/usb/gadget/configfs.c @@ -0,0 +1,604 @@ +#include <linux/configfs.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/usb/composite.h> + +#define MAX_NAME_LEN 40 + +static struct config_item_type gadget_core_type = { + .ct_owner = THIS_MODULE, +}; + +static struct config_item_type udc_type = { + .ct_owner = THIS_MODULE, +}; + +static struct config_group udc_group = { + .cg_item = { + .ci_namebuf = "udcs", + .ci_type = &udc_type, + }, +}; + +struct gadget_info { + struct config_group group; + struct config_group config_group; + struct config_group *default_groups[3]; + + struct usb_device_descriptor device_desc; + char *sManufacturer; + char *sProduct; + char *sSerialNumber; + + struct usb_composite_driver driver; +#ifdef CONFIG_USB_OTG + struct usb_otg_descriptor otg; +#endif + struct usb_composite_driver composite; + struct usb_composite_dev cdev; +}; + +struct config_usb_cfg { + struct config_group group; + struct usb_configuration c; + char *sConfiguration; +}; + +static int usb_string_copy(const char *s, char **s_copy) +{ + int ret; + char *str; + char *copy = *s_copy; + ret = strlen(s); + if (ret > 126) + return -EOVERFLOW; + + str = kstrdup(s, GFP_KERNEL); + if (!str) + return -ENOMEM; + if (str[ret - 1] == '\n') + str[ret - 1] = '\0'; + kfree(copy); + *s_copy = str; + return 0; +} + +CONFIGFS_ATTR_STRUCT(gadget_info); +CONFIGFS_ATTR_STRUCT(config_usb_cfg); + +#define GI_DEVICE_DESC_ITEM_ATTR(name) \ + static struct gadget_info_attribute gadget_device_desc_##name = \ + __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, \ + gadget_dev_desc_##name##_show, \ + gadget_dev_desc_##name##_store) + +#define GI_DEVICE_DESC_SIMPLE_R_u8(__name) \ + static ssize_t gadget_dev_desc_##__name##_show(struct gadget_info *gi, char *page) \ +{ \ + return sprintf(page, "0x%02x\n", gi->device_desc.__name); \ +} + +#define GI_DEVICE_DESC_SIMPLE_R_u16(__name) \ + static ssize_t gadget_dev_desc_##__name##_show(struct gadget_info *gi, char *page) \ +{ \ + return sprintf(page, "0x%04x\n", le16_to_cpup(&gi->device_desc.__name)); \ +} + +#define GI_DEVICE_DESC_SIMPLE_R_charp(__name) \ + static ssize_t gadget_dev_desc_##__name##_show(struct gadget_info *gi, char *page) \ +{ \ + return sprintf(page, "%s\n", gi->__name); \ +} + +#define GI_DEVICE_DESC_SIMPLE_W_u8(_name) \ + static ssize_t gadget_dev_desc_##_name##_store(struct gadget_info *gi, \ + const char *page, size_t len) \ +{ \ + u8 val; \ + int ret; \ + ret = kstrtou8(page, 0, &val); \ + if (ret) \ + return ret; \ + gi->device_desc._name = val; \ + return len; \ +} + +#define GI_DEVICE_DESC_SIMPLE_W_u16(_name) \ + static ssize_t gadget_dev_desc_##_name##_store(struct gadget_info *gi, \ + const char *page, size_t len) \ +{ \ + u16 val; \ + int ret; \ + ret = kstrtou16(page, 0, &val); \ + if (ret) \ + return ret; \ + gi->device_desc._name = cpu_to_le16p(&val); \ + return len; \ +} + +#define GI_DEVICE_DESC_SIMPLE_W_charp(_name) \ + static ssize_t gadget_dev_desc_##_name##_store(struct gadget_info *gi, \ + const char *page, size_t len) \ +{ \ + int ret; \ + \ + ret = usb_string_copy(page, &gi->_name); \ + if (ret) \ + return ret; \ + return len; \ +} + +#define GI_DEVICE_DESC_SIMPLE_RW(_name, _type) \ + GI_DEVICE_DESC_SIMPLE_R_##_type(_name) \ + GI_DEVICE_DESC_SIMPLE_W_##_type(_name) + +GI_DEVICE_DESC_SIMPLE_RW(bDeviceClass, u8); +GI_DEVICE_DESC_SIMPLE_RW(bDeviceSubClass, u8); +GI_DEVICE_DESC_SIMPLE_RW(bDeviceProtocol, u8); +GI_DEVICE_DESC_SIMPLE_RW(bMaxPacketSize0, u8); +GI_DEVICE_DESC_SIMPLE_RW(idVendor, u16); +GI_DEVICE_DESC_SIMPLE_RW(idProduct, u16); +GI_DEVICE_DESC_SIMPLE_R_u16(bcdUSB); + +static ssize_t gadget_dev_desc_bcdUSB_store(struct gadget_info *gi, + const char *page, size_t len) +{ + u16 bcdUSB; + int ret; + + ret = kstrtou16(page, 0, &bcdUSB); + if (ret) + return ret; + if ((bcdUSB & 0xf) > 9) + return -EINVAL; + if (((bcdUSB >> 4) & 0xf) > 9) + return -EINVAL; + if (((bcdUSB >> 8) & 0xf) > 9) + return -EINVAL; + if (((bcdUSB >> 12) & 0xf) > 9) + return -EINVAL; + + gi->device_desc.bcdUSB = cpu_to_le16(bcdUSB); + return len; +} + +GI_DEVICE_DESC_SIMPLE_RW(sManufacturer, charp); +GI_DEVICE_DESC_SIMPLE_RW(sProduct, charp); +GI_DEVICE_DESC_SIMPLE_RW(sSerialNumber, charp); + +GI_DEVICE_DESC_ITEM_ATTR(bDeviceClass); +GI_DEVICE_DESC_ITEM_ATTR(bDeviceSubClass); +GI_DEVICE_DESC_ITEM_ATTR(bDeviceProtocol); +GI_DEVICE_DESC_ITEM_ATTR(bMaxPacketSize0); +GI_DEVICE_DESC_ITEM_ATTR(idVendor); +GI_DEVICE_DESC_ITEM_ATTR(idProduct); +GI_DEVICE_DESC_ITEM_ATTR(bcdUSB); +GI_DEVICE_DESC_ITEM_ATTR(sManufacturer); +GI_DEVICE_DESC_ITEM_ATTR(sProduct); +GI_DEVICE_DESC_ITEM_ATTR(sSerialNumber); + +static struct configfs_attribute *gadget_root_attrs[] = { + &gadget_device_desc_bDeviceClass.attr, + &gadget_device_desc_bDeviceSubClass.attr, + &gadget_device_desc_bDeviceProtocol.attr, + &gadget_device_desc_bMaxPacketSize0.attr, + &gadget_device_desc_idVendor.attr, + &gadget_device_desc_idProduct.attr, + &gadget_device_desc_bcdUSB.attr, + &gadget_device_desc_sManufacturer.attr, + &gadget_device_desc_sProduct.attr, + &gadget_device_desc_sSerialNumber.attr, + NULL, +}; + + +static inline struct gadget_info *to_gadget_info(struct config_item *item) +{ + return container_of(to_config_group(item), struct gadget_info, group); +} + +static inline struct config_usb_cfg *to_config_usb_cfg(struct config_item *item) +{ + return container_of(to_config_group(item), struct config_usb_cfg, + group); +} + +static void gadget_info_attr_release(struct config_item *item) +{ + struct gadget_info *gi = to_gadget_info(item); + + WARN_ON(!list_empty(&gi->cdev.configs)); + + kfree(gi->sManufacturer); + kfree(gi->sProduct); + kfree(gi->sSerialNumber); + kfree(gi); +} + +CONFIGFS_ATTR_OPS(gadget_info); + +static struct configfs_item_operations gadget_root_item_ops = { + .release = gadget_info_attr_release, + .show_attribute = gadget_info_attr_show, + .store_attribute = gadget_info_attr_store, +}; + +static void gadget_config_attr_release(struct config_item *item) +{ + struct config_usb_cfg *cfg = to_config_usb_cfg(item); + + kfree(cfg->sConfiguration); + list_del(&cfg->c.list); + kfree(cfg); +} + +CONFIGFS_ATTR_OPS(config_usb_cfg); + +static struct configfs_item_operations gadget_config_item_ops = { + .release = gadget_config_attr_release, + .show_attribute = config_usb_cfg_attr_show, + .store_attribute = config_usb_cfg_attr_store, +}; + + +static ssize_t gadget_config_desc_MaxPower_show(struct config_usb_cfg *cfg, char *page) +{ + return sprintf(page, "%u\n", cfg->c.MaxPower); +} + +static ssize_t gadget_config_desc_MaxPower_store(struct config_usb_cfg *cfg, + const char *page, size_t len) +{ + u16 val; + int ret; + ret = kstrtou16(page, 0, &val); + if (ret) + return ret; + if (DIV_ROUND_UP(val, 8) > 0xff) + return -ERANGE; + cfg->c.MaxPower = val; + return len; +} + +static ssize_t gadget_config_desc_bmAttributes_show(struct config_usb_cfg *cfg, char *page) +{ + return sprintf(page, "0x%02x\n", cfg->c.bmAttributes); +} + +static ssize_t gadget_config_desc_bmAttributes_store(struct config_usb_cfg *cfg, + const char *page, size_t len) +{ + u8 val; + int ret; + ret = kstrtou8(page, 0, &val); + if (ret) + return ret; + if (!(val & USB_CONFIG_ATT_ONE)) + return -EINVAL; + if (val & ~(USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER | + USB_CONFIG_ATT_WAKEUP)) + return -EINVAL; + cfg->c.bmAttributes = val; + return len; +} + +static ssize_t gadget_config_desc_sConfiguration_show(struct config_usb_cfg *cfg, char *page) +{ + return sprintf(page, "%s\n", cfg->sConfiguration); +} + +static ssize_t gadget_config_desc_sConfiguration_store(struct config_usb_cfg *cfg, + const char *page, size_t len) +{ + int ret; + + ret = usb_string_copy(page, &cfg->sConfiguration); + if (ret) + return ret; + return len; +} + +#define CFG_CONFIG_DESC_ITEM_ATTR(name) \ + static struct config_usb_cfg_attribute gadget_usb_cfg_##name = \ + __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, \ + gadget_config_desc_##name##_show, \ + gadget_config_desc_##name##_store) + +CFG_CONFIG_DESC_ITEM_ATTR(MaxPower); +CFG_CONFIG_DESC_ITEM_ATTR(bmAttributes); +CFG_CONFIG_DESC_ITEM_ATTR(sConfiguration); + +static struct configfs_attribute *gadget_config_attrs[] = { + &gadget_usb_cfg_MaxPower.attr, + &gadget_usb_cfg_bmAttributes.attr, + &gadget_usb_cfg_sConfiguration.attr, + NULL, +}; + +static struct config_item_type gadget_config_type = { + .ct_item_ops = &gadget_config_item_ops, + .ct_attrs = gadget_config_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_item_type gadget_root_type = { + .ct_item_ops = &gadget_root_item_ops, + .ct_attrs = gadget_root_attrs, + .ct_owner = THIS_MODULE, +}; + +static void composite_init_dev(struct usb_composite_dev *cdev) +{ + spin_lock_init(&cdev->lock); + INIT_LIST_HEAD(&cdev->configs); +} + +static struct config_group *function_make( + struct config_group *group, + const char *name) +{ + struct usb_function *f; + char buf[MAX_NAME_LEN]; + char *func_name; + char *instance_name; + int ret; + + ret = snprintf(buf, MAX_NAME_LEN, "%s", name); + if (ret >= MAX_NAME_LEN) + ERR_PTR(-ENAMETOOLONG); + + func_name = buf; + instance_name = strchr(func_name, '.'); + if (!instance_name) { + pr_err("Unable to locate . in FUNC.INSTANCE\n"); + return ERR_PTR(-EINVAL); + } + *instance_name = '\0'; + instance_name++; + + f = usb_get_function_configfs(func_name); + if (IS_ERR(f)) + return ERR_PTR(PTR_ERR(f)); + + return &f->group; +} + +static void function_drop( + struct config_group *group, + struct config_item *item) +{ + config_item_put(item); +} + +static struct configfs_group_operations functions_ops = { + .make_group = &function_make, + .drop_item = &function_drop, +}; + +static struct config_item_type functions_type = { + .ct_group_ops = &functions_ops, + .ct_owner = THIS_MODULE, +}; + +static struct config_group func_group = { + .cg_item = { + .ci_namebuf = "functions", + .ci_type = &functions_type, + }, +}; + +static struct config_group *config_desc_make( + struct config_group *group, + const char *name) +{ + struct gadget_info *gi; + struct usb_configuration *c; + struct config_usb_cfg *cfg; + char buf[MAX_NAME_LEN]; + char *num_str; + u8 num; + int ret; + + gi = container_of(group, struct gadget_info, config_group); + ret = snprintf(buf, MAX_NAME_LEN, "%s", name); + if (ret >= MAX_NAME_LEN) + return ERR_PTR(-ENAMETOOLONG); + + num_str = strchr(buf, '.'); + if (!num_str) { + pr_err("Unable to locate . in name.bConfigurationValue\n"); + return ERR_PTR(-EINVAL); + } + + *num_str = '\0'; + num_str++; + + if (!strlen(buf)) + return ERR_PTR(-EINVAL); + + ret = kstrtou8(num_str, 0, &num); + if (ret) + return ERR_PTR(ret); + + if (!num) + return ERR_PTR(-EINVAL); + + list_for_each_entry(c, &gi->cdev.configs, list) { + if (c->bConfigurationValue == num) + return ERR_PTR(-EEXIST); + } + + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) + return ERR_PTR(-ENOMEM); + cfg->c.label = "configfs-configuration"; + cfg->sConfiguration = kstrdup(buf, GFP_KERNEL); + if (!cfg->sConfiguration) + goto err; + cfg->c.bConfigurationValue = num; + cfg->c.MaxPower = CONFIG_USB_GADGET_VBUS_DRAW; + cfg->c.bmAttributes = USB_CONFIG_ATT_ONE; + + config_group_init_type_name(&cfg->group, "", + &gadget_config_type); + + list_add_tail(&cfg->c.list, &gi->cdev.configs); + + return &cfg->group; +err: + kfree(cfg); + return ERR_PTR(-ENOMEM); +} + +static void config_desc_drop( + struct config_group *group, + struct config_item *item) +{ + config_item_put(item); +} + +static struct configfs_group_operations config_desc_ops = { + .make_group = &config_desc_make, + .drop_item = &config_desc_drop, +}; + +static struct config_item_type config_desc_type = { + .ct_group_ops = &config_desc_ops, + .ct_owner = THIS_MODULE, +}; + +static struct config_group *gadgets_make( + struct config_group *group, + const char *name) +{ + struct gadget_info *gi; + + gi = kzalloc(sizeof(*gi), GFP_KERNEL); + if (!gi) + return ERR_PTR(-ENOMEM); + + gi->group.default_groups = gi->default_groups; + gi->group.default_groups[0] = &func_group, + gi->group.default_groups[1] = &gi->config_group; + + config_group_init_type_name(&gi->config_group, "configs", + &config_desc_type); + + gi->device_desc.bLength = USB_DT_DEVICE_SIZE; + gi->device_desc.bDescriptorType = USB_DT_DEVICE; + + gi->driver.dev = &gi->device_desc; + gi->driver.bind = NULL; + gi->driver.unbind = NULL; + gi->driver.suspend = NULL; + gi->driver.resume = NULL; + gi->driver.max_speed = USB_SPEED_SUPER; + + composite_init_dev(&gi->cdev); + /* gi->composite.gadget_driver = composite_driver_template; */ + +#ifdef CONFIG_USB_OTG + gi->otg.bLength = sizeof(struct usb_otg_descriptor); + gi->otg.bDescriptorType = USB_DT_OTG; + gi->otg.bmAttributes = USB_OTG_SRP | USB_OTG_HNP; +#endif + + config_group_init_type_name(&gi->group, "", + &gadget_root_type); + return &gi->group; +} + +static void gadgets_drop(struct config_group *group, struct config_item *item) +{ + config_item_put(item); +} + +static struct configfs_group_operations gadgets_ops = { + .make_group = &gadgets_make, + .drop_item = &gadgets_drop, +}; + +static struct config_item_type gadgets_type = { + .ct_group_ops = &gadgets_ops, + .ct_owner = THIS_MODULE, +}; + +static struct config_group gadget_group = { + .cg_item = { + .ci_namebuf = "gadgets", + .ci_type = &gadgets_type, + }, +}; + +static struct config_group *root_groups[] = { + &gadget_group, + &udc_group, + NULL +}; + +static struct configfs_subsystem gadget_subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = "usb_gadget", + .ci_type = &gadget_core_type, + }, + .default_groups = root_groups, + }, + .su_mutex = __MUTEX_INITIALIZER(gadget_subsys.su_mutex), +}; + +struct udc_configfs { + struct config_group group; +}; + +struct config_group *udc_add_configfs(struct device *dev) +{ + struct udc_configfs *udc_cfs; + struct config_group *group; + int ret; + + udc_cfs = kzalloc(sizeof(struct udc_configfs), GFP_KERNEL); + if (!udc_cfs) + return ERR_PTR(-ENOMEM); + + group = &udc_cfs->group; + config_group_init_type_name(group, + kobject_name(&dev->kobj), + &gadget_core_type); + ret = configfs_create_group(&udc_group, group); + if (ret) + goto err; + return group; +err: + kfree(udc_cfs); + return ERR_PTR(ret); +} + +void udc_del_configfs(struct config_group *group) +{ + struct udc_configfs *udc_cfs; + + udc_cfs = container_of(group, struct udc_configfs, group); + + configfs_remove_group(group); + kfree(udc_cfs); +} + +int __init gadget_cfs_init(void) +{ + int ret; + + config_group_init(&gadget_subsys.su_group); + config_group_init(&func_group); + config_group_init(&gadget_group); + config_group_init(&udc_group); + + ret = configfs_register_subsystem(&gadget_subsys); + return ret; +} + +void __exit gadget_cfs_exit(void) +{ + configfs_unregister_subsystem(&gadget_subsys); +} diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index 64cea16..d8736bc 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c @@ -769,7 +769,62 @@ void facm_configure(struct usb_function *f, unsigned port) } EXPORT_SYMBOL_GPL(facm_configure); -static struct usb_function *acm_alloc_func(void) +static inline struct f_acm *to_f_acm(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_acm, + port.func.group); +} + +CONFIGFS_ATTR_STRUCT(f_acm); +static ssize_t f_acm_attr_show(struct config_item *item, + struct configfs_attribute *attr, + char *page) +{ + struct f_acm *f_acm = to_f_acm(item); + struct f_acm_attribute *f_acm_attr = + container_of(attr, struct f_acm_attribute, attr); + ssize_t ret = 0; + + if (f_acm_attr->show) + ret = f_acm_attr->show(f_acm, page); + return ret; +} + +static void acm_attr_release(struct config_item *item) +{ + struct f_acm *acm = to_f_acm(item); + + usb_put_function(&acm->port.func); +} + +static struct configfs_item_operations acm_item_ops = { + .release = acm_attr_release, + .show_attribute = f_acm_attr_show, +}; + +static ssize_t f_acm_port_num_show(struct f_acm *acm, char *page) +{ + return sprintf(page, "%u\n", acm->port_num); +} + +static struct f_acm_attribute f_acm_port_num = + __CONFIGFS_ATTR_RO(port_num, f_acm_port_num_show); + + +static struct configfs_attribute *acm_attrs[] = { + &f_acm_port_num.attr, + NULL, +}; + +static struct config_item_type acm_func_type = { + .ct_item_ops = &acm_item_ops, + .ct_attrs = acm_attrs, + .ct_owner = THIS_MODULE, +}; + +static int dumb_port_counter; + +static struct usb_function *acm_alloc_func(int configfs) { struct f_acm *acm; @@ -778,7 +833,11 @@ static struct usb_function *acm_alloc_func(void) return ERR_PTR(-ENOMEM); acm->port.func.free_func = acm_free_func; - + if (configfs) { + config_group_init_type_name(&acm->port.func.group, "", + &acm_func_type); + acm->port_num = dumb_port_counter++; + } return &acm->port.func; } DECLARE_USB_FUNCTION_INIT(acm, acm_alloc_func); diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c index 2d5aade..4c48a4d 100644 --- a/drivers/usb/gadget/f_loopback.c +++ b/drivers/usb/gadget/f_loopback.c @@ -379,7 +379,7 @@ static void loopback_disable(struct usb_function *f) disable_loopback(loop); } -static struct usb_function *loopback_alloc(void) +static struct usb_function *loopback_alloc(int configfs) { struct f_loopback *loop; diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c index 4538a3b..f6beb50 100644 --- a/drivers/usb/gadget/f_sourcesink.c +++ b/drivers/usb/gadget/f_sourcesink.c @@ -878,7 +878,7 @@ static int sourcesink_setup(struct usb_function *f, return value; } -static struct usb_function *source_sink_alloc(void) +static struct usb_function *source_sink_alloc(int configfs) { struct f_sourcesink *ss; diff --git a/drivers/usb/gadget/functions.c b/drivers/usb/gadget/functions.c index b7a36c3..5901213 100644 --- a/drivers/usb/gadget/functions.c +++ b/drivers/usb/gadget/functions.c @@ -8,7 +8,7 @@ static LIST_HEAD(func_list); static DEFINE_MUTEX(func_lock); -static struct usb_function *try_get_usb_function(const char *name) +static struct usb_function *try_get_usb_function(const char *name, int configfs) { struct usb_function_driver *fd; struct usb_function *f; @@ -24,7 +24,7 @@ static struct usb_function *try_get_usb_function(const char *name) f = ERR_PTR(-EBUSY); break; } - f = fd->alloc(); + f = fd->alloc(configfs); if (f) { f->mod = fd->mod; } else { @@ -37,12 +37,12 @@ static struct usb_function *try_get_usb_function(const char *name) return f; } -struct usb_function *usb_get_function(const char *name) +static struct usb_function *__usb_get_function(const char *name, int configfs) { struct usb_function *f; int ret; - f = try_get_usb_function(name); + f = try_get_usb_function(name, configfs); if (!IS_ERR(f)) return f; ret = PTR_ERR(f); @@ -51,10 +51,21 @@ struct usb_function *usb_get_function(const char *name) ret = request_module("usbfunc:%s", name); if (ret < 0) return ERR_PTR(ret); - return try_get_usb_function(name); + return try_get_usb_function(name, configfs); +} + +struct usb_function *usb_get_function(const char *name) +{ + return __usb_get_function(name, 0); } EXPORT_SYMBOL_GPL(usb_get_function); +struct usb_function *usb_get_function_configfs(const char *name) +{ + return __usb_get_function(name, 1); +} +EXPORT_SYMBOL_GPL(usb_get_function_configfs); + void usb_put_function(struct usb_function *fd) { struct module *mod; diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index 4d90a80..48ca094 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -26,6 +26,7 @@ #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> +#include <linux/configfs.h> /** * struct usb_udc - describes one usb device controller @@ -42,6 +43,7 @@ struct usb_udc { struct usb_gadget *gadget; struct device dev; struct list_head list; + struct config_group *group; }; static struct class *udc_class; @@ -231,6 +233,7 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) if (ret) goto err3; + udc->group = udc_add_configfs(&udc->dev); mutex_unlock(&udc_lock); return 0; @@ -305,6 +308,7 @@ void usb_del_gadget_udc(struct usb_gadget *gadget) usb_gadget_remove_driver(udc); kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE); + udc_del_configfs(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 ret __maybe_unused; + udc_class = class_create(THIS_MODULE, "udc"); if (IS_ERR(udc_class)) { pr_err("failed to create udc class --> %ld\n", @@ -512,12 +518,30 @@ static int __init usb_udc_init(void) } udc_class->dev_uevent = usb_udc_uevent; +#ifdef MODULE + ret = gadget_cfs_init(); + if (ret) + class_destroy(udc_class); + return ret; +#else return 0; +#endif } subsys_initcall(usb_udc_init); +#ifndef MODULE +static int __init usb_udc_init_mod(void) +{ + if (IS_ERR(udc_class)) + return PTR_ERR(udc_class); + return gadget_cfs_init(); +} +module_init(usb_udc_init_mod); +#endif + static void __exit usb_udc_exit(void) { + gadget_cfs_exit(); class_destroy(udc_class); } module_exit(usb_udc_exit); diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 1f71370..46e4da8 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -120,7 +120,7 @@ struct usb_configuration; * two or more distinct instances within the same configuration, providing * several independent logical data links to a USB host. */ - +#include <linux/configfs.h> struct usb_function { const char *name; struct usb_gadget_strings **strings; @@ -163,6 +163,7 @@ struct usb_function { /* internals */ struct list_head list; DECLARE_BITMAP(endpoints, 32); + struct config_group group; }; int usb_add_function(struct usb_configuration *, struct usb_function *); @@ -443,13 +444,14 @@ struct usb_function_driver { const char *name; struct module *mod; struct list_head list; - struct usb_function *(*alloc)(void); + struct usb_function *(*alloc)(int configfs); }; void usb_function_unregister(struct usb_function_driver *f); int usb_function_register(struct usb_function_driver *newf); void usb_put_function(struct usb_function *f); struct usb_function *usb_get_function(const char *name); +struct usb_function *usb_get_function_configfs(const char *name); struct usb_configuration *usb_get_config(struct usb_composite_dev *cdev, int val); int usb_add_config_only(struct usb_composite_dev *cdev, diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 0af6569..6f5fe20 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -883,6 +883,11 @@ extern void usb_del_gadget_udc(struct usb_gadget *gadget); /*-------------------------------------------------------------------------*/ +struct config_group *udc_add_configfs(struct device *dev); +void udc_del_configfs(struct config_group *group); +int gadget_cfs_init(void); +void __exit gadget_cfs_exit(void); + /* utility to simplify dealing with string descriptors */ /** -- 1.7.10.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