188af8bbeSSebastian Andrzej Siewior #include <linux/configfs.h> 288af8bbeSSebastian Andrzej Siewior #include <linux/module.h> 388af8bbeSSebastian Andrzej Siewior #include <linux/slab.h> 488af8bbeSSebastian Andrzej Siewior #include <linux/device.h> 588af8bbeSSebastian Andrzej Siewior #include <linux/usb/composite.h> 688af8bbeSSebastian Andrzej Siewior #include <linux/usb/gadget_configfs.h> 70009e99aSRashika Kheria #include "configfs.h" 888af8bbeSSebastian Andrzej Siewior 988af8bbeSSebastian Andrzej Siewior int check_user_usb_string(const char *name, 1088af8bbeSSebastian Andrzej Siewior struct usb_gadget_strings *stringtab_dev) 1188af8bbeSSebastian Andrzej Siewior { 1288af8bbeSSebastian Andrzej Siewior unsigned primary_lang; 1388af8bbeSSebastian Andrzej Siewior unsigned sub_lang; 1488af8bbeSSebastian Andrzej Siewior u16 num; 1588af8bbeSSebastian Andrzej Siewior int ret; 1688af8bbeSSebastian Andrzej Siewior 1788af8bbeSSebastian Andrzej Siewior ret = kstrtou16(name, 0, &num); 1888af8bbeSSebastian Andrzej Siewior if (ret) 1988af8bbeSSebastian Andrzej Siewior return ret; 2088af8bbeSSebastian Andrzej Siewior 2188af8bbeSSebastian Andrzej Siewior primary_lang = num & 0x3ff; 2288af8bbeSSebastian Andrzej Siewior sub_lang = num >> 10; 2388af8bbeSSebastian Andrzej Siewior 2488af8bbeSSebastian Andrzej Siewior /* simple sanity check for valid langid */ 2588af8bbeSSebastian Andrzej Siewior switch (primary_lang) { 2688af8bbeSSebastian Andrzej Siewior case 0: 2788af8bbeSSebastian Andrzej Siewior case 0x62 ... 0xfe: 2888af8bbeSSebastian Andrzej Siewior case 0x100 ... 0x3ff: 2988af8bbeSSebastian Andrzej Siewior return -EINVAL; 3088af8bbeSSebastian Andrzej Siewior } 3188af8bbeSSebastian Andrzej Siewior if (!sub_lang) 3288af8bbeSSebastian Andrzej Siewior return -EINVAL; 3388af8bbeSSebastian Andrzej Siewior 3488af8bbeSSebastian Andrzej Siewior stringtab_dev->language = num; 3588af8bbeSSebastian Andrzej Siewior return 0; 3688af8bbeSSebastian Andrzej Siewior } 3788af8bbeSSebastian Andrzej Siewior 3888af8bbeSSebastian Andrzej Siewior #define MAX_NAME_LEN 40 3988af8bbeSSebastian Andrzej Siewior #define MAX_USB_STRING_LANGS 2 4088af8bbeSSebastian Andrzej Siewior 4188af8bbeSSebastian Andrzej Siewior struct gadget_info { 4288af8bbeSSebastian Andrzej Siewior struct config_group group; 4388af8bbeSSebastian Andrzej Siewior struct config_group functions_group; 4488af8bbeSSebastian Andrzej Siewior struct config_group configs_group; 4588af8bbeSSebastian Andrzej Siewior struct config_group strings_group; 4688af8bbeSSebastian Andrzej Siewior struct config_group *default_groups[4]; 4788af8bbeSSebastian Andrzej Siewior 4888af8bbeSSebastian Andrzej Siewior struct mutex lock; 4988af8bbeSSebastian Andrzej Siewior struct usb_gadget_strings *gstrings[MAX_USB_STRING_LANGS + 1]; 5088af8bbeSSebastian Andrzej Siewior struct list_head string_list; 5188af8bbeSSebastian Andrzej Siewior struct list_head available_func; 5288af8bbeSSebastian Andrzej Siewior 5388af8bbeSSebastian Andrzej Siewior const char *udc_name; 5488af8bbeSSebastian Andrzej Siewior #ifdef CONFIG_USB_OTG 5588af8bbeSSebastian Andrzej Siewior struct usb_otg_descriptor otg; 5688af8bbeSSebastian Andrzej Siewior #endif 5788af8bbeSSebastian Andrzej Siewior struct usb_composite_driver composite; 5888af8bbeSSebastian Andrzej Siewior struct usb_composite_dev cdev; 5988af8bbeSSebastian Andrzej Siewior }; 6088af8bbeSSebastian Andrzej Siewior 6188af8bbeSSebastian Andrzej Siewior struct config_usb_cfg { 6288af8bbeSSebastian Andrzej Siewior struct config_group group; 6388af8bbeSSebastian Andrzej Siewior struct config_group strings_group; 6488af8bbeSSebastian Andrzej Siewior struct config_group *default_groups[2]; 6588af8bbeSSebastian Andrzej Siewior struct list_head string_list; 6688af8bbeSSebastian Andrzej Siewior struct usb_configuration c; 6788af8bbeSSebastian Andrzej Siewior struct list_head func_list; 6888af8bbeSSebastian Andrzej Siewior struct usb_gadget_strings *gstrings[MAX_USB_STRING_LANGS + 1]; 6988af8bbeSSebastian Andrzej Siewior }; 7088af8bbeSSebastian Andrzej Siewior 7188af8bbeSSebastian Andrzej Siewior struct gadget_strings { 7288af8bbeSSebastian Andrzej Siewior struct usb_gadget_strings stringtab_dev; 7388af8bbeSSebastian Andrzej Siewior struct usb_string strings[USB_GADGET_FIRST_AVAIL_IDX]; 7488af8bbeSSebastian Andrzej Siewior char *manufacturer; 7588af8bbeSSebastian Andrzej Siewior char *product; 7688af8bbeSSebastian Andrzej Siewior char *serialnumber; 7788af8bbeSSebastian Andrzej Siewior 7888af8bbeSSebastian Andrzej Siewior struct config_group group; 7988af8bbeSSebastian Andrzej Siewior struct list_head list; 8088af8bbeSSebastian Andrzej Siewior }; 8188af8bbeSSebastian Andrzej Siewior 8288af8bbeSSebastian Andrzej Siewior struct gadget_config_name { 8388af8bbeSSebastian Andrzej Siewior struct usb_gadget_strings stringtab_dev; 8488af8bbeSSebastian Andrzej Siewior struct usb_string strings; 8588af8bbeSSebastian Andrzej Siewior char *configuration; 8688af8bbeSSebastian Andrzej Siewior 8788af8bbeSSebastian Andrzej Siewior struct config_group group; 8888af8bbeSSebastian Andrzej Siewior struct list_head list; 8988af8bbeSSebastian Andrzej Siewior }; 9088af8bbeSSebastian Andrzej Siewior 9188af8bbeSSebastian Andrzej Siewior static int usb_string_copy(const char *s, char **s_copy) 9288af8bbeSSebastian Andrzej Siewior { 9388af8bbeSSebastian Andrzej Siewior int ret; 9488af8bbeSSebastian Andrzej Siewior char *str; 9588af8bbeSSebastian Andrzej Siewior char *copy = *s_copy; 9688af8bbeSSebastian Andrzej Siewior ret = strlen(s); 9788af8bbeSSebastian Andrzej Siewior if (ret > 126) 9888af8bbeSSebastian Andrzej Siewior return -EOVERFLOW; 9988af8bbeSSebastian Andrzej Siewior 10088af8bbeSSebastian Andrzej Siewior str = kstrdup(s, GFP_KERNEL); 10188af8bbeSSebastian Andrzej Siewior if (!str) 10288af8bbeSSebastian Andrzej Siewior return -ENOMEM; 10388af8bbeSSebastian Andrzej Siewior if (str[ret - 1] == '\n') 10488af8bbeSSebastian Andrzej Siewior str[ret - 1] = '\0'; 10588af8bbeSSebastian Andrzej Siewior kfree(copy); 10688af8bbeSSebastian Andrzej Siewior *s_copy = str; 10788af8bbeSSebastian Andrzej Siewior return 0; 10888af8bbeSSebastian Andrzej Siewior } 10988af8bbeSSebastian Andrzej Siewior 11088af8bbeSSebastian Andrzej Siewior CONFIGFS_ATTR_STRUCT(gadget_info); 11188af8bbeSSebastian Andrzej Siewior CONFIGFS_ATTR_STRUCT(config_usb_cfg); 11288af8bbeSSebastian Andrzej Siewior 11388af8bbeSSebastian Andrzej Siewior #define GI_DEVICE_DESC_ITEM_ATTR(name) \ 11488af8bbeSSebastian Andrzej Siewior static struct gadget_info_attribute gadget_cdev_desc_##name = \ 11588af8bbeSSebastian Andrzej Siewior __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, \ 11688af8bbeSSebastian Andrzej Siewior gadget_dev_desc_##name##_show, \ 11788af8bbeSSebastian Andrzej Siewior gadget_dev_desc_##name##_store) 11888af8bbeSSebastian Andrzej Siewior 11988af8bbeSSebastian Andrzej Siewior #define GI_DEVICE_DESC_SIMPLE_R_u8(__name) \ 12088af8bbeSSebastian Andrzej Siewior static ssize_t gadget_dev_desc_##__name##_show(struct gadget_info *gi, \ 12188af8bbeSSebastian Andrzej Siewior char *page) \ 12288af8bbeSSebastian Andrzej Siewior { \ 12388af8bbeSSebastian Andrzej Siewior return sprintf(page, "0x%02x\n", gi->cdev.desc.__name); \ 12488af8bbeSSebastian Andrzej Siewior } 12588af8bbeSSebastian Andrzej Siewior 12688af8bbeSSebastian Andrzej Siewior #define GI_DEVICE_DESC_SIMPLE_R_u16(__name) \ 12788af8bbeSSebastian Andrzej Siewior static ssize_t gadget_dev_desc_##__name##_show(struct gadget_info *gi, \ 12888af8bbeSSebastian Andrzej Siewior char *page) \ 12988af8bbeSSebastian Andrzej Siewior { \ 13088af8bbeSSebastian Andrzej Siewior return sprintf(page, "0x%04x\n", le16_to_cpup(&gi->cdev.desc.__name)); \ 13188af8bbeSSebastian Andrzej Siewior } 13288af8bbeSSebastian Andrzej Siewior 13388af8bbeSSebastian Andrzej Siewior 13488af8bbeSSebastian Andrzej Siewior #define GI_DEVICE_DESC_SIMPLE_W_u8(_name) \ 13588af8bbeSSebastian Andrzej Siewior static ssize_t gadget_dev_desc_##_name##_store(struct gadget_info *gi, \ 13688af8bbeSSebastian Andrzej Siewior const char *page, size_t len) \ 13788af8bbeSSebastian Andrzej Siewior { \ 13888af8bbeSSebastian Andrzej Siewior u8 val; \ 13988af8bbeSSebastian Andrzej Siewior int ret; \ 14088af8bbeSSebastian Andrzej Siewior ret = kstrtou8(page, 0, &val); \ 14188af8bbeSSebastian Andrzej Siewior if (ret) \ 14288af8bbeSSebastian Andrzej Siewior return ret; \ 14388af8bbeSSebastian Andrzej Siewior gi->cdev.desc._name = val; \ 14488af8bbeSSebastian Andrzej Siewior return len; \ 14588af8bbeSSebastian Andrzej Siewior } 14688af8bbeSSebastian Andrzej Siewior 14788af8bbeSSebastian Andrzej Siewior #define GI_DEVICE_DESC_SIMPLE_W_u16(_name) \ 14888af8bbeSSebastian Andrzej Siewior static ssize_t gadget_dev_desc_##_name##_store(struct gadget_info *gi, \ 14988af8bbeSSebastian Andrzej Siewior const char *page, size_t len) \ 15088af8bbeSSebastian Andrzej Siewior { \ 15188af8bbeSSebastian Andrzej Siewior u16 val; \ 15288af8bbeSSebastian Andrzej Siewior int ret; \ 15388af8bbeSSebastian Andrzej Siewior ret = kstrtou16(page, 0, &val); \ 15488af8bbeSSebastian Andrzej Siewior if (ret) \ 15588af8bbeSSebastian Andrzej Siewior return ret; \ 15688af8bbeSSebastian Andrzej Siewior gi->cdev.desc._name = cpu_to_le16p(&val); \ 15788af8bbeSSebastian Andrzej Siewior return len; \ 15888af8bbeSSebastian Andrzej Siewior } 15988af8bbeSSebastian Andrzej Siewior 16088af8bbeSSebastian Andrzej Siewior #define GI_DEVICE_DESC_SIMPLE_RW(_name, _type) \ 16188af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_R_##_type(_name) \ 16288af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_W_##_type(_name) 16388af8bbeSSebastian Andrzej Siewior 16488af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_R_u16(bcdUSB); 16588af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(bDeviceClass, u8); 16688af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(bDeviceSubClass, u8); 16788af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(bDeviceProtocol, u8); 16888af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(bMaxPacketSize0, u8); 16988af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(idVendor, u16); 17088af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(idProduct, u16); 17188af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_R_u16(bcdDevice); 17288af8bbeSSebastian Andrzej Siewior 17388af8bbeSSebastian Andrzej Siewior static ssize_t is_valid_bcd(u16 bcd_val) 17488af8bbeSSebastian Andrzej Siewior { 17588af8bbeSSebastian Andrzej Siewior if ((bcd_val & 0xf) > 9) 17688af8bbeSSebastian Andrzej Siewior return -EINVAL; 17788af8bbeSSebastian Andrzej Siewior if (((bcd_val >> 4) & 0xf) > 9) 17888af8bbeSSebastian Andrzej Siewior return -EINVAL; 17988af8bbeSSebastian Andrzej Siewior if (((bcd_val >> 8) & 0xf) > 9) 18088af8bbeSSebastian Andrzej Siewior return -EINVAL; 18188af8bbeSSebastian Andrzej Siewior if (((bcd_val >> 12) & 0xf) > 9) 18288af8bbeSSebastian Andrzej Siewior return -EINVAL; 18388af8bbeSSebastian Andrzej Siewior return 0; 18488af8bbeSSebastian Andrzej Siewior } 18588af8bbeSSebastian Andrzej Siewior 18688af8bbeSSebastian Andrzej Siewior static ssize_t gadget_dev_desc_bcdDevice_store(struct gadget_info *gi, 18788af8bbeSSebastian Andrzej Siewior const char *page, size_t len) 18888af8bbeSSebastian Andrzej Siewior { 18988af8bbeSSebastian Andrzej Siewior u16 bcdDevice; 19088af8bbeSSebastian Andrzej Siewior int ret; 19188af8bbeSSebastian Andrzej Siewior 19288af8bbeSSebastian Andrzej Siewior ret = kstrtou16(page, 0, &bcdDevice); 19388af8bbeSSebastian Andrzej Siewior if (ret) 19488af8bbeSSebastian Andrzej Siewior return ret; 19588af8bbeSSebastian Andrzej Siewior ret = is_valid_bcd(bcdDevice); 19688af8bbeSSebastian Andrzej Siewior if (ret) 19788af8bbeSSebastian Andrzej Siewior return ret; 19888af8bbeSSebastian Andrzej Siewior 19988af8bbeSSebastian Andrzej Siewior gi->cdev.desc.bcdDevice = cpu_to_le16(bcdDevice); 20088af8bbeSSebastian Andrzej Siewior return len; 20188af8bbeSSebastian Andrzej Siewior } 20288af8bbeSSebastian Andrzej Siewior 20388af8bbeSSebastian Andrzej Siewior static ssize_t gadget_dev_desc_bcdUSB_store(struct gadget_info *gi, 20488af8bbeSSebastian Andrzej Siewior const char *page, size_t len) 20588af8bbeSSebastian Andrzej Siewior { 20688af8bbeSSebastian Andrzej Siewior u16 bcdUSB; 20788af8bbeSSebastian Andrzej Siewior int ret; 20888af8bbeSSebastian Andrzej Siewior 20988af8bbeSSebastian Andrzej Siewior ret = kstrtou16(page, 0, &bcdUSB); 21088af8bbeSSebastian Andrzej Siewior if (ret) 21188af8bbeSSebastian Andrzej Siewior return ret; 21288af8bbeSSebastian Andrzej Siewior ret = is_valid_bcd(bcdUSB); 21388af8bbeSSebastian Andrzej Siewior if (ret) 21488af8bbeSSebastian Andrzej Siewior return ret; 21588af8bbeSSebastian Andrzej Siewior 21688af8bbeSSebastian Andrzej Siewior gi->cdev.desc.bcdUSB = cpu_to_le16(bcdUSB); 21788af8bbeSSebastian Andrzej Siewior return len; 21888af8bbeSSebastian Andrzej Siewior } 21988af8bbeSSebastian Andrzej Siewior 22088af8bbeSSebastian Andrzej Siewior static ssize_t gadget_dev_desc_UDC_show(struct gadget_info *gi, char *page) 22188af8bbeSSebastian Andrzej Siewior { 22288af8bbeSSebastian Andrzej Siewior return sprintf(page, "%s\n", gi->udc_name ?: ""); 22388af8bbeSSebastian Andrzej Siewior } 22488af8bbeSSebastian Andrzej Siewior 22588af8bbeSSebastian Andrzej Siewior static int unregister_gadget(struct gadget_info *gi) 22688af8bbeSSebastian Andrzej Siewior { 22788af8bbeSSebastian Andrzej Siewior int ret; 22888af8bbeSSebastian Andrzej Siewior 22988af8bbeSSebastian Andrzej Siewior if (!gi->udc_name) 23088af8bbeSSebastian Andrzej Siewior return -ENODEV; 23188af8bbeSSebastian Andrzej Siewior 23288af8bbeSSebastian Andrzej Siewior ret = usb_gadget_unregister_driver(&gi->composite.gadget_driver); 23388af8bbeSSebastian Andrzej Siewior if (ret) 23488af8bbeSSebastian Andrzej Siewior return ret; 23588af8bbeSSebastian Andrzej Siewior kfree(gi->udc_name); 23688af8bbeSSebastian Andrzej Siewior gi->udc_name = NULL; 23788af8bbeSSebastian Andrzej Siewior return 0; 23888af8bbeSSebastian Andrzej Siewior } 23988af8bbeSSebastian Andrzej Siewior 24088af8bbeSSebastian Andrzej Siewior static ssize_t gadget_dev_desc_UDC_store(struct gadget_info *gi, 24188af8bbeSSebastian Andrzej Siewior const char *page, size_t len) 24288af8bbeSSebastian Andrzej Siewior { 24388af8bbeSSebastian Andrzej Siewior char *name; 24488af8bbeSSebastian Andrzej Siewior int ret; 24588af8bbeSSebastian Andrzej Siewior 24688af8bbeSSebastian Andrzej Siewior name = kstrdup(page, GFP_KERNEL); 24788af8bbeSSebastian Andrzej Siewior if (!name) 24888af8bbeSSebastian Andrzej Siewior return -ENOMEM; 24988af8bbeSSebastian Andrzej Siewior if (name[len - 1] == '\n') 25088af8bbeSSebastian Andrzej Siewior name[len - 1] = '\0'; 25188af8bbeSSebastian Andrzej Siewior 25288af8bbeSSebastian Andrzej Siewior mutex_lock(&gi->lock); 25388af8bbeSSebastian Andrzej Siewior 25488af8bbeSSebastian Andrzej Siewior if (!strlen(name)) { 25588af8bbeSSebastian Andrzej Siewior ret = unregister_gadget(gi); 25688af8bbeSSebastian Andrzej Siewior if (ret) 25788af8bbeSSebastian Andrzej Siewior goto err; 25888af8bbeSSebastian Andrzej Siewior } else { 25988af8bbeSSebastian Andrzej Siewior if (gi->udc_name) { 26088af8bbeSSebastian Andrzej Siewior ret = -EBUSY; 26188af8bbeSSebastian Andrzej Siewior goto err; 26288af8bbeSSebastian Andrzej Siewior } 26388af8bbeSSebastian Andrzej Siewior ret = udc_attach_driver(name, &gi->composite.gadget_driver); 26488af8bbeSSebastian Andrzej Siewior if (ret) 26588af8bbeSSebastian Andrzej Siewior goto err; 26688af8bbeSSebastian Andrzej Siewior gi->udc_name = name; 26788af8bbeSSebastian Andrzej Siewior } 26888af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 26988af8bbeSSebastian Andrzej Siewior return len; 27088af8bbeSSebastian Andrzej Siewior err: 27188af8bbeSSebastian Andrzej Siewior kfree(name); 27288af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 27388af8bbeSSebastian Andrzej Siewior return ret; 27488af8bbeSSebastian Andrzej Siewior } 27588af8bbeSSebastian Andrzej Siewior 27688af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_ITEM_ATTR(bDeviceClass); 27788af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_ITEM_ATTR(bDeviceSubClass); 27888af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_ITEM_ATTR(bDeviceProtocol); 27988af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_ITEM_ATTR(bMaxPacketSize0); 28088af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_ITEM_ATTR(idVendor); 28188af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_ITEM_ATTR(idProduct); 28288af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_ITEM_ATTR(bcdDevice); 28388af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_ITEM_ATTR(bcdUSB); 28488af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_ITEM_ATTR(UDC); 28588af8bbeSSebastian Andrzej Siewior 28688af8bbeSSebastian Andrzej Siewior static struct configfs_attribute *gadget_root_attrs[] = { 28788af8bbeSSebastian Andrzej Siewior &gadget_cdev_desc_bDeviceClass.attr, 28888af8bbeSSebastian Andrzej Siewior &gadget_cdev_desc_bDeviceSubClass.attr, 28988af8bbeSSebastian Andrzej Siewior &gadget_cdev_desc_bDeviceProtocol.attr, 29088af8bbeSSebastian Andrzej Siewior &gadget_cdev_desc_bMaxPacketSize0.attr, 29188af8bbeSSebastian Andrzej Siewior &gadget_cdev_desc_idVendor.attr, 29288af8bbeSSebastian Andrzej Siewior &gadget_cdev_desc_idProduct.attr, 29388af8bbeSSebastian Andrzej Siewior &gadget_cdev_desc_bcdDevice.attr, 29488af8bbeSSebastian Andrzej Siewior &gadget_cdev_desc_bcdUSB.attr, 29588af8bbeSSebastian Andrzej Siewior &gadget_cdev_desc_UDC.attr, 29688af8bbeSSebastian Andrzej Siewior NULL, 29788af8bbeSSebastian Andrzej Siewior }; 29888af8bbeSSebastian Andrzej Siewior 29988af8bbeSSebastian Andrzej Siewior static inline struct gadget_info *to_gadget_info(struct config_item *item) 30088af8bbeSSebastian Andrzej Siewior { 30188af8bbeSSebastian Andrzej Siewior return container_of(to_config_group(item), struct gadget_info, group); 30288af8bbeSSebastian Andrzej Siewior } 30388af8bbeSSebastian Andrzej Siewior 30488af8bbeSSebastian Andrzej Siewior static inline struct gadget_strings *to_gadget_strings(struct config_item *item) 30588af8bbeSSebastian Andrzej Siewior { 30688af8bbeSSebastian Andrzej Siewior return container_of(to_config_group(item), struct gadget_strings, 30788af8bbeSSebastian Andrzej Siewior group); 30888af8bbeSSebastian Andrzej Siewior } 30988af8bbeSSebastian Andrzej Siewior 31088af8bbeSSebastian Andrzej Siewior static inline struct gadget_config_name *to_gadget_config_name( 31188af8bbeSSebastian Andrzej Siewior struct config_item *item) 31288af8bbeSSebastian Andrzej Siewior { 31388af8bbeSSebastian Andrzej Siewior return container_of(to_config_group(item), struct gadget_config_name, 31488af8bbeSSebastian Andrzej Siewior group); 31588af8bbeSSebastian Andrzej Siewior } 31688af8bbeSSebastian Andrzej Siewior 31788af8bbeSSebastian Andrzej Siewior static inline struct config_usb_cfg *to_config_usb_cfg(struct config_item *item) 31888af8bbeSSebastian Andrzej Siewior { 31988af8bbeSSebastian Andrzej Siewior return container_of(to_config_group(item), struct config_usb_cfg, 32088af8bbeSSebastian Andrzej Siewior group); 32188af8bbeSSebastian Andrzej Siewior } 32288af8bbeSSebastian Andrzej Siewior 32388af8bbeSSebastian Andrzej Siewior static inline struct usb_function_instance *to_usb_function_instance( 32488af8bbeSSebastian Andrzej Siewior struct config_item *item) 32588af8bbeSSebastian Andrzej Siewior { 32688af8bbeSSebastian Andrzej Siewior return container_of(to_config_group(item), 32788af8bbeSSebastian Andrzej Siewior struct usb_function_instance, group); 32888af8bbeSSebastian Andrzej Siewior } 32988af8bbeSSebastian Andrzej Siewior 33088af8bbeSSebastian Andrzej Siewior static void gadget_info_attr_release(struct config_item *item) 33188af8bbeSSebastian Andrzej Siewior { 33288af8bbeSSebastian Andrzej Siewior struct gadget_info *gi = to_gadget_info(item); 33388af8bbeSSebastian Andrzej Siewior 33488af8bbeSSebastian Andrzej Siewior WARN_ON(!list_empty(&gi->cdev.configs)); 33588af8bbeSSebastian Andrzej Siewior WARN_ON(!list_empty(&gi->string_list)); 33688af8bbeSSebastian Andrzej Siewior WARN_ON(!list_empty(&gi->available_func)); 33788af8bbeSSebastian Andrzej Siewior kfree(gi->composite.gadget_driver.function); 33888af8bbeSSebastian Andrzej Siewior kfree(gi); 33988af8bbeSSebastian Andrzej Siewior } 34088af8bbeSSebastian Andrzej Siewior 34188af8bbeSSebastian Andrzej Siewior CONFIGFS_ATTR_OPS(gadget_info); 34288af8bbeSSebastian Andrzej Siewior 34388af8bbeSSebastian Andrzej Siewior static struct configfs_item_operations gadget_root_item_ops = { 34488af8bbeSSebastian Andrzej Siewior .release = gadget_info_attr_release, 34588af8bbeSSebastian Andrzej Siewior .show_attribute = gadget_info_attr_show, 34688af8bbeSSebastian Andrzej Siewior .store_attribute = gadget_info_attr_store, 34788af8bbeSSebastian Andrzej Siewior }; 34888af8bbeSSebastian Andrzej Siewior 34988af8bbeSSebastian Andrzej Siewior static void gadget_config_attr_release(struct config_item *item) 35088af8bbeSSebastian Andrzej Siewior { 35188af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg = to_config_usb_cfg(item); 35288af8bbeSSebastian Andrzej Siewior 35388af8bbeSSebastian Andrzej Siewior WARN_ON(!list_empty(&cfg->c.functions)); 35488af8bbeSSebastian Andrzej Siewior list_del(&cfg->c.list); 35588af8bbeSSebastian Andrzej Siewior kfree(cfg->c.label); 35688af8bbeSSebastian Andrzej Siewior kfree(cfg); 35788af8bbeSSebastian Andrzej Siewior } 35888af8bbeSSebastian Andrzej Siewior 35988af8bbeSSebastian Andrzej Siewior static int config_usb_cfg_link( 36088af8bbeSSebastian Andrzej Siewior struct config_item *usb_cfg_ci, 36188af8bbeSSebastian Andrzej Siewior struct config_item *usb_func_ci) 36288af8bbeSSebastian Andrzej Siewior { 36388af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg = to_config_usb_cfg(usb_cfg_ci); 36488af8bbeSSebastian Andrzej Siewior struct usb_composite_dev *cdev = cfg->c.cdev; 36588af8bbeSSebastian Andrzej Siewior struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev); 36688af8bbeSSebastian Andrzej Siewior 36788af8bbeSSebastian Andrzej Siewior struct config_group *group = to_config_group(usb_func_ci); 36888af8bbeSSebastian Andrzej Siewior struct usb_function_instance *fi = container_of(group, 36988af8bbeSSebastian Andrzej Siewior struct usb_function_instance, group); 37088af8bbeSSebastian Andrzej Siewior struct usb_function_instance *a_fi; 37188af8bbeSSebastian Andrzej Siewior struct usb_function *f; 37288af8bbeSSebastian Andrzej Siewior int ret; 37388af8bbeSSebastian Andrzej Siewior 37488af8bbeSSebastian Andrzej Siewior mutex_lock(&gi->lock); 37588af8bbeSSebastian Andrzej Siewior /* 37688af8bbeSSebastian Andrzej Siewior * Make sure this function is from within our _this_ gadget and not 37788af8bbeSSebastian Andrzej Siewior * from another gadget or a random directory. 37888af8bbeSSebastian Andrzej Siewior * Also a function instance can only be linked once. 37988af8bbeSSebastian Andrzej Siewior */ 38088af8bbeSSebastian Andrzej Siewior list_for_each_entry(a_fi, &gi->available_func, cfs_list) { 38188af8bbeSSebastian Andrzej Siewior if (a_fi == fi) 38288af8bbeSSebastian Andrzej Siewior break; 38388af8bbeSSebastian Andrzej Siewior } 38488af8bbeSSebastian Andrzej Siewior if (a_fi != fi) { 38588af8bbeSSebastian Andrzej Siewior ret = -EINVAL; 38688af8bbeSSebastian Andrzej Siewior goto out; 38788af8bbeSSebastian Andrzej Siewior } 38888af8bbeSSebastian Andrzej Siewior 38988af8bbeSSebastian Andrzej Siewior list_for_each_entry(f, &cfg->func_list, list) { 39088af8bbeSSebastian Andrzej Siewior if (f->fi == fi) { 39188af8bbeSSebastian Andrzej Siewior ret = -EEXIST; 39288af8bbeSSebastian Andrzej Siewior goto out; 39388af8bbeSSebastian Andrzej Siewior } 39488af8bbeSSebastian Andrzej Siewior } 39588af8bbeSSebastian Andrzej Siewior 39688af8bbeSSebastian Andrzej Siewior f = usb_get_function(fi); 39788af8bbeSSebastian Andrzej Siewior if (IS_ERR(f)) { 39888af8bbeSSebastian Andrzej Siewior ret = PTR_ERR(f); 39988af8bbeSSebastian Andrzej Siewior goto out; 40088af8bbeSSebastian Andrzej Siewior } 40188af8bbeSSebastian Andrzej Siewior 40288af8bbeSSebastian Andrzej Siewior /* stash the function until we bind it to the gadget */ 40388af8bbeSSebastian Andrzej Siewior list_add_tail(&f->list, &cfg->func_list); 40488af8bbeSSebastian Andrzej Siewior ret = 0; 40588af8bbeSSebastian Andrzej Siewior out: 40688af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 40788af8bbeSSebastian Andrzej Siewior return ret; 40888af8bbeSSebastian Andrzej Siewior } 40988af8bbeSSebastian Andrzej Siewior 41088af8bbeSSebastian Andrzej Siewior static int config_usb_cfg_unlink( 41188af8bbeSSebastian Andrzej Siewior struct config_item *usb_cfg_ci, 41288af8bbeSSebastian Andrzej Siewior struct config_item *usb_func_ci) 41388af8bbeSSebastian Andrzej Siewior { 41488af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg = to_config_usb_cfg(usb_cfg_ci); 41588af8bbeSSebastian Andrzej Siewior struct usb_composite_dev *cdev = cfg->c.cdev; 41688af8bbeSSebastian Andrzej Siewior struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev); 41788af8bbeSSebastian Andrzej Siewior 41888af8bbeSSebastian Andrzej Siewior struct config_group *group = to_config_group(usb_func_ci); 41988af8bbeSSebastian Andrzej Siewior struct usb_function_instance *fi = container_of(group, 42088af8bbeSSebastian Andrzej Siewior struct usb_function_instance, group); 42188af8bbeSSebastian Andrzej Siewior struct usb_function *f; 42288af8bbeSSebastian Andrzej Siewior 42388af8bbeSSebastian Andrzej Siewior /* 42488af8bbeSSebastian Andrzej Siewior * ideally I would like to forbid to unlink functions while a gadget is 42588af8bbeSSebastian Andrzej Siewior * bound to an UDC. Since this isn't possible at the moment, we simply 42688af8bbeSSebastian Andrzej Siewior * force an unbind, the function is available here and then we can 42788af8bbeSSebastian Andrzej Siewior * remove the function. 42888af8bbeSSebastian Andrzej Siewior */ 42988af8bbeSSebastian Andrzej Siewior mutex_lock(&gi->lock); 43088af8bbeSSebastian Andrzej Siewior if (gi->udc_name) 43188af8bbeSSebastian Andrzej Siewior unregister_gadget(gi); 43288af8bbeSSebastian Andrzej Siewior WARN_ON(gi->udc_name); 43388af8bbeSSebastian Andrzej Siewior 43488af8bbeSSebastian Andrzej Siewior list_for_each_entry(f, &cfg->func_list, list) { 43588af8bbeSSebastian Andrzej Siewior if (f->fi == fi) { 43688af8bbeSSebastian Andrzej Siewior list_del(&f->list); 43788af8bbeSSebastian Andrzej Siewior usb_put_function(f); 43888af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 43988af8bbeSSebastian Andrzej Siewior return 0; 44088af8bbeSSebastian Andrzej Siewior } 44188af8bbeSSebastian Andrzej Siewior } 44288af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 44375bfe23aSDavid Rientjes WARN(1, "Unable to locate function to unbind\n"); 44488af8bbeSSebastian Andrzej Siewior return 0; 44588af8bbeSSebastian Andrzej Siewior } 44688af8bbeSSebastian Andrzej Siewior 44788af8bbeSSebastian Andrzej Siewior CONFIGFS_ATTR_OPS(config_usb_cfg); 44888af8bbeSSebastian Andrzej Siewior 44988af8bbeSSebastian Andrzej Siewior static struct configfs_item_operations gadget_config_item_ops = { 45088af8bbeSSebastian Andrzej Siewior .release = gadget_config_attr_release, 45188af8bbeSSebastian Andrzej Siewior .show_attribute = config_usb_cfg_attr_show, 45288af8bbeSSebastian Andrzej Siewior .store_attribute = config_usb_cfg_attr_store, 45388af8bbeSSebastian Andrzej Siewior .allow_link = config_usb_cfg_link, 45488af8bbeSSebastian Andrzej Siewior .drop_link = config_usb_cfg_unlink, 45588af8bbeSSebastian Andrzej Siewior }; 45688af8bbeSSebastian Andrzej Siewior 45788af8bbeSSebastian Andrzej Siewior 45888af8bbeSSebastian Andrzej Siewior static ssize_t gadget_config_desc_MaxPower_show(struct config_usb_cfg *cfg, 45988af8bbeSSebastian Andrzej Siewior char *page) 46088af8bbeSSebastian Andrzej Siewior { 46188af8bbeSSebastian Andrzej Siewior return sprintf(page, "%u\n", cfg->c.MaxPower); 46288af8bbeSSebastian Andrzej Siewior } 46388af8bbeSSebastian Andrzej Siewior 46488af8bbeSSebastian Andrzej Siewior static ssize_t gadget_config_desc_MaxPower_store(struct config_usb_cfg *cfg, 46588af8bbeSSebastian Andrzej Siewior const char *page, size_t len) 46688af8bbeSSebastian Andrzej Siewior { 46788af8bbeSSebastian Andrzej Siewior u16 val; 46888af8bbeSSebastian Andrzej Siewior int ret; 46988af8bbeSSebastian Andrzej Siewior ret = kstrtou16(page, 0, &val); 47088af8bbeSSebastian Andrzej Siewior if (ret) 47188af8bbeSSebastian Andrzej Siewior return ret; 47288af8bbeSSebastian Andrzej Siewior if (DIV_ROUND_UP(val, 8) > 0xff) 47388af8bbeSSebastian Andrzej Siewior return -ERANGE; 47488af8bbeSSebastian Andrzej Siewior cfg->c.MaxPower = val; 47588af8bbeSSebastian Andrzej Siewior return len; 47688af8bbeSSebastian Andrzej Siewior } 47788af8bbeSSebastian Andrzej Siewior 47888af8bbeSSebastian Andrzej Siewior static ssize_t gadget_config_desc_bmAttributes_show(struct config_usb_cfg *cfg, 47988af8bbeSSebastian Andrzej Siewior char *page) 48088af8bbeSSebastian Andrzej Siewior { 48188af8bbeSSebastian Andrzej Siewior return sprintf(page, "0x%02x\n", cfg->c.bmAttributes); 48288af8bbeSSebastian Andrzej Siewior } 48388af8bbeSSebastian Andrzej Siewior 48488af8bbeSSebastian Andrzej Siewior static ssize_t gadget_config_desc_bmAttributes_store(struct config_usb_cfg *cfg, 48588af8bbeSSebastian Andrzej Siewior const char *page, size_t len) 48688af8bbeSSebastian Andrzej Siewior { 48788af8bbeSSebastian Andrzej Siewior u8 val; 48888af8bbeSSebastian Andrzej Siewior int ret; 48988af8bbeSSebastian Andrzej Siewior ret = kstrtou8(page, 0, &val); 49088af8bbeSSebastian Andrzej Siewior if (ret) 49188af8bbeSSebastian Andrzej Siewior return ret; 49288af8bbeSSebastian Andrzej Siewior if (!(val & USB_CONFIG_ATT_ONE)) 49388af8bbeSSebastian Andrzej Siewior return -EINVAL; 49488af8bbeSSebastian Andrzej Siewior if (val & ~(USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER | 49588af8bbeSSebastian Andrzej Siewior USB_CONFIG_ATT_WAKEUP)) 49688af8bbeSSebastian Andrzej Siewior return -EINVAL; 49788af8bbeSSebastian Andrzej Siewior cfg->c.bmAttributes = val; 49888af8bbeSSebastian Andrzej Siewior return len; 49988af8bbeSSebastian Andrzej Siewior } 50088af8bbeSSebastian Andrzej Siewior 50188af8bbeSSebastian Andrzej Siewior #define CFG_CONFIG_DESC_ITEM_ATTR(name) \ 50288af8bbeSSebastian Andrzej Siewior static struct config_usb_cfg_attribute gadget_usb_cfg_##name = \ 50388af8bbeSSebastian Andrzej Siewior __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, \ 50488af8bbeSSebastian Andrzej Siewior gadget_config_desc_##name##_show, \ 50588af8bbeSSebastian Andrzej Siewior gadget_config_desc_##name##_store) 50688af8bbeSSebastian Andrzej Siewior 50788af8bbeSSebastian Andrzej Siewior CFG_CONFIG_DESC_ITEM_ATTR(MaxPower); 50888af8bbeSSebastian Andrzej Siewior CFG_CONFIG_DESC_ITEM_ATTR(bmAttributes); 50988af8bbeSSebastian Andrzej Siewior 51088af8bbeSSebastian Andrzej Siewior static struct configfs_attribute *gadget_config_attrs[] = { 51188af8bbeSSebastian Andrzej Siewior &gadget_usb_cfg_MaxPower.attr, 51288af8bbeSSebastian Andrzej Siewior &gadget_usb_cfg_bmAttributes.attr, 51388af8bbeSSebastian Andrzej Siewior NULL, 51488af8bbeSSebastian Andrzej Siewior }; 51588af8bbeSSebastian Andrzej Siewior 51688af8bbeSSebastian Andrzej Siewior static struct config_item_type gadget_config_type = { 51788af8bbeSSebastian Andrzej Siewior .ct_item_ops = &gadget_config_item_ops, 51888af8bbeSSebastian Andrzej Siewior .ct_attrs = gadget_config_attrs, 51988af8bbeSSebastian Andrzej Siewior .ct_owner = THIS_MODULE, 52088af8bbeSSebastian Andrzej Siewior }; 52188af8bbeSSebastian Andrzej Siewior 52288af8bbeSSebastian Andrzej Siewior static struct config_item_type gadget_root_type = { 52388af8bbeSSebastian Andrzej Siewior .ct_item_ops = &gadget_root_item_ops, 52488af8bbeSSebastian Andrzej Siewior .ct_attrs = gadget_root_attrs, 52588af8bbeSSebastian Andrzej Siewior .ct_owner = THIS_MODULE, 52688af8bbeSSebastian Andrzej Siewior }; 52788af8bbeSSebastian Andrzej Siewior 52888af8bbeSSebastian Andrzej Siewior static void composite_init_dev(struct usb_composite_dev *cdev) 52988af8bbeSSebastian Andrzej Siewior { 53088af8bbeSSebastian Andrzej Siewior spin_lock_init(&cdev->lock); 53188af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&cdev->configs); 53288af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&cdev->gstrings); 53388af8bbeSSebastian Andrzej Siewior } 53488af8bbeSSebastian Andrzej Siewior 53588af8bbeSSebastian Andrzej Siewior static struct config_group *function_make( 53688af8bbeSSebastian Andrzej Siewior struct config_group *group, 53788af8bbeSSebastian Andrzej Siewior const char *name) 53888af8bbeSSebastian Andrzej Siewior { 53988af8bbeSSebastian Andrzej Siewior struct gadget_info *gi; 54088af8bbeSSebastian Andrzej Siewior struct usb_function_instance *fi; 54188af8bbeSSebastian Andrzej Siewior char buf[MAX_NAME_LEN]; 54288af8bbeSSebastian Andrzej Siewior char *func_name; 54388af8bbeSSebastian Andrzej Siewior char *instance_name; 54488af8bbeSSebastian Andrzej Siewior int ret; 54588af8bbeSSebastian Andrzej Siewior 54688af8bbeSSebastian Andrzej Siewior ret = snprintf(buf, MAX_NAME_LEN, "%s", name); 54788af8bbeSSebastian Andrzej Siewior if (ret >= MAX_NAME_LEN) 54888af8bbeSSebastian Andrzej Siewior return ERR_PTR(-ENAMETOOLONG); 54988af8bbeSSebastian Andrzej Siewior 55088af8bbeSSebastian Andrzej Siewior func_name = buf; 55188af8bbeSSebastian Andrzej Siewior instance_name = strchr(func_name, '.'); 55288af8bbeSSebastian Andrzej Siewior if (!instance_name) { 55388af8bbeSSebastian Andrzej Siewior pr_err("Unable to locate . in FUNC.INSTANCE\n"); 55488af8bbeSSebastian Andrzej Siewior return ERR_PTR(-EINVAL); 55588af8bbeSSebastian Andrzej Siewior } 55688af8bbeSSebastian Andrzej Siewior *instance_name = '\0'; 55788af8bbeSSebastian Andrzej Siewior instance_name++; 55888af8bbeSSebastian Andrzej Siewior 55988af8bbeSSebastian Andrzej Siewior fi = usb_get_function_instance(func_name); 56088af8bbeSSebastian Andrzej Siewior if (IS_ERR(fi)) 561a3469411SDuan Jiong return ERR_CAST(fi); 56288af8bbeSSebastian Andrzej Siewior 56388af8bbeSSebastian Andrzej Siewior ret = config_item_set_name(&fi->group.cg_item, name); 56488af8bbeSSebastian Andrzej Siewior if (ret) { 56588af8bbeSSebastian Andrzej Siewior usb_put_function_instance(fi); 56688af8bbeSSebastian Andrzej Siewior return ERR_PTR(ret); 56788af8bbeSSebastian Andrzej Siewior } 5681933861dSAndrzej Pietrasiewicz if (fi->set_inst_name) { 5691933861dSAndrzej Pietrasiewicz ret = fi->set_inst_name(fi, instance_name); 5701933861dSAndrzej Pietrasiewicz if (ret) { 5711933861dSAndrzej Pietrasiewicz usb_put_function_instance(fi); 5721933861dSAndrzej Pietrasiewicz return ERR_PTR(ret); 5731933861dSAndrzej Pietrasiewicz } 5741933861dSAndrzej Pietrasiewicz } 57588af8bbeSSebastian Andrzej Siewior 57688af8bbeSSebastian Andrzej Siewior gi = container_of(group, struct gadget_info, functions_group); 57788af8bbeSSebastian Andrzej Siewior 57888af8bbeSSebastian Andrzej Siewior mutex_lock(&gi->lock); 57988af8bbeSSebastian Andrzej Siewior list_add_tail(&fi->cfs_list, &gi->available_func); 58088af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 58188af8bbeSSebastian Andrzej Siewior return &fi->group; 58288af8bbeSSebastian Andrzej Siewior } 58388af8bbeSSebastian Andrzej Siewior 58488af8bbeSSebastian Andrzej Siewior static void function_drop( 58588af8bbeSSebastian Andrzej Siewior struct config_group *group, 58688af8bbeSSebastian Andrzej Siewior struct config_item *item) 58788af8bbeSSebastian Andrzej Siewior { 58888af8bbeSSebastian Andrzej Siewior struct usb_function_instance *fi = to_usb_function_instance(item); 58988af8bbeSSebastian Andrzej Siewior struct gadget_info *gi; 59088af8bbeSSebastian Andrzej Siewior 59188af8bbeSSebastian Andrzej Siewior gi = container_of(group, struct gadget_info, functions_group); 59288af8bbeSSebastian Andrzej Siewior 59388af8bbeSSebastian Andrzej Siewior mutex_lock(&gi->lock); 59488af8bbeSSebastian Andrzej Siewior list_del(&fi->cfs_list); 59588af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 59688af8bbeSSebastian Andrzej Siewior config_item_put(item); 59788af8bbeSSebastian Andrzej Siewior } 59888af8bbeSSebastian Andrzej Siewior 59988af8bbeSSebastian Andrzej Siewior static struct configfs_group_operations functions_ops = { 60088af8bbeSSebastian Andrzej Siewior .make_group = &function_make, 60188af8bbeSSebastian Andrzej Siewior .drop_item = &function_drop, 60288af8bbeSSebastian Andrzej Siewior }; 60388af8bbeSSebastian Andrzej Siewior 60488af8bbeSSebastian Andrzej Siewior static struct config_item_type functions_type = { 60588af8bbeSSebastian Andrzej Siewior .ct_group_ops = &functions_ops, 60688af8bbeSSebastian Andrzej Siewior .ct_owner = THIS_MODULE, 60788af8bbeSSebastian Andrzej Siewior }; 60888af8bbeSSebastian Andrzej Siewior 60988af8bbeSSebastian Andrzej Siewior CONFIGFS_ATTR_STRUCT(gadget_config_name); 61088af8bbeSSebastian Andrzej Siewior GS_STRINGS_RW(gadget_config_name, configuration); 61188af8bbeSSebastian Andrzej Siewior 61288af8bbeSSebastian Andrzej Siewior static struct configfs_attribute *gadget_config_name_langid_attrs[] = { 61388af8bbeSSebastian Andrzej Siewior &gadget_config_name_configuration.attr, 61488af8bbeSSebastian Andrzej Siewior NULL, 61588af8bbeSSebastian Andrzej Siewior }; 61688af8bbeSSebastian Andrzej Siewior 61788af8bbeSSebastian Andrzej Siewior static void gadget_config_name_attr_release(struct config_item *item) 61888af8bbeSSebastian Andrzej Siewior { 61988af8bbeSSebastian Andrzej Siewior struct gadget_config_name *cn = to_gadget_config_name(item); 62088af8bbeSSebastian Andrzej Siewior 62188af8bbeSSebastian Andrzej Siewior kfree(cn->configuration); 62288af8bbeSSebastian Andrzej Siewior 62388af8bbeSSebastian Andrzej Siewior list_del(&cn->list); 62488af8bbeSSebastian Andrzej Siewior kfree(cn); 62588af8bbeSSebastian Andrzej Siewior } 62688af8bbeSSebastian Andrzej Siewior 62788af8bbeSSebastian Andrzej Siewior USB_CONFIG_STRING_RW_OPS(gadget_config_name); 62888af8bbeSSebastian Andrzej Siewior USB_CONFIG_STRINGS_LANG(gadget_config_name, config_usb_cfg); 62988af8bbeSSebastian Andrzej Siewior 63088af8bbeSSebastian Andrzej Siewior static struct config_group *config_desc_make( 63188af8bbeSSebastian Andrzej Siewior struct config_group *group, 63288af8bbeSSebastian Andrzej Siewior const char *name) 63388af8bbeSSebastian Andrzej Siewior { 63488af8bbeSSebastian Andrzej Siewior struct gadget_info *gi; 63588af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg; 63688af8bbeSSebastian Andrzej Siewior char buf[MAX_NAME_LEN]; 63788af8bbeSSebastian Andrzej Siewior char *num_str; 63888af8bbeSSebastian Andrzej Siewior u8 num; 63988af8bbeSSebastian Andrzej Siewior int ret; 64088af8bbeSSebastian Andrzej Siewior 64188af8bbeSSebastian Andrzej Siewior gi = container_of(group, struct gadget_info, configs_group); 64288af8bbeSSebastian Andrzej Siewior ret = snprintf(buf, MAX_NAME_LEN, "%s", name); 64388af8bbeSSebastian Andrzej Siewior if (ret >= MAX_NAME_LEN) 64488af8bbeSSebastian Andrzej Siewior return ERR_PTR(-ENAMETOOLONG); 64588af8bbeSSebastian Andrzej Siewior 64688af8bbeSSebastian Andrzej Siewior num_str = strchr(buf, '.'); 64788af8bbeSSebastian Andrzej Siewior if (!num_str) { 64888af8bbeSSebastian Andrzej Siewior pr_err("Unable to locate . in name.bConfigurationValue\n"); 64988af8bbeSSebastian Andrzej Siewior return ERR_PTR(-EINVAL); 65088af8bbeSSebastian Andrzej Siewior } 65188af8bbeSSebastian Andrzej Siewior 65288af8bbeSSebastian Andrzej Siewior *num_str = '\0'; 65388af8bbeSSebastian Andrzej Siewior num_str++; 65488af8bbeSSebastian Andrzej Siewior 65588af8bbeSSebastian Andrzej Siewior if (!strlen(buf)) 65688af8bbeSSebastian Andrzej Siewior return ERR_PTR(-EINVAL); 65788af8bbeSSebastian Andrzej Siewior 65888af8bbeSSebastian Andrzej Siewior ret = kstrtou8(num_str, 0, &num); 65988af8bbeSSebastian Andrzej Siewior if (ret) 66088af8bbeSSebastian Andrzej Siewior return ERR_PTR(ret); 66188af8bbeSSebastian Andrzej Siewior 66288af8bbeSSebastian Andrzej Siewior cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); 66388af8bbeSSebastian Andrzej Siewior if (!cfg) 66488af8bbeSSebastian Andrzej Siewior return ERR_PTR(-ENOMEM); 66588af8bbeSSebastian Andrzej Siewior cfg->c.label = kstrdup(buf, GFP_KERNEL); 66688af8bbeSSebastian Andrzej Siewior if (!cfg->c.label) { 66788af8bbeSSebastian Andrzej Siewior ret = -ENOMEM; 66888af8bbeSSebastian Andrzej Siewior goto err; 66988af8bbeSSebastian Andrzej Siewior } 67088af8bbeSSebastian Andrzej Siewior cfg->c.bConfigurationValue = num; 67188af8bbeSSebastian Andrzej Siewior cfg->c.MaxPower = CONFIG_USB_GADGET_VBUS_DRAW; 67288af8bbeSSebastian Andrzej Siewior cfg->c.bmAttributes = USB_CONFIG_ATT_ONE; 67388af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&cfg->string_list); 67488af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&cfg->func_list); 67588af8bbeSSebastian Andrzej Siewior 67688af8bbeSSebastian Andrzej Siewior cfg->group.default_groups = cfg->default_groups; 67788af8bbeSSebastian Andrzej Siewior cfg->default_groups[0] = &cfg->strings_group; 67888af8bbeSSebastian Andrzej Siewior 67988af8bbeSSebastian Andrzej Siewior config_group_init_type_name(&cfg->group, name, 68088af8bbeSSebastian Andrzej Siewior &gadget_config_type); 68188af8bbeSSebastian Andrzej Siewior config_group_init_type_name(&cfg->strings_group, "strings", 68288af8bbeSSebastian Andrzej Siewior &gadget_config_name_strings_type); 68388af8bbeSSebastian Andrzej Siewior 68488af8bbeSSebastian Andrzej Siewior ret = usb_add_config_only(&gi->cdev, &cfg->c); 68588af8bbeSSebastian Andrzej Siewior if (ret) 68688af8bbeSSebastian Andrzej Siewior goto err; 68788af8bbeSSebastian Andrzej Siewior 68888af8bbeSSebastian Andrzej Siewior return &cfg->group; 68988af8bbeSSebastian Andrzej Siewior err: 69088af8bbeSSebastian Andrzej Siewior kfree(cfg->c.label); 69188af8bbeSSebastian Andrzej Siewior kfree(cfg); 69288af8bbeSSebastian Andrzej Siewior return ERR_PTR(ret); 69388af8bbeSSebastian Andrzej Siewior } 69488af8bbeSSebastian Andrzej Siewior 69588af8bbeSSebastian Andrzej Siewior static void config_desc_drop( 69688af8bbeSSebastian Andrzej Siewior struct config_group *group, 69788af8bbeSSebastian Andrzej Siewior struct config_item *item) 69888af8bbeSSebastian Andrzej Siewior { 69988af8bbeSSebastian Andrzej Siewior config_item_put(item); 70088af8bbeSSebastian Andrzej Siewior } 70188af8bbeSSebastian Andrzej Siewior 70288af8bbeSSebastian Andrzej Siewior static struct configfs_group_operations config_desc_ops = { 70388af8bbeSSebastian Andrzej Siewior .make_group = &config_desc_make, 70488af8bbeSSebastian Andrzej Siewior .drop_item = &config_desc_drop, 70588af8bbeSSebastian Andrzej Siewior }; 70688af8bbeSSebastian Andrzej Siewior 70788af8bbeSSebastian Andrzej Siewior static struct config_item_type config_desc_type = { 70888af8bbeSSebastian Andrzej Siewior .ct_group_ops = &config_desc_ops, 70988af8bbeSSebastian Andrzej Siewior .ct_owner = THIS_MODULE, 71088af8bbeSSebastian Andrzej Siewior }; 71188af8bbeSSebastian Andrzej Siewior 71288af8bbeSSebastian Andrzej Siewior CONFIGFS_ATTR_STRUCT(gadget_strings); 71388af8bbeSSebastian Andrzej Siewior GS_STRINGS_RW(gadget_strings, manufacturer); 71488af8bbeSSebastian Andrzej Siewior GS_STRINGS_RW(gadget_strings, product); 71588af8bbeSSebastian Andrzej Siewior GS_STRINGS_RW(gadget_strings, serialnumber); 71688af8bbeSSebastian Andrzej Siewior 71788af8bbeSSebastian Andrzej Siewior static struct configfs_attribute *gadget_strings_langid_attrs[] = { 71888af8bbeSSebastian Andrzej Siewior &gadget_strings_manufacturer.attr, 71988af8bbeSSebastian Andrzej Siewior &gadget_strings_product.attr, 72088af8bbeSSebastian Andrzej Siewior &gadget_strings_serialnumber.attr, 72188af8bbeSSebastian Andrzej Siewior NULL, 72288af8bbeSSebastian Andrzej Siewior }; 72388af8bbeSSebastian Andrzej Siewior 72488af8bbeSSebastian Andrzej Siewior static void gadget_strings_attr_release(struct config_item *item) 72588af8bbeSSebastian Andrzej Siewior { 72688af8bbeSSebastian Andrzej Siewior struct gadget_strings *gs = to_gadget_strings(item); 72788af8bbeSSebastian Andrzej Siewior 72888af8bbeSSebastian Andrzej Siewior kfree(gs->manufacturer); 72988af8bbeSSebastian Andrzej Siewior kfree(gs->product); 73088af8bbeSSebastian Andrzej Siewior kfree(gs->serialnumber); 73188af8bbeSSebastian Andrzej Siewior 73288af8bbeSSebastian Andrzej Siewior list_del(&gs->list); 73388af8bbeSSebastian Andrzej Siewior kfree(gs); 73488af8bbeSSebastian Andrzej Siewior } 73588af8bbeSSebastian Andrzej Siewior 73688af8bbeSSebastian Andrzej Siewior USB_CONFIG_STRING_RW_OPS(gadget_strings); 73788af8bbeSSebastian Andrzej Siewior USB_CONFIG_STRINGS_LANG(gadget_strings, gadget_info); 73888af8bbeSSebastian Andrzej Siewior 73988af8bbeSSebastian Andrzej Siewior static int configfs_do_nothing(struct usb_composite_dev *cdev) 74088af8bbeSSebastian Andrzej Siewior { 74175bfe23aSDavid Rientjes WARN_ON(1); 74288af8bbeSSebastian Andrzej Siewior return -EINVAL; 74388af8bbeSSebastian Andrzej Siewior } 74488af8bbeSSebastian Andrzej Siewior 74588af8bbeSSebastian Andrzej Siewior int composite_dev_prepare(struct usb_composite_driver *composite, 74688af8bbeSSebastian Andrzej Siewior struct usb_composite_dev *dev); 74788af8bbeSSebastian Andrzej Siewior 74888af8bbeSSebastian Andrzej Siewior static void purge_configs_funcs(struct gadget_info *gi) 74988af8bbeSSebastian Andrzej Siewior { 75088af8bbeSSebastian Andrzej Siewior struct usb_configuration *c; 75188af8bbeSSebastian Andrzej Siewior 75288af8bbeSSebastian Andrzej Siewior list_for_each_entry(c, &gi->cdev.configs, list) { 75388af8bbeSSebastian Andrzej Siewior struct usb_function *f, *tmp; 75488af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg; 75588af8bbeSSebastian Andrzej Siewior 75688af8bbeSSebastian Andrzej Siewior cfg = container_of(c, struct config_usb_cfg, c); 75788af8bbeSSebastian Andrzej Siewior 75888af8bbeSSebastian Andrzej Siewior list_for_each_entry_safe(f, tmp, &c->functions, list) { 75988af8bbeSSebastian Andrzej Siewior 76088af8bbeSSebastian Andrzej Siewior list_move_tail(&f->list, &cfg->func_list); 76188af8bbeSSebastian Andrzej Siewior if (f->unbind) { 76288af8bbeSSebastian Andrzej Siewior dev_err(&gi->cdev.gadget->dev, "unbind function" 76388af8bbeSSebastian Andrzej Siewior " '%s'/%p\n", f->name, f); 76488af8bbeSSebastian Andrzej Siewior f->unbind(c, f); 76588af8bbeSSebastian Andrzej Siewior } 76688af8bbeSSebastian Andrzej Siewior } 76788af8bbeSSebastian Andrzej Siewior c->next_interface_id = 0; 76888af8bbeSSebastian Andrzej Siewior c->superspeed = 0; 76988af8bbeSSebastian Andrzej Siewior c->highspeed = 0; 77088af8bbeSSebastian Andrzej Siewior c->fullspeed = 0; 77188af8bbeSSebastian Andrzej Siewior } 77288af8bbeSSebastian Andrzej Siewior } 77388af8bbeSSebastian Andrzej Siewior 77488af8bbeSSebastian Andrzej Siewior static int configfs_composite_bind(struct usb_gadget *gadget, 77588af8bbeSSebastian Andrzej Siewior struct usb_gadget_driver *gdriver) 77688af8bbeSSebastian Andrzej Siewior { 77788af8bbeSSebastian Andrzej Siewior struct usb_composite_driver *composite = to_cdriver(gdriver); 77888af8bbeSSebastian Andrzej Siewior struct gadget_info *gi = container_of(composite, 77988af8bbeSSebastian Andrzej Siewior struct gadget_info, composite); 78088af8bbeSSebastian Andrzej Siewior struct usb_composite_dev *cdev = &gi->cdev; 78188af8bbeSSebastian Andrzej Siewior struct usb_configuration *c; 78288af8bbeSSebastian Andrzej Siewior struct usb_string *s; 78388af8bbeSSebastian Andrzej Siewior unsigned i; 78488af8bbeSSebastian Andrzej Siewior int ret; 78588af8bbeSSebastian Andrzej Siewior 78688af8bbeSSebastian Andrzej Siewior /* the gi->lock is hold by the caller */ 78788af8bbeSSebastian Andrzej Siewior cdev->gadget = gadget; 78888af8bbeSSebastian Andrzej Siewior set_gadget_data(gadget, cdev); 78988af8bbeSSebastian Andrzej Siewior ret = composite_dev_prepare(composite, cdev); 79088af8bbeSSebastian Andrzej Siewior if (ret) 79188af8bbeSSebastian Andrzej Siewior return ret; 79288af8bbeSSebastian Andrzej Siewior /* and now the gadget bind */ 79388af8bbeSSebastian Andrzej Siewior ret = -EINVAL; 79488af8bbeSSebastian Andrzej Siewior 79588af8bbeSSebastian Andrzej Siewior if (list_empty(&gi->cdev.configs)) { 79688af8bbeSSebastian Andrzej Siewior pr_err("Need atleast one configuration in %s.\n", 79788af8bbeSSebastian Andrzej Siewior gi->composite.name); 79888af8bbeSSebastian Andrzej Siewior goto err_comp_cleanup; 79988af8bbeSSebastian Andrzej Siewior } 80088af8bbeSSebastian Andrzej Siewior 80188af8bbeSSebastian Andrzej Siewior 80288af8bbeSSebastian Andrzej Siewior list_for_each_entry(c, &gi->cdev.configs, list) { 80388af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg; 80488af8bbeSSebastian Andrzej Siewior 80588af8bbeSSebastian Andrzej Siewior cfg = container_of(c, struct config_usb_cfg, c); 80688af8bbeSSebastian Andrzej Siewior if (list_empty(&cfg->func_list)) { 80788af8bbeSSebastian Andrzej Siewior pr_err("Config %s/%d of %s needs atleast one function.\n", 80888af8bbeSSebastian Andrzej Siewior c->label, c->bConfigurationValue, 80988af8bbeSSebastian Andrzej Siewior gi->composite.name); 81088af8bbeSSebastian Andrzej Siewior goto err_comp_cleanup; 81188af8bbeSSebastian Andrzej Siewior } 81288af8bbeSSebastian Andrzej Siewior } 81388af8bbeSSebastian Andrzej Siewior 81488af8bbeSSebastian Andrzej Siewior /* init all strings */ 81588af8bbeSSebastian Andrzej Siewior if (!list_empty(&gi->string_list)) { 81688af8bbeSSebastian Andrzej Siewior struct gadget_strings *gs; 81788af8bbeSSebastian Andrzej Siewior 81888af8bbeSSebastian Andrzej Siewior i = 0; 81988af8bbeSSebastian Andrzej Siewior list_for_each_entry(gs, &gi->string_list, list) { 82088af8bbeSSebastian Andrzej Siewior 82188af8bbeSSebastian Andrzej Siewior gi->gstrings[i] = &gs->stringtab_dev; 82288af8bbeSSebastian Andrzej Siewior gs->stringtab_dev.strings = gs->strings; 82388af8bbeSSebastian Andrzej Siewior gs->strings[USB_GADGET_MANUFACTURER_IDX].s = 82488af8bbeSSebastian Andrzej Siewior gs->manufacturer; 82588af8bbeSSebastian Andrzej Siewior gs->strings[USB_GADGET_PRODUCT_IDX].s = gs->product; 82688af8bbeSSebastian Andrzej Siewior gs->strings[USB_GADGET_SERIAL_IDX].s = gs->serialnumber; 82788af8bbeSSebastian Andrzej Siewior i++; 82888af8bbeSSebastian Andrzej Siewior } 82988af8bbeSSebastian Andrzej Siewior gi->gstrings[i] = NULL; 83088af8bbeSSebastian Andrzej Siewior s = usb_gstrings_attach(&gi->cdev, gi->gstrings, 83188af8bbeSSebastian Andrzej Siewior USB_GADGET_FIRST_AVAIL_IDX); 832fea77077SWei Yongjun if (IS_ERR(s)) { 833fea77077SWei Yongjun ret = PTR_ERR(s); 83488af8bbeSSebastian Andrzej Siewior goto err_comp_cleanup; 835fea77077SWei Yongjun } 83688af8bbeSSebastian Andrzej Siewior 83788af8bbeSSebastian Andrzej Siewior gi->cdev.desc.iManufacturer = s[USB_GADGET_MANUFACTURER_IDX].id; 83888af8bbeSSebastian Andrzej Siewior gi->cdev.desc.iProduct = s[USB_GADGET_PRODUCT_IDX].id; 83988af8bbeSSebastian Andrzej Siewior gi->cdev.desc.iSerialNumber = s[USB_GADGET_SERIAL_IDX].id; 84088af8bbeSSebastian Andrzej Siewior } 84188af8bbeSSebastian Andrzej Siewior 84288af8bbeSSebastian Andrzej Siewior /* Go through all configs, attach all functions */ 84388af8bbeSSebastian Andrzej Siewior list_for_each_entry(c, &gi->cdev.configs, list) { 84488af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg; 84588af8bbeSSebastian Andrzej Siewior struct usb_function *f; 84688af8bbeSSebastian Andrzej Siewior struct usb_function *tmp; 84788af8bbeSSebastian Andrzej Siewior struct gadget_config_name *cn; 84888af8bbeSSebastian Andrzej Siewior 84988af8bbeSSebastian Andrzej Siewior cfg = container_of(c, struct config_usb_cfg, c); 85088af8bbeSSebastian Andrzej Siewior if (!list_empty(&cfg->string_list)) { 85188af8bbeSSebastian Andrzej Siewior i = 0; 85288af8bbeSSebastian Andrzej Siewior list_for_each_entry(cn, &cfg->string_list, list) { 85388af8bbeSSebastian Andrzej Siewior cfg->gstrings[i] = &cn->stringtab_dev; 85488af8bbeSSebastian Andrzej Siewior cn->stringtab_dev.strings = &cn->strings; 85588af8bbeSSebastian Andrzej Siewior cn->strings.s = cn->configuration; 85688af8bbeSSebastian Andrzej Siewior i++; 85788af8bbeSSebastian Andrzej Siewior } 85888af8bbeSSebastian Andrzej Siewior cfg->gstrings[i] = NULL; 85988af8bbeSSebastian Andrzej Siewior s = usb_gstrings_attach(&gi->cdev, cfg->gstrings, 1); 860fea77077SWei Yongjun if (IS_ERR(s)) { 861fea77077SWei Yongjun ret = PTR_ERR(s); 86288af8bbeSSebastian Andrzej Siewior goto err_comp_cleanup; 863fea77077SWei Yongjun } 86488af8bbeSSebastian Andrzej Siewior c->iConfiguration = s[0].id; 86588af8bbeSSebastian Andrzej Siewior } 86688af8bbeSSebastian Andrzej Siewior 86788af8bbeSSebastian Andrzej Siewior list_for_each_entry_safe(f, tmp, &cfg->func_list, list) { 86888af8bbeSSebastian Andrzej Siewior list_del(&f->list); 86988af8bbeSSebastian Andrzej Siewior ret = usb_add_function(c, f); 8705a68e9b5SAndrzej Pietrasiewicz if (ret) { 8715a68e9b5SAndrzej Pietrasiewicz list_add(&f->list, &cfg->func_list); 87288af8bbeSSebastian Andrzej Siewior goto err_purge_funcs; 87388af8bbeSSebastian Andrzej Siewior } 8745a68e9b5SAndrzej Pietrasiewicz } 87588af8bbeSSebastian Andrzej Siewior usb_ep_autoconfig_reset(cdev->gadget); 87688af8bbeSSebastian Andrzej Siewior } 87788af8bbeSSebastian Andrzej Siewior usb_ep_autoconfig_reset(cdev->gadget); 87888af8bbeSSebastian Andrzej Siewior return 0; 87988af8bbeSSebastian Andrzej Siewior 88088af8bbeSSebastian Andrzej Siewior err_purge_funcs: 88188af8bbeSSebastian Andrzej Siewior purge_configs_funcs(gi); 88288af8bbeSSebastian Andrzej Siewior err_comp_cleanup: 88388af8bbeSSebastian Andrzej Siewior composite_dev_cleanup(cdev); 88488af8bbeSSebastian Andrzej Siewior return ret; 88588af8bbeSSebastian Andrzej Siewior } 88688af8bbeSSebastian Andrzej Siewior 88788af8bbeSSebastian Andrzej Siewior static void configfs_composite_unbind(struct usb_gadget *gadget) 88888af8bbeSSebastian Andrzej Siewior { 88988af8bbeSSebastian Andrzej Siewior struct usb_composite_dev *cdev; 89088af8bbeSSebastian Andrzej Siewior struct gadget_info *gi; 89188af8bbeSSebastian Andrzej Siewior 89288af8bbeSSebastian Andrzej Siewior /* the gi->lock is hold by the caller */ 89388af8bbeSSebastian Andrzej Siewior 89488af8bbeSSebastian Andrzej Siewior cdev = get_gadget_data(gadget); 89588af8bbeSSebastian Andrzej Siewior gi = container_of(cdev, struct gadget_info, cdev); 89688af8bbeSSebastian Andrzej Siewior 89788af8bbeSSebastian Andrzej Siewior purge_configs_funcs(gi); 89888af8bbeSSebastian Andrzej Siewior composite_dev_cleanup(cdev); 89988af8bbeSSebastian Andrzej Siewior usb_ep_autoconfig_reset(cdev->gadget); 90088af8bbeSSebastian Andrzej Siewior cdev->gadget = NULL; 90188af8bbeSSebastian Andrzej Siewior set_gadget_data(gadget, NULL); 90288af8bbeSSebastian Andrzej Siewior } 90388af8bbeSSebastian Andrzej Siewior 90488af8bbeSSebastian Andrzej Siewior static const struct usb_gadget_driver configfs_driver_template = { 90588af8bbeSSebastian Andrzej Siewior .bind = configfs_composite_bind, 90688af8bbeSSebastian Andrzej Siewior .unbind = configfs_composite_unbind, 90788af8bbeSSebastian Andrzej Siewior 90888af8bbeSSebastian Andrzej Siewior .setup = composite_setup, 90988af8bbeSSebastian Andrzej Siewior .disconnect = composite_disconnect, 91088af8bbeSSebastian Andrzej Siewior 91188af8bbeSSebastian Andrzej Siewior .max_speed = USB_SPEED_SUPER, 91288af8bbeSSebastian Andrzej Siewior .driver = { 91388af8bbeSSebastian Andrzej Siewior .owner = THIS_MODULE, 91488af8bbeSSebastian Andrzej Siewior .name = "configfs-gadget", 91588af8bbeSSebastian Andrzej Siewior }, 91688af8bbeSSebastian Andrzej Siewior }; 91788af8bbeSSebastian Andrzej Siewior 91888af8bbeSSebastian Andrzej Siewior static struct config_group *gadgets_make( 91988af8bbeSSebastian Andrzej Siewior struct config_group *group, 92088af8bbeSSebastian Andrzej Siewior const char *name) 92188af8bbeSSebastian Andrzej Siewior { 92288af8bbeSSebastian Andrzej Siewior struct gadget_info *gi; 92388af8bbeSSebastian Andrzej Siewior 92488af8bbeSSebastian Andrzej Siewior gi = kzalloc(sizeof(*gi), GFP_KERNEL); 92588af8bbeSSebastian Andrzej Siewior if (!gi) 92688af8bbeSSebastian Andrzej Siewior return ERR_PTR(-ENOMEM); 92788af8bbeSSebastian Andrzej Siewior 92888af8bbeSSebastian Andrzej Siewior gi->group.default_groups = gi->default_groups; 92988af8bbeSSebastian Andrzej Siewior gi->group.default_groups[0] = &gi->functions_group; 93088af8bbeSSebastian Andrzej Siewior gi->group.default_groups[1] = &gi->configs_group; 93188af8bbeSSebastian Andrzej Siewior gi->group.default_groups[2] = &gi->strings_group; 93288af8bbeSSebastian Andrzej Siewior 93388af8bbeSSebastian Andrzej Siewior config_group_init_type_name(&gi->functions_group, "functions", 93488af8bbeSSebastian Andrzej Siewior &functions_type); 93588af8bbeSSebastian Andrzej Siewior config_group_init_type_name(&gi->configs_group, "configs", 93688af8bbeSSebastian Andrzej Siewior &config_desc_type); 93788af8bbeSSebastian Andrzej Siewior config_group_init_type_name(&gi->strings_group, "strings", 93888af8bbeSSebastian Andrzej Siewior &gadget_strings_strings_type); 93988af8bbeSSebastian Andrzej Siewior 94088af8bbeSSebastian Andrzej Siewior gi->composite.bind = configfs_do_nothing; 94188af8bbeSSebastian Andrzej Siewior gi->composite.unbind = configfs_do_nothing; 94288af8bbeSSebastian Andrzej Siewior gi->composite.suspend = NULL; 94388af8bbeSSebastian Andrzej Siewior gi->composite.resume = NULL; 94488af8bbeSSebastian Andrzej Siewior gi->composite.max_speed = USB_SPEED_SUPER; 94588af8bbeSSebastian Andrzej Siewior 94688af8bbeSSebastian Andrzej Siewior mutex_init(&gi->lock); 94788af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&gi->string_list); 94888af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&gi->available_func); 94988af8bbeSSebastian Andrzej Siewior 95088af8bbeSSebastian Andrzej Siewior composite_init_dev(&gi->cdev); 95188af8bbeSSebastian Andrzej Siewior gi->cdev.desc.bLength = USB_DT_DEVICE_SIZE; 95288af8bbeSSebastian Andrzej Siewior gi->cdev.desc.bDescriptorType = USB_DT_DEVICE; 95388af8bbeSSebastian Andrzej Siewior gi->cdev.desc.bcdDevice = cpu_to_le16(get_default_bcdDevice()); 95488af8bbeSSebastian Andrzej Siewior 95588af8bbeSSebastian Andrzej Siewior gi->composite.gadget_driver = configfs_driver_template; 95688af8bbeSSebastian Andrzej Siewior 95788af8bbeSSebastian Andrzej Siewior gi->composite.gadget_driver.function = kstrdup(name, GFP_KERNEL); 95888af8bbeSSebastian Andrzej Siewior gi->composite.name = gi->composite.gadget_driver.function; 95988af8bbeSSebastian Andrzej Siewior 96088af8bbeSSebastian Andrzej Siewior if (!gi->composite.gadget_driver.function) 96188af8bbeSSebastian Andrzej Siewior goto err; 96288af8bbeSSebastian Andrzej Siewior 96388af8bbeSSebastian Andrzej Siewior #ifdef CONFIG_USB_OTG 96488af8bbeSSebastian Andrzej Siewior gi->otg.bLength = sizeof(struct usb_otg_descriptor); 96588af8bbeSSebastian Andrzej Siewior gi->otg.bDescriptorType = USB_DT_OTG; 96688af8bbeSSebastian Andrzej Siewior gi->otg.bmAttributes = USB_OTG_SRP | USB_OTG_HNP; 96788af8bbeSSebastian Andrzej Siewior #endif 96888af8bbeSSebastian Andrzej Siewior 96988af8bbeSSebastian Andrzej Siewior config_group_init_type_name(&gi->group, name, 97088af8bbeSSebastian Andrzej Siewior &gadget_root_type); 97188af8bbeSSebastian Andrzej Siewior return &gi->group; 97288af8bbeSSebastian Andrzej Siewior err: 97388af8bbeSSebastian Andrzej Siewior kfree(gi); 97488af8bbeSSebastian Andrzej Siewior return ERR_PTR(-ENOMEM); 97588af8bbeSSebastian Andrzej Siewior } 97688af8bbeSSebastian Andrzej Siewior 97788af8bbeSSebastian Andrzej Siewior static void gadgets_drop(struct config_group *group, struct config_item *item) 97888af8bbeSSebastian Andrzej Siewior { 97988af8bbeSSebastian Andrzej Siewior config_item_put(item); 98088af8bbeSSebastian Andrzej Siewior } 98188af8bbeSSebastian Andrzej Siewior 98288af8bbeSSebastian Andrzej Siewior static struct configfs_group_operations gadgets_ops = { 98388af8bbeSSebastian Andrzej Siewior .make_group = &gadgets_make, 98488af8bbeSSebastian Andrzej Siewior .drop_item = &gadgets_drop, 98588af8bbeSSebastian Andrzej Siewior }; 98688af8bbeSSebastian Andrzej Siewior 98788af8bbeSSebastian Andrzej Siewior static struct config_item_type gadgets_type = { 98888af8bbeSSebastian Andrzej Siewior .ct_group_ops = &gadgets_ops, 98988af8bbeSSebastian Andrzej Siewior .ct_owner = THIS_MODULE, 99088af8bbeSSebastian Andrzej Siewior }; 99188af8bbeSSebastian Andrzej Siewior 99288af8bbeSSebastian Andrzej Siewior static struct configfs_subsystem gadget_subsys = { 99388af8bbeSSebastian Andrzej Siewior .su_group = { 99488af8bbeSSebastian Andrzej Siewior .cg_item = { 99588af8bbeSSebastian Andrzej Siewior .ci_namebuf = "usb_gadget", 99688af8bbeSSebastian Andrzej Siewior .ci_type = &gadgets_type, 99788af8bbeSSebastian Andrzej Siewior }, 99888af8bbeSSebastian Andrzej Siewior }, 99988af8bbeSSebastian Andrzej Siewior .su_mutex = __MUTEX_INITIALIZER(gadget_subsys.su_mutex), 100088af8bbeSSebastian Andrzej Siewior }; 100188af8bbeSSebastian Andrzej Siewior 1002092a4bd0SAndrzej Pietrasiewicz void unregister_gadget_item(struct config_item *item) 1003092a4bd0SAndrzej Pietrasiewicz { 1004092a4bd0SAndrzej Pietrasiewicz struct gadget_info *gi = to_gadget_info(item); 1005092a4bd0SAndrzej Pietrasiewicz 1006092a4bd0SAndrzej Pietrasiewicz unregister_gadget(gi); 1007092a4bd0SAndrzej Pietrasiewicz } 1008*0700faafSFelipe Balbi EXPORT_SYMBOL_GPL(unregister_gadget_item); 1009092a4bd0SAndrzej Pietrasiewicz 101088af8bbeSSebastian Andrzej Siewior static int __init gadget_cfs_init(void) 101188af8bbeSSebastian Andrzej Siewior { 101288af8bbeSSebastian Andrzej Siewior int ret; 101388af8bbeSSebastian Andrzej Siewior 101488af8bbeSSebastian Andrzej Siewior config_group_init(&gadget_subsys.su_group); 101588af8bbeSSebastian Andrzej Siewior 101688af8bbeSSebastian Andrzej Siewior ret = configfs_register_subsystem(&gadget_subsys); 101788af8bbeSSebastian Andrzej Siewior return ret; 101888af8bbeSSebastian Andrzej Siewior } 101988af8bbeSSebastian Andrzej Siewior module_init(gadget_cfs_init); 102088af8bbeSSebastian Andrzej Siewior 102188af8bbeSSebastian Andrzej Siewior static void __exit gadget_cfs_exit(void) 102288af8bbeSSebastian Andrzej Siewior { 102388af8bbeSSebastian Andrzej Siewior configfs_unregister_subsystem(&gadget_subsys); 102488af8bbeSSebastian Andrzej Siewior } 102588af8bbeSSebastian Andrzej Siewior module_exit(gadget_cfs_exit); 1026