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> 587213d38SAndrzej Pietrasiewicz #include <linux/nls.h> 688af8bbeSSebastian Andrzej Siewior #include <linux/usb/composite.h> 788af8bbeSSebastian Andrzej Siewior #include <linux/usb/gadget_configfs.h> 80009e99aSRashika Kheria #include "configfs.h" 9da424314SAndrzej Pietrasiewicz #include "u_f.h" 107419485fSAndrzej Pietrasiewicz #include "u_os_desc.h" 1188af8bbeSSebastian Andrzej Siewior 1288af8bbeSSebastian Andrzej Siewior int check_user_usb_string(const char *name, 1388af8bbeSSebastian Andrzej Siewior struct usb_gadget_strings *stringtab_dev) 1488af8bbeSSebastian Andrzej Siewior { 1588af8bbeSSebastian Andrzej Siewior unsigned primary_lang; 1688af8bbeSSebastian Andrzej Siewior unsigned sub_lang; 1788af8bbeSSebastian Andrzej Siewior u16 num; 1888af8bbeSSebastian Andrzej Siewior int ret; 1988af8bbeSSebastian Andrzej Siewior 2088af8bbeSSebastian Andrzej Siewior ret = kstrtou16(name, 0, &num); 2188af8bbeSSebastian Andrzej Siewior if (ret) 2288af8bbeSSebastian Andrzej Siewior return ret; 2388af8bbeSSebastian Andrzej Siewior 2488af8bbeSSebastian Andrzej Siewior primary_lang = num & 0x3ff; 2588af8bbeSSebastian Andrzej Siewior sub_lang = num >> 10; 2688af8bbeSSebastian Andrzej Siewior 2788af8bbeSSebastian Andrzej Siewior /* simple sanity check for valid langid */ 2888af8bbeSSebastian Andrzej Siewior switch (primary_lang) { 2988af8bbeSSebastian Andrzej Siewior case 0: 3088af8bbeSSebastian Andrzej Siewior case 0x62 ... 0xfe: 3188af8bbeSSebastian Andrzej Siewior case 0x100 ... 0x3ff: 3288af8bbeSSebastian Andrzej Siewior return -EINVAL; 3388af8bbeSSebastian Andrzej Siewior } 3488af8bbeSSebastian Andrzej Siewior if (!sub_lang) 3588af8bbeSSebastian Andrzej Siewior return -EINVAL; 3688af8bbeSSebastian Andrzej Siewior 3788af8bbeSSebastian Andrzej Siewior stringtab_dev->language = num; 3888af8bbeSSebastian Andrzej Siewior return 0; 3988af8bbeSSebastian Andrzej Siewior } 4088af8bbeSSebastian Andrzej Siewior 4188af8bbeSSebastian Andrzej Siewior #define MAX_NAME_LEN 40 4288af8bbeSSebastian Andrzej Siewior #define MAX_USB_STRING_LANGS 2 4388af8bbeSSebastian Andrzej Siewior 4441ce84c8SLi Jun static const struct usb_descriptor_header *otg_desc[2]; 4541ce84c8SLi Jun 4688af8bbeSSebastian Andrzej Siewior struct gadget_info { 4788af8bbeSSebastian Andrzej Siewior struct config_group group; 4888af8bbeSSebastian Andrzej Siewior struct config_group functions_group; 4988af8bbeSSebastian Andrzej Siewior struct config_group configs_group; 5088af8bbeSSebastian Andrzej Siewior struct config_group strings_group; 5187213d38SAndrzej Pietrasiewicz struct config_group os_desc_group; 5288af8bbeSSebastian Andrzej Siewior 5388af8bbeSSebastian Andrzej Siewior struct mutex lock; 5488af8bbeSSebastian Andrzej Siewior struct usb_gadget_strings *gstrings[MAX_USB_STRING_LANGS + 1]; 5588af8bbeSSebastian Andrzej Siewior struct list_head string_list; 5688af8bbeSSebastian Andrzej Siewior struct list_head available_func; 5788af8bbeSSebastian Andrzej Siewior 5888af8bbeSSebastian Andrzej Siewior struct usb_composite_driver composite; 5988af8bbeSSebastian Andrzej Siewior struct usb_composite_dev cdev; 6087213d38SAndrzej Pietrasiewicz bool use_os_desc; 6187213d38SAndrzej Pietrasiewicz char b_vendor_code; 6287213d38SAndrzej Pietrasiewicz char qw_sign[OS_STRING_QW_SIGN_LEN]; 6388af8bbeSSebastian Andrzej Siewior }; 6488af8bbeSSebastian Andrzej Siewior 6545b6a73fSChristoph Hellwig static inline struct gadget_info *to_gadget_info(struct config_item *item) 6645b6a73fSChristoph Hellwig { 6745b6a73fSChristoph Hellwig return container_of(to_config_group(item), struct gadget_info, group); 6845b6a73fSChristoph Hellwig } 6945b6a73fSChristoph Hellwig 7088af8bbeSSebastian Andrzej Siewior struct config_usb_cfg { 7188af8bbeSSebastian Andrzej Siewior struct config_group group; 7288af8bbeSSebastian Andrzej Siewior struct config_group strings_group; 7388af8bbeSSebastian Andrzej Siewior struct list_head string_list; 7488af8bbeSSebastian Andrzej Siewior struct usb_configuration c; 7588af8bbeSSebastian Andrzej Siewior struct list_head func_list; 7688af8bbeSSebastian Andrzej Siewior struct usb_gadget_strings *gstrings[MAX_USB_STRING_LANGS + 1]; 7788af8bbeSSebastian Andrzej Siewior }; 7888af8bbeSSebastian Andrzej Siewior 7945b6a73fSChristoph Hellwig static inline struct config_usb_cfg *to_config_usb_cfg(struct config_item *item) 8045b6a73fSChristoph Hellwig { 8145b6a73fSChristoph Hellwig return container_of(to_config_group(item), struct config_usb_cfg, 8245b6a73fSChristoph Hellwig group); 8345b6a73fSChristoph Hellwig } 8445b6a73fSChristoph Hellwig 8588af8bbeSSebastian Andrzej Siewior struct gadget_strings { 8688af8bbeSSebastian Andrzej Siewior struct usb_gadget_strings stringtab_dev; 8788af8bbeSSebastian Andrzej Siewior struct usb_string strings[USB_GADGET_FIRST_AVAIL_IDX]; 8888af8bbeSSebastian Andrzej Siewior char *manufacturer; 8988af8bbeSSebastian Andrzej Siewior char *product; 9088af8bbeSSebastian Andrzej Siewior char *serialnumber; 9188af8bbeSSebastian Andrzej Siewior 9288af8bbeSSebastian Andrzej Siewior struct config_group group; 9388af8bbeSSebastian Andrzej Siewior struct list_head list; 9488af8bbeSSebastian Andrzej Siewior }; 9588af8bbeSSebastian Andrzej Siewior 9687213d38SAndrzej Pietrasiewicz struct os_desc { 9787213d38SAndrzej Pietrasiewicz struct config_group group; 9887213d38SAndrzej Pietrasiewicz }; 9987213d38SAndrzej Pietrasiewicz 10088af8bbeSSebastian Andrzej Siewior struct gadget_config_name { 10188af8bbeSSebastian Andrzej Siewior struct usb_gadget_strings stringtab_dev; 10288af8bbeSSebastian Andrzej Siewior struct usb_string strings; 10388af8bbeSSebastian Andrzej Siewior char *configuration; 10488af8bbeSSebastian Andrzej Siewior 10588af8bbeSSebastian Andrzej Siewior struct config_group group; 10688af8bbeSSebastian Andrzej Siewior struct list_head list; 10788af8bbeSSebastian Andrzej Siewior }; 10888af8bbeSSebastian Andrzej Siewior 10988af8bbeSSebastian Andrzej Siewior static int usb_string_copy(const char *s, char **s_copy) 11088af8bbeSSebastian Andrzej Siewior { 11188af8bbeSSebastian Andrzej Siewior int ret; 11288af8bbeSSebastian Andrzej Siewior char *str; 11388af8bbeSSebastian Andrzej Siewior char *copy = *s_copy; 11488af8bbeSSebastian Andrzej Siewior ret = strlen(s); 11588af8bbeSSebastian Andrzej Siewior if (ret > 126) 11688af8bbeSSebastian Andrzej Siewior return -EOVERFLOW; 11788af8bbeSSebastian Andrzej Siewior 11888af8bbeSSebastian Andrzej Siewior str = kstrdup(s, GFP_KERNEL); 11988af8bbeSSebastian Andrzej Siewior if (!str) 12088af8bbeSSebastian Andrzej Siewior return -ENOMEM; 12188af8bbeSSebastian Andrzej Siewior if (str[ret - 1] == '\n') 12288af8bbeSSebastian Andrzej Siewior str[ret - 1] = '\0'; 12388af8bbeSSebastian Andrzej Siewior kfree(copy); 12488af8bbeSSebastian Andrzej Siewior *s_copy = str; 12588af8bbeSSebastian Andrzej Siewior return 0; 12688af8bbeSSebastian Andrzej Siewior } 12788af8bbeSSebastian Andrzej Siewior 12888af8bbeSSebastian Andrzej Siewior #define GI_DEVICE_DESC_SIMPLE_R_u8(__name) \ 12945b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_##__name##_show(struct config_item *item, \ 13088af8bbeSSebastian Andrzej Siewior char *page) \ 13188af8bbeSSebastian Andrzej Siewior { \ 13245b6a73fSChristoph Hellwig return sprintf(page, "0x%02x\n", \ 13345b6a73fSChristoph Hellwig to_gadget_info(item)->cdev.desc.__name); \ 13488af8bbeSSebastian Andrzej Siewior } 13588af8bbeSSebastian Andrzej Siewior 13688af8bbeSSebastian Andrzej Siewior #define GI_DEVICE_DESC_SIMPLE_R_u16(__name) \ 13745b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_##__name##_show(struct config_item *item, \ 13888af8bbeSSebastian Andrzej Siewior char *page) \ 13988af8bbeSSebastian Andrzej Siewior { \ 14045b6a73fSChristoph Hellwig return sprintf(page, "0x%04x\n", \ 14145b6a73fSChristoph Hellwig le16_to_cpup(&to_gadget_info(item)->cdev.desc.__name)); \ 14288af8bbeSSebastian Andrzej Siewior } 14388af8bbeSSebastian Andrzej Siewior 14488af8bbeSSebastian Andrzej Siewior 14588af8bbeSSebastian Andrzej Siewior #define GI_DEVICE_DESC_SIMPLE_W_u8(_name) \ 14645b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_##_name##_store(struct config_item *item, \ 14788af8bbeSSebastian Andrzej Siewior const char *page, size_t len) \ 14888af8bbeSSebastian Andrzej Siewior { \ 14988af8bbeSSebastian Andrzej Siewior u8 val; \ 15088af8bbeSSebastian Andrzej Siewior int ret; \ 15188af8bbeSSebastian Andrzej Siewior ret = kstrtou8(page, 0, &val); \ 15288af8bbeSSebastian Andrzej Siewior if (ret) \ 15388af8bbeSSebastian Andrzej Siewior return ret; \ 15445b6a73fSChristoph Hellwig to_gadget_info(item)->cdev.desc._name = val; \ 15588af8bbeSSebastian Andrzej Siewior return len; \ 15688af8bbeSSebastian Andrzej Siewior } 15788af8bbeSSebastian Andrzej Siewior 15888af8bbeSSebastian Andrzej Siewior #define GI_DEVICE_DESC_SIMPLE_W_u16(_name) \ 15945b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_##_name##_store(struct config_item *item, \ 16088af8bbeSSebastian Andrzej Siewior const char *page, size_t len) \ 16188af8bbeSSebastian Andrzej Siewior { \ 16288af8bbeSSebastian Andrzej Siewior u16 val; \ 16388af8bbeSSebastian Andrzej Siewior int ret; \ 16488af8bbeSSebastian Andrzej Siewior ret = kstrtou16(page, 0, &val); \ 16588af8bbeSSebastian Andrzej Siewior if (ret) \ 16688af8bbeSSebastian Andrzej Siewior return ret; \ 16745b6a73fSChristoph Hellwig to_gadget_info(item)->cdev.desc._name = cpu_to_le16p(&val); \ 16888af8bbeSSebastian Andrzej Siewior return len; \ 16988af8bbeSSebastian Andrzej Siewior } 17088af8bbeSSebastian Andrzej Siewior 17188af8bbeSSebastian Andrzej Siewior #define GI_DEVICE_DESC_SIMPLE_RW(_name, _type) \ 17288af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_R_##_type(_name) \ 17388af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_W_##_type(_name) 17488af8bbeSSebastian Andrzej Siewior 17588af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_R_u16(bcdUSB); 17688af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(bDeviceClass, u8); 17788af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(bDeviceSubClass, u8); 17888af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(bDeviceProtocol, u8); 17988af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(bMaxPacketSize0, u8); 18088af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(idVendor, u16); 18188af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(idProduct, u16); 18288af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_R_u16(bcdDevice); 18388af8bbeSSebastian Andrzej Siewior 18488af8bbeSSebastian Andrzej Siewior static ssize_t is_valid_bcd(u16 bcd_val) 18588af8bbeSSebastian Andrzej Siewior { 18688af8bbeSSebastian Andrzej Siewior if ((bcd_val & 0xf) > 9) 18788af8bbeSSebastian Andrzej Siewior return -EINVAL; 18888af8bbeSSebastian Andrzej Siewior if (((bcd_val >> 4) & 0xf) > 9) 18988af8bbeSSebastian Andrzej Siewior return -EINVAL; 19088af8bbeSSebastian Andrzej Siewior if (((bcd_val >> 8) & 0xf) > 9) 19188af8bbeSSebastian Andrzej Siewior return -EINVAL; 19288af8bbeSSebastian Andrzej Siewior if (((bcd_val >> 12) & 0xf) > 9) 19388af8bbeSSebastian Andrzej Siewior return -EINVAL; 19488af8bbeSSebastian Andrzej Siewior return 0; 19588af8bbeSSebastian Andrzej Siewior } 19688af8bbeSSebastian Andrzej Siewior 19745b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_bcdDevice_store(struct config_item *item, 19888af8bbeSSebastian Andrzej Siewior const char *page, size_t len) 19988af8bbeSSebastian Andrzej Siewior { 20088af8bbeSSebastian Andrzej Siewior u16 bcdDevice; 20188af8bbeSSebastian Andrzej Siewior int ret; 20288af8bbeSSebastian Andrzej Siewior 20388af8bbeSSebastian Andrzej Siewior ret = kstrtou16(page, 0, &bcdDevice); 20488af8bbeSSebastian Andrzej Siewior if (ret) 20588af8bbeSSebastian Andrzej Siewior return ret; 20688af8bbeSSebastian Andrzej Siewior ret = is_valid_bcd(bcdDevice); 20788af8bbeSSebastian Andrzej Siewior if (ret) 20888af8bbeSSebastian Andrzej Siewior return ret; 20988af8bbeSSebastian Andrzej Siewior 21045b6a73fSChristoph Hellwig to_gadget_info(item)->cdev.desc.bcdDevice = cpu_to_le16(bcdDevice); 21188af8bbeSSebastian Andrzej Siewior return len; 21288af8bbeSSebastian Andrzej Siewior } 21388af8bbeSSebastian Andrzej Siewior 21445b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_bcdUSB_store(struct config_item *item, 21588af8bbeSSebastian Andrzej Siewior const char *page, size_t len) 21688af8bbeSSebastian Andrzej Siewior { 21788af8bbeSSebastian Andrzej Siewior u16 bcdUSB; 21888af8bbeSSebastian Andrzej Siewior int ret; 21988af8bbeSSebastian Andrzej Siewior 22088af8bbeSSebastian Andrzej Siewior ret = kstrtou16(page, 0, &bcdUSB); 22188af8bbeSSebastian Andrzej Siewior if (ret) 22288af8bbeSSebastian Andrzej Siewior return ret; 22388af8bbeSSebastian Andrzej Siewior ret = is_valid_bcd(bcdUSB); 22488af8bbeSSebastian Andrzej Siewior if (ret) 22588af8bbeSSebastian Andrzej Siewior return ret; 22688af8bbeSSebastian Andrzej Siewior 22745b6a73fSChristoph Hellwig to_gadget_info(item)->cdev.desc.bcdUSB = cpu_to_le16(bcdUSB); 22888af8bbeSSebastian Andrzej Siewior return len; 22988af8bbeSSebastian Andrzej Siewior } 23088af8bbeSSebastian Andrzej Siewior 23145b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_UDC_show(struct config_item *item, char *page) 23288af8bbeSSebastian Andrzej Siewior { 233afdaadc3SRuslan Bilovol char *udc_name = to_gadget_info(item)->composite.gadget_driver.udc_name; 234afdaadc3SRuslan Bilovol 235afdaadc3SRuslan Bilovol return sprintf(page, "%s\n", udc_name ?: ""); 23688af8bbeSSebastian Andrzej Siewior } 23788af8bbeSSebastian Andrzej Siewior 23888af8bbeSSebastian Andrzej Siewior static int unregister_gadget(struct gadget_info *gi) 23988af8bbeSSebastian Andrzej Siewior { 24088af8bbeSSebastian Andrzej Siewior int ret; 24188af8bbeSSebastian Andrzej Siewior 242afdaadc3SRuslan Bilovol if (!gi->composite.gadget_driver.udc_name) 24388af8bbeSSebastian Andrzej Siewior return -ENODEV; 24488af8bbeSSebastian Andrzej Siewior 24588af8bbeSSebastian Andrzej Siewior ret = usb_gadget_unregister_driver(&gi->composite.gadget_driver); 24688af8bbeSSebastian Andrzej Siewior if (ret) 24788af8bbeSSebastian Andrzej Siewior return ret; 248afdaadc3SRuslan Bilovol kfree(gi->composite.gadget_driver.udc_name); 249afdaadc3SRuslan Bilovol gi->composite.gadget_driver.udc_name = NULL; 25088af8bbeSSebastian Andrzej Siewior return 0; 25188af8bbeSSebastian Andrzej Siewior } 25288af8bbeSSebastian Andrzej Siewior 25345b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_UDC_store(struct config_item *item, 25488af8bbeSSebastian Andrzej Siewior const char *page, size_t len) 25588af8bbeSSebastian Andrzej Siewior { 25645b6a73fSChristoph Hellwig struct gadget_info *gi = to_gadget_info(item); 25788af8bbeSSebastian Andrzej Siewior char *name; 25888af8bbeSSebastian Andrzej Siewior int ret; 25988af8bbeSSebastian Andrzej Siewior 26088af8bbeSSebastian Andrzej Siewior name = kstrdup(page, GFP_KERNEL); 26188af8bbeSSebastian Andrzej Siewior if (!name) 26288af8bbeSSebastian Andrzej Siewior return -ENOMEM; 26388af8bbeSSebastian Andrzej Siewior if (name[len - 1] == '\n') 26488af8bbeSSebastian Andrzej Siewior name[len - 1] = '\0'; 26588af8bbeSSebastian Andrzej Siewior 26688af8bbeSSebastian Andrzej Siewior mutex_lock(&gi->lock); 26788af8bbeSSebastian Andrzej Siewior 26888af8bbeSSebastian Andrzej Siewior if (!strlen(name)) { 26988af8bbeSSebastian Andrzej Siewior ret = unregister_gadget(gi); 27088af8bbeSSebastian Andrzej Siewior if (ret) 27188af8bbeSSebastian Andrzej Siewior goto err; 27238355b2aSJohn Keeping kfree(name); 27388af8bbeSSebastian Andrzej Siewior } else { 274afdaadc3SRuslan Bilovol if (gi->composite.gadget_driver.udc_name) { 27588af8bbeSSebastian Andrzej Siewior ret = -EBUSY; 27688af8bbeSSebastian Andrzej Siewior goto err; 27788af8bbeSSebastian Andrzej Siewior } 278afdaadc3SRuslan Bilovol gi->composite.gadget_driver.udc_name = name; 279afdaadc3SRuslan Bilovol ret = usb_gadget_probe_driver(&gi->composite.gadget_driver); 280afdaadc3SRuslan Bilovol if (ret) { 281afdaadc3SRuslan Bilovol gi->composite.gadget_driver.udc_name = NULL; 28288af8bbeSSebastian Andrzej Siewior goto err; 283afdaadc3SRuslan Bilovol } 28488af8bbeSSebastian Andrzej Siewior } 28588af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 28688af8bbeSSebastian Andrzej Siewior return len; 28788af8bbeSSebastian Andrzej Siewior err: 28888af8bbeSSebastian Andrzej Siewior kfree(name); 28988af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 29088af8bbeSSebastian Andrzej Siewior return ret; 29188af8bbeSSebastian Andrzej Siewior } 29288af8bbeSSebastian Andrzej Siewior 29345b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, bDeviceClass); 29445b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, bDeviceSubClass); 29545b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, bDeviceProtocol); 29645b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, bMaxPacketSize0); 29745b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, idVendor); 29845b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, idProduct); 29945b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, bcdDevice); 30045b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, bcdUSB); 30145b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, UDC); 30288af8bbeSSebastian Andrzej Siewior 30388af8bbeSSebastian Andrzej Siewior static struct configfs_attribute *gadget_root_attrs[] = { 30445b6a73fSChristoph Hellwig &gadget_dev_desc_attr_bDeviceClass, 30545b6a73fSChristoph Hellwig &gadget_dev_desc_attr_bDeviceSubClass, 30645b6a73fSChristoph Hellwig &gadget_dev_desc_attr_bDeviceProtocol, 30745b6a73fSChristoph Hellwig &gadget_dev_desc_attr_bMaxPacketSize0, 30845b6a73fSChristoph Hellwig &gadget_dev_desc_attr_idVendor, 30945b6a73fSChristoph Hellwig &gadget_dev_desc_attr_idProduct, 31045b6a73fSChristoph Hellwig &gadget_dev_desc_attr_bcdDevice, 31145b6a73fSChristoph Hellwig &gadget_dev_desc_attr_bcdUSB, 31245b6a73fSChristoph Hellwig &gadget_dev_desc_attr_UDC, 31388af8bbeSSebastian Andrzej Siewior NULL, 31488af8bbeSSebastian Andrzej Siewior }; 31588af8bbeSSebastian Andrzej Siewior 31688af8bbeSSebastian Andrzej Siewior static inline struct gadget_strings *to_gadget_strings(struct config_item *item) 31788af8bbeSSebastian Andrzej Siewior { 31888af8bbeSSebastian Andrzej Siewior return container_of(to_config_group(item), struct gadget_strings, 31988af8bbeSSebastian Andrzej Siewior group); 32088af8bbeSSebastian Andrzej Siewior } 32188af8bbeSSebastian Andrzej Siewior 32288af8bbeSSebastian Andrzej Siewior static inline struct gadget_config_name *to_gadget_config_name( 32388af8bbeSSebastian Andrzej Siewior struct config_item *item) 32488af8bbeSSebastian Andrzej Siewior { 32588af8bbeSSebastian Andrzej Siewior return container_of(to_config_group(item), struct gadget_config_name, 32688af8bbeSSebastian Andrzej Siewior group); 32788af8bbeSSebastian Andrzej Siewior } 32888af8bbeSSebastian Andrzej Siewior 32988af8bbeSSebastian Andrzej Siewior static inline struct usb_function_instance *to_usb_function_instance( 33088af8bbeSSebastian Andrzej Siewior struct config_item *item) 33188af8bbeSSebastian Andrzej Siewior { 33288af8bbeSSebastian Andrzej Siewior return container_of(to_config_group(item), 33388af8bbeSSebastian Andrzej Siewior struct usb_function_instance, group); 33488af8bbeSSebastian Andrzej Siewior } 33588af8bbeSSebastian Andrzej Siewior 33688af8bbeSSebastian Andrzej Siewior static void gadget_info_attr_release(struct config_item *item) 33788af8bbeSSebastian Andrzej Siewior { 33888af8bbeSSebastian Andrzej Siewior struct gadget_info *gi = to_gadget_info(item); 33988af8bbeSSebastian Andrzej Siewior 34088af8bbeSSebastian Andrzej Siewior WARN_ON(!list_empty(&gi->cdev.configs)); 34188af8bbeSSebastian Andrzej Siewior WARN_ON(!list_empty(&gi->string_list)); 34288af8bbeSSebastian Andrzej Siewior WARN_ON(!list_empty(&gi->available_func)); 34388af8bbeSSebastian Andrzej Siewior kfree(gi->composite.gadget_driver.function); 34488af8bbeSSebastian Andrzej Siewior kfree(gi); 34588af8bbeSSebastian Andrzej Siewior } 34688af8bbeSSebastian Andrzej Siewior 34788af8bbeSSebastian Andrzej Siewior static struct configfs_item_operations gadget_root_item_ops = { 34888af8bbeSSebastian Andrzej Siewior .release = gadget_info_attr_release, 34988af8bbeSSebastian Andrzej Siewior }; 35088af8bbeSSebastian Andrzej Siewior 35188af8bbeSSebastian Andrzej Siewior static void gadget_config_attr_release(struct config_item *item) 35288af8bbeSSebastian Andrzej Siewior { 35388af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg = to_config_usb_cfg(item); 35488af8bbeSSebastian Andrzej Siewior 35588af8bbeSSebastian Andrzej Siewior WARN_ON(!list_empty(&cfg->c.functions)); 35688af8bbeSSebastian Andrzej Siewior list_del(&cfg->c.list); 35788af8bbeSSebastian Andrzej Siewior kfree(cfg->c.label); 35888af8bbeSSebastian Andrzej Siewior kfree(cfg); 35988af8bbeSSebastian Andrzej Siewior } 36088af8bbeSSebastian Andrzej Siewior 36188af8bbeSSebastian Andrzej Siewior static int config_usb_cfg_link( 36288af8bbeSSebastian Andrzej Siewior struct config_item *usb_cfg_ci, 36388af8bbeSSebastian Andrzej Siewior struct config_item *usb_func_ci) 36488af8bbeSSebastian Andrzej Siewior { 36588af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg = to_config_usb_cfg(usb_cfg_ci); 36688af8bbeSSebastian Andrzej Siewior struct usb_composite_dev *cdev = cfg->c.cdev; 36788af8bbeSSebastian Andrzej Siewior struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev); 36888af8bbeSSebastian Andrzej Siewior 36988af8bbeSSebastian Andrzej Siewior struct config_group *group = to_config_group(usb_func_ci); 37088af8bbeSSebastian Andrzej Siewior struct usb_function_instance *fi = container_of(group, 37188af8bbeSSebastian Andrzej Siewior struct usb_function_instance, group); 37288af8bbeSSebastian Andrzej Siewior struct usb_function_instance *a_fi; 37388af8bbeSSebastian Andrzej Siewior struct usb_function *f; 37488af8bbeSSebastian Andrzej Siewior int ret; 37588af8bbeSSebastian Andrzej Siewior 37688af8bbeSSebastian Andrzej Siewior mutex_lock(&gi->lock); 37788af8bbeSSebastian Andrzej Siewior /* 37888af8bbeSSebastian Andrzej Siewior * Make sure this function is from within our _this_ gadget and not 37988af8bbeSSebastian Andrzej Siewior * from another gadget or a random directory. 38088af8bbeSSebastian Andrzej Siewior * Also a function instance can only be linked once. 38188af8bbeSSebastian Andrzej Siewior */ 38288af8bbeSSebastian Andrzej Siewior list_for_each_entry(a_fi, &gi->available_func, cfs_list) { 38388af8bbeSSebastian Andrzej Siewior if (a_fi == fi) 38488af8bbeSSebastian Andrzej Siewior break; 38588af8bbeSSebastian Andrzej Siewior } 38688af8bbeSSebastian Andrzej Siewior if (a_fi != fi) { 38788af8bbeSSebastian Andrzej Siewior ret = -EINVAL; 38888af8bbeSSebastian Andrzej Siewior goto out; 38988af8bbeSSebastian Andrzej Siewior } 39088af8bbeSSebastian Andrzej Siewior 39188af8bbeSSebastian Andrzej Siewior list_for_each_entry(f, &cfg->func_list, list) { 39288af8bbeSSebastian Andrzej Siewior if (f->fi == fi) { 39388af8bbeSSebastian Andrzej Siewior ret = -EEXIST; 39488af8bbeSSebastian Andrzej Siewior goto out; 39588af8bbeSSebastian Andrzej Siewior } 39688af8bbeSSebastian Andrzej Siewior } 39788af8bbeSSebastian Andrzej Siewior 39888af8bbeSSebastian Andrzej Siewior f = usb_get_function(fi); 39988af8bbeSSebastian Andrzej Siewior if (IS_ERR(f)) { 40088af8bbeSSebastian Andrzej Siewior ret = PTR_ERR(f); 40188af8bbeSSebastian Andrzej Siewior goto out; 40288af8bbeSSebastian Andrzej Siewior } 40388af8bbeSSebastian Andrzej Siewior 40488af8bbeSSebastian Andrzej Siewior /* stash the function until we bind it to the gadget */ 40588af8bbeSSebastian Andrzej Siewior list_add_tail(&f->list, &cfg->func_list); 40688af8bbeSSebastian Andrzej Siewior ret = 0; 40788af8bbeSSebastian Andrzej Siewior out: 40888af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 40988af8bbeSSebastian Andrzej Siewior return ret; 41088af8bbeSSebastian Andrzej Siewior } 41188af8bbeSSebastian Andrzej Siewior 412e16769d4SAndrzej Pietrasiewicz static void config_usb_cfg_unlink( 41388af8bbeSSebastian Andrzej Siewior struct config_item *usb_cfg_ci, 41488af8bbeSSebastian Andrzej Siewior struct config_item *usb_func_ci) 41588af8bbeSSebastian Andrzej Siewior { 41688af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg = to_config_usb_cfg(usb_cfg_ci); 41788af8bbeSSebastian Andrzej Siewior struct usb_composite_dev *cdev = cfg->c.cdev; 41888af8bbeSSebastian Andrzej Siewior struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev); 41988af8bbeSSebastian Andrzej Siewior 42088af8bbeSSebastian Andrzej Siewior struct config_group *group = to_config_group(usb_func_ci); 42188af8bbeSSebastian Andrzej Siewior struct usb_function_instance *fi = container_of(group, 42288af8bbeSSebastian Andrzej Siewior struct usb_function_instance, group); 42388af8bbeSSebastian Andrzej Siewior struct usb_function *f; 42488af8bbeSSebastian Andrzej Siewior 42588af8bbeSSebastian Andrzej Siewior /* 42688af8bbeSSebastian Andrzej Siewior * ideally I would like to forbid to unlink functions while a gadget is 42788af8bbeSSebastian Andrzej Siewior * bound to an UDC. Since this isn't possible at the moment, we simply 42888af8bbeSSebastian Andrzej Siewior * force an unbind, the function is available here and then we can 42988af8bbeSSebastian Andrzej Siewior * remove the function. 43088af8bbeSSebastian Andrzej Siewior */ 43188af8bbeSSebastian Andrzej Siewior mutex_lock(&gi->lock); 432afdaadc3SRuslan Bilovol if (gi->composite.gadget_driver.udc_name) 43388af8bbeSSebastian Andrzej Siewior unregister_gadget(gi); 434afdaadc3SRuslan Bilovol WARN_ON(gi->composite.gadget_driver.udc_name); 43588af8bbeSSebastian Andrzej Siewior 43688af8bbeSSebastian Andrzej Siewior list_for_each_entry(f, &cfg->func_list, list) { 43788af8bbeSSebastian Andrzej Siewior if (f->fi == fi) { 43888af8bbeSSebastian Andrzej Siewior list_del(&f->list); 43988af8bbeSSebastian Andrzej Siewior usb_put_function(f); 44088af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 441e16769d4SAndrzej Pietrasiewicz return; 44288af8bbeSSebastian Andrzej Siewior } 44388af8bbeSSebastian Andrzej Siewior } 44488af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 44575bfe23aSDavid Rientjes WARN(1, "Unable to locate function to unbind\n"); 44688af8bbeSSebastian Andrzej Siewior } 44788af8bbeSSebastian Andrzej Siewior 44888af8bbeSSebastian Andrzej Siewior static struct configfs_item_operations gadget_config_item_ops = { 44988af8bbeSSebastian Andrzej Siewior .release = gadget_config_attr_release, 45088af8bbeSSebastian Andrzej Siewior .allow_link = config_usb_cfg_link, 45188af8bbeSSebastian Andrzej Siewior .drop_link = config_usb_cfg_unlink, 45288af8bbeSSebastian Andrzej Siewior }; 45388af8bbeSSebastian Andrzej Siewior 45488af8bbeSSebastian Andrzej Siewior 45545b6a73fSChristoph Hellwig static ssize_t gadget_config_desc_MaxPower_show(struct config_item *item, 45688af8bbeSSebastian Andrzej Siewior char *page) 45788af8bbeSSebastian Andrzej Siewior { 45845b6a73fSChristoph Hellwig return sprintf(page, "%u\n", to_config_usb_cfg(item)->c.MaxPower); 45988af8bbeSSebastian Andrzej Siewior } 46088af8bbeSSebastian Andrzej Siewior 46145b6a73fSChristoph Hellwig static ssize_t gadget_config_desc_MaxPower_store(struct config_item *item, 46288af8bbeSSebastian Andrzej Siewior const char *page, size_t len) 46388af8bbeSSebastian Andrzej Siewior { 46488af8bbeSSebastian Andrzej Siewior u16 val; 46588af8bbeSSebastian Andrzej Siewior int ret; 46688af8bbeSSebastian Andrzej Siewior ret = kstrtou16(page, 0, &val); 46788af8bbeSSebastian Andrzej Siewior if (ret) 46888af8bbeSSebastian Andrzej Siewior return ret; 46988af8bbeSSebastian Andrzej Siewior if (DIV_ROUND_UP(val, 8) > 0xff) 47088af8bbeSSebastian Andrzej Siewior return -ERANGE; 47145b6a73fSChristoph Hellwig to_config_usb_cfg(item)->c.MaxPower = val; 47288af8bbeSSebastian Andrzej Siewior return len; 47388af8bbeSSebastian Andrzej Siewior } 47488af8bbeSSebastian Andrzej Siewior 47545b6a73fSChristoph Hellwig static ssize_t gadget_config_desc_bmAttributes_show(struct config_item *item, 47688af8bbeSSebastian Andrzej Siewior char *page) 47788af8bbeSSebastian Andrzej Siewior { 47845b6a73fSChristoph Hellwig return sprintf(page, "0x%02x\n", 47945b6a73fSChristoph Hellwig to_config_usb_cfg(item)->c.bmAttributes); 48088af8bbeSSebastian Andrzej Siewior } 48188af8bbeSSebastian Andrzej Siewior 48245b6a73fSChristoph Hellwig static ssize_t gadget_config_desc_bmAttributes_store(struct config_item *item, 48388af8bbeSSebastian Andrzej Siewior const char *page, size_t len) 48488af8bbeSSebastian Andrzej Siewior { 48588af8bbeSSebastian Andrzej Siewior u8 val; 48688af8bbeSSebastian Andrzej Siewior int ret; 48788af8bbeSSebastian Andrzej Siewior ret = kstrtou8(page, 0, &val); 48888af8bbeSSebastian Andrzej Siewior if (ret) 48988af8bbeSSebastian Andrzej Siewior return ret; 49088af8bbeSSebastian Andrzej Siewior if (!(val & USB_CONFIG_ATT_ONE)) 49188af8bbeSSebastian Andrzej Siewior return -EINVAL; 49288af8bbeSSebastian Andrzej Siewior if (val & ~(USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER | 49388af8bbeSSebastian Andrzej Siewior USB_CONFIG_ATT_WAKEUP)) 49488af8bbeSSebastian Andrzej Siewior return -EINVAL; 49545b6a73fSChristoph Hellwig to_config_usb_cfg(item)->c.bmAttributes = val; 49688af8bbeSSebastian Andrzej Siewior return len; 49788af8bbeSSebastian Andrzej Siewior } 49888af8bbeSSebastian Andrzej Siewior 49945b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_config_desc_, MaxPower); 50045b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_config_desc_, bmAttributes); 50188af8bbeSSebastian Andrzej Siewior 50288af8bbeSSebastian Andrzej Siewior static struct configfs_attribute *gadget_config_attrs[] = { 50345b6a73fSChristoph Hellwig &gadget_config_desc_attr_MaxPower, 50445b6a73fSChristoph Hellwig &gadget_config_desc_attr_bmAttributes, 50588af8bbeSSebastian Andrzej Siewior NULL, 50688af8bbeSSebastian Andrzej Siewior }; 50788af8bbeSSebastian Andrzej Siewior 508*4ad01412SBhumika Goyal static const struct config_item_type gadget_config_type = { 50988af8bbeSSebastian Andrzej Siewior .ct_item_ops = &gadget_config_item_ops, 51088af8bbeSSebastian Andrzej Siewior .ct_attrs = gadget_config_attrs, 51188af8bbeSSebastian Andrzej Siewior .ct_owner = THIS_MODULE, 51288af8bbeSSebastian Andrzej Siewior }; 51388af8bbeSSebastian Andrzej Siewior 514*4ad01412SBhumika Goyal static const struct config_item_type gadget_root_type = { 51588af8bbeSSebastian Andrzej Siewior .ct_item_ops = &gadget_root_item_ops, 51688af8bbeSSebastian Andrzej Siewior .ct_attrs = gadget_root_attrs, 51788af8bbeSSebastian Andrzej Siewior .ct_owner = THIS_MODULE, 51888af8bbeSSebastian Andrzej Siewior }; 51988af8bbeSSebastian Andrzej Siewior 52088af8bbeSSebastian Andrzej Siewior static void composite_init_dev(struct usb_composite_dev *cdev) 52188af8bbeSSebastian Andrzej Siewior { 52288af8bbeSSebastian Andrzej Siewior spin_lock_init(&cdev->lock); 52388af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&cdev->configs); 52488af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&cdev->gstrings); 52588af8bbeSSebastian Andrzej Siewior } 52688af8bbeSSebastian Andrzej Siewior 52788af8bbeSSebastian Andrzej Siewior static struct config_group *function_make( 52888af8bbeSSebastian Andrzej Siewior struct config_group *group, 52988af8bbeSSebastian Andrzej Siewior const char *name) 53088af8bbeSSebastian Andrzej Siewior { 53188af8bbeSSebastian Andrzej Siewior struct gadget_info *gi; 53288af8bbeSSebastian Andrzej Siewior struct usb_function_instance *fi; 53388af8bbeSSebastian Andrzej Siewior char buf[MAX_NAME_LEN]; 53488af8bbeSSebastian Andrzej Siewior char *func_name; 53588af8bbeSSebastian Andrzej Siewior char *instance_name; 53688af8bbeSSebastian Andrzej Siewior int ret; 53788af8bbeSSebastian Andrzej Siewior 53888af8bbeSSebastian Andrzej Siewior ret = snprintf(buf, MAX_NAME_LEN, "%s", name); 53988af8bbeSSebastian Andrzej Siewior if (ret >= MAX_NAME_LEN) 54088af8bbeSSebastian Andrzej Siewior return ERR_PTR(-ENAMETOOLONG); 54188af8bbeSSebastian Andrzej Siewior 54288af8bbeSSebastian Andrzej Siewior func_name = buf; 54388af8bbeSSebastian Andrzej Siewior instance_name = strchr(func_name, '.'); 54488af8bbeSSebastian Andrzej Siewior if (!instance_name) { 54588af8bbeSSebastian Andrzej Siewior pr_err("Unable to locate . in FUNC.INSTANCE\n"); 54688af8bbeSSebastian Andrzej Siewior return ERR_PTR(-EINVAL); 54788af8bbeSSebastian Andrzej Siewior } 54888af8bbeSSebastian Andrzej Siewior *instance_name = '\0'; 54988af8bbeSSebastian Andrzej Siewior instance_name++; 55088af8bbeSSebastian Andrzej Siewior 55188af8bbeSSebastian Andrzej Siewior fi = usb_get_function_instance(func_name); 55288af8bbeSSebastian Andrzej Siewior if (IS_ERR(fi)) 553a3469411SDuan Jiong return ERR_CAST(fi); 55488af8bbeSSebastian Andrzej Siewior 5553958b792SNicolas Iooss ret = config_item_set_name(&fi->group.cg_item, "%s", name); 55688af8bbeSSebastian Andrzej Siewior if (ret) { 55788af8bbeSSebastian Andrzej Siewior usb_put_function_instance(fi); 55888af8bbeSSebastian Andrzej Siewior return ERR_PTR(ret); 55988af8bbeSSebastian Andrzej Siewior } 5601933861dSAndrzej Pietrasiewicz if (fi->set_inst_name) { 5611933861dSAndrzej Pietrasiewicz ret = fi->set_inst_name(fi, instance_name); 5621933861dSAndrzej Pietrasiewicz if (ret) { 5631933861dSAndrzej Pietrasiewicz usb_put_function_instance(fi); 5641933861dSAndrzej Pietrasiewicz return ERR_PTR(ret); 5651933861dSAndrzej Pietrasiewicz } 5661933861dSAndrzej Pietrasiewicz } 56788af8bbeSSebastian Andrzej Siewior 56888af8bbeSSebastian Andrzej Siewior gi = container_of(group, struct gadget_info, functions_group); 56988af8bbeSSebastian Andrzej Siewior 57088af8bbeSSebastian Andrzej Siewior mutex_lock(&gi->lock); 57188af8bbeSSebastian Andrzej Siewior list_add_tail(&fi->cfs_list, &gi->available_func); 57288af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 57388af8bbeSSebastian Andrzej Siewior return &fi->group; 57488af8bbeSSebastian Andrzej Siewior } 57588af8bbeSSebastian Andrzej Siewior 57688af8bbeSSebastian Andrzej Siewior static void function_drop( 57788af8bbeSSebastian Andrzej Siewior struct config_group *group, 57888af8bbeSSebastian Andrzej Siewior struct config_item *item) 57988af8bbeSSebastian Andrzej Siewior { 58088af8bbeSSebastian Andrzej Siewior struct usb_function_instance *fi = to_usb_function_instance(item); 58188af8bbeSSebastian Andrzej Siewior struct gadget_info *gi; 58288af8bbeSSebastian Andrzej Siewior 58388af8bbeSSebastian Andrzej Siewior gi = container_of(group, struct gadget_info, functions_group); 58488af8bbeSSebastian Andrzej Siewior 58588af8bbeSSebastian Andrzej Siewior mutex_lock(&gi->lock); 58688af8bbeSSebastian Andrzej Siewior list_del(&fi->cfs_list); 58788af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 58888af8bbeSSebastian Andrzej Siewior config_item_put(item); 58988af8bbeSSebastian Andrzej Siewior } 59088af8bbeSSebastian Andrzej Siewior 59188af8bbeSSebastian Andrzej Siewior static struct configfs_group_operations functions_ops = { 59288af8bbeSSebastian Andrzej Siewior .make_group = &function_make, 59388af8bbeSSebastian Andrzej Siewior .drop_item = &function_drop, 59488af8bbeSSebastian Andrzej Siewior }; 59588af8bbeSSebastian Andrzej Siewior 596*4ad01412SBhumika Goyal static const struct config_item_type functions_type = { 59788af8bbeSSebastian Andrzej Siewior .ct_group_ops = &functions_ops, 59888af8bbeSSebastian Andrzej Siewior .ct_owner = THIS_MODULE, 59988af8bbeSSebastian Andrzej Siewior }; 60088af8bbeSSebastian Andrzej Siewior 60188af8bbeSSebastian Andrzej Siewior GS_STRINGS_RW(gadget_config_name, configuration); 60288af8bbeSSebastian Andrzej Siewior 60388af8bbeSSebastian Andrzej Siewior static struct configfs_attribute *gadget_config_name_langid_attrs[] = { 60445b6a73fSChristoph Hellwig &gadget_config_name_attr_configuration, 60588af8bbeSSebastian Andrzej Siewior NULL, 60688af8bbeSSebastian Andrzej Siewior }; 60788af8bbeSSebastian Andrzej Siewior 60888af8bbeSSebastian Andrzej Siewior static void gadget_config_name_attr_release(struct config_item *item) 60988af8bbeSSebastian Andrzej Siewior { 61088af8bbeSSebastian Andrzej Siewior struct gadget_config_name *cn = to_gadget_config_name(item); 61188af8bbeSSebastian Andrzej Siewior 61288af8bbeSSebastian Andrzej Siewior kfree(cn->configuration); 61388af8bbeSSebastian Andrzej Siewior 61488af8bbeSSebastian Andrzej Siewior list_del(&cn->list); 61588af8bbeSSebastian Andrzej Siewior kfree(cn); 61688af8bbeSSebastian Andrzej Siewior } 61788af8bbeSSebastian Andrzej Siewior 61888af8bbeSSebastian Andrzej Siewior USB_CONFIG_STRING_RW_OPS(gadget_config_name); 61988af8bbeSSebastian Andrzej Siewior USB_CONFIG_STRINGS_LANG(gadget_config_name, config_usb_cfg); 62088af8bbeSSebastian Andrzej Siewior 62188af8bbeSSebastian Andrzej Siewior static struct config_group *config_desc_make( 62288af8bbeSSebastian Andrzej Siewior struct config_group *group, 62388af8bbeSSebastian Andrzej Siewior const char *name) 62488af8bbeSSebastian Andrzej Siewior { 62588af8bbeSSebastian Andrzej Siewior struct gadget_info *gi; 62688af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg; 62788af8bbeSSebastian Andrzej Siewior char buf[MAX_NAME_LEN]; 62888af8bbeSSebastian Andrzej Siewior char *num_str; 62988af8bbeSSebastian Andrzej Siewior u8 num; 63088af8bbeSSebastian Andrzej Siewior int ret; 63188af8bbeSSebastian Andrzej Siewior 63288af8bbeSSebastian Andrzej Siewior gi = container_of(group, struct gadget_info, configs_group); 63388af8bbeSSebastian Andrzej Siewior ret = snprintf(buf, MAX_NAME_LEN, "%s", name); 63488af8bbeSSebastian Andrzej Siewior if (ret >= MAX_NAME_LEN) 63588af8bbeSSebastian Andrzej Siewior return ERR_PTR(-ENAMETOOLONG); 63688af8bbeSSebastian Andrzej Siewior 63788af8bbeSSebastian Andrzej Siewior num_str = strchr(buf, '.'); 63888af8bbeSSebastian Andrzej Siewior if (!num_str) { 63988af8bbeSSebastian Andrzej Siewior pr_err("Unable to locate . in name.bConfigurationValue\n"); 64088af8bbeSSebastian Andrzej Siewior return ERR_PTR(-EINVAL); 64188af8bbeSSebastian Andrzej Siewior } 64288af8bbeSSebastian Andrzej Siewior 64388af8bbeSSebastian Andrzej Siewior *num_str = '\0'; 64488af8bbeSSebastian Andrzej Siewior num_str++; 64588af8bbeSSebastian Andrzej Siewior 64688af8bbeSSebastian Andrzej Siewior if (!strlen(buf)) 64788af8bbeSSebastian Andrzej Siewior return ERR_PTR(-EINVAL); 64888af8bbeSSebastian Andrzej Siewior 64988af8bbeSSebastian Andrzej Siewior ret = kstrtou8(num_str, 0, &num); 65088af8bbeSSebastian Andrzej Siewior if (ret) 65188af8bbeSSebastian Andrzej Siewior return ERR_PTR(ret); 65288af8bbeSSebastian Andrzej Siewior 65388af8bbeSSebastian Andrzej Siewior cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); 65488af8bbeSSebastian Andrzej Siewior if (!cfg) 65588af8bbeSSebastian Andrzej Siewior return ERR_PTR(-ENOMEM); 65688af8bbeSSebastian Andrzej Siewior cfg->c.label = kstrdup(buf, GFP_KERNEL); 65788af8bbeSSebastian Andrzej Siewior if (!cfg->c.label) { 65888af8bbeSSebastian Andrzej Siewior ret = -ENOMEM; 65988af8bbeSSebastian Andrzej Siewior goto err; 66088af8bbeSSebastian Andrzej Siewior } 66188af8bbeSSebastian Andrzej Siewior cfg->c.bConfigurationValue = num; 66288af8bbeSSebastian Andrzej Siewior cfg->c.MaxPower = CONFIG_USB_GADGET_VBUS_DRAW; 66388af8bbeSSebastian Andrzej Siewior cfg->c.bmAttributes = USB_CONFIG_ATT_ONE; 66488af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&cfg->string_list); 66588af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&cfg->func_list); 66688af8bbeSSebastian Andrzej Siewior 66788af8bbeSSebastian Andrzej Siewior config_group_init_type_name(&cfg->group, name, 66888af8bbeSSebastian Andrzej Siewior &gadget_config_type); 6691ae1602dSChristoph Hellwig 67088af8bbeSSebastian Andrzej Siewior config_group_init_type_name(&cfg->strings_group, "strings", 67188af8bbeSSebastian Andrzej Siewior &gadget_config_name_strings_type); 6721ae1602dSChristoph Hellwig configfs_add_default_group(&cfg->strings_group, &cfg->group); 67388af8bbeSSebastian Andrzej Siewior 67488af8bbeSSebastian Andrzej Siewior ret = usb_add_config_only(&gi->cdev, &cfg->c); 67588af8bbeSSebastian Andrzej Siewior if (ret) 67688af8bbeSSebastian Andrzej Siewior goto err; 67788af8bbeSSebastian Andrzej Siewior 67888af8bbeSSebastian Andrzej Siewior return &cfg->group; 67988af8bbeSSebastian Andrzej Siewior err: 68088af8bbeSSebastian Andrzej Siewior kfree(cfg->c.label); 68188af8bbeSSebastian Andrzej Siewior kfree(cfg); 68288af8bbeSSebastian Andrzej Siewior return ERR_PTR(ret); 68388af8bbeSSebastian Andrzej Siewior } 68488af8bbeSSebastian Andrzej Siewior 68588af8bbeSSebastian Andrzej Siewior static void config_desc_drop( 68688af8bbeSSebastian Andrzej Siewior struct config_group *group, 68788af8bbeSSebastian Andrzej Siewior struct config_item *item) 68888af8bbeSSebastian Andrzej Siewior { 68988af8bbeSSebastian Andrzej Siewior config_item_put(item); 69088af8bbeSSebastian Andrzej Siewior } 69188af8bbeSSebastian Andrzej Siewior 69288af8bbeSSebastian Andrzej Siewior static struct configfs_group_operations config_desc_ops = { 69388af8bbeSSebastian Andrzej Siewior .make_group = &config_desc_make, 69488af8bbeSSebastian Andrzej Siewior .drop_item = &config_desc_drop, 69588af8bbeSSebastian Andrzej Siewior }; 69688af8bbeSSebastian Andrzej Siewior 697*4ad01412SBhumika Goyal static const struct config_item_type config_desc_type = { 69888af8bbeSSebastian Andrzej Siewior .ct_group_ops = &config_desc_ops, 69988af8bbeSSebastian Andrzej Siewior .ct_owner = THIS_MODULE, 70088af8bbeSSebastian Andrzej Siewior }; 70188af8bbeSSebastian Andrzej Siewior 70288af8bbeSSebastian Andrzej Siewior GS_STRINGS_RW(gadget_strings, manufacturer); 70388af8bbeSSebastian Andrzej Siewior GS_STRINGS_RW(gadget_strings, product); 70488af8bbeSSebastian Andrzej Siewior GS_STRINGS_RW(gadget_strings, serialnumber); 70588af8bbeSSebastian Andrzej Siewior 70688af8bbeSSebastian Andrzej Siewior static struct configfs_attribute *gadget_strings_langid_attrs[] = { 70745b6a73fSChristoph Hellwig &gadget_strings_attr_manufacturer, 70845b6a73fSChristoph Hellwig &gadget_strings_attr_product, 70945b6a73fSChristoph Hellwig &gadget_strings_attr_serialnumber, 71088af8bbeSSebastian Andrzej Siewior NULL, 71188af8bbeSSebastian Andrzej Siewior }; 71288af8bbeSSebastian Andrzej Siewior 71388af8bbeSSebastian Andrzej Siewior static void gadget_strings_attr_release(struct config_item *item) 71488af8bbeSSebastian Andrzej Siewior { 71588af8bbeSSebastian Andrzej Siewior struct gadget_strings *gs = to_gadget_strings(item); 71688af8bbeSSebastian Andrzej Siewior 71788af8bbeSSebastian Andrzej Siewior kfree(gs->manufacturer); 71888af8bbeSSebastian Andrzej Siewior kfree(gs->product); 71988af8bbeSSebastian Andrzej Siewior kfree(gs->serialnumber); 72088af8bbeSSebastian Andrzej Siewior 72188af8bbeSSebastian Andrzej Siewior list_del(&gs->list); 72288af8bbeSSebastian Andrzej Siewior kfree(gs); 72388af8bbeSSebastian Andrzej Siewior } 72488af8bbeSSebastian Andrzej Siewior 72588af8bbeSSebastian Andrzej Siewior USB_CONFIG_STRING_RW_OPS(gadget_strings); 72688af8bbeSSebastian Andrzej Siewior USB_CONFIG_STRINGS_LANG(gadget_strings, gadget_info); 72788af8bbeSSebastian Andrzej Siewior 72887213d38SAndrzej Pietrasiewicz static inline struct os_desc *to_os_desc(struct config_item *item) 72987213d38SAndrzej Pietrasiewicz { 73087213d38SAndrzej Pietrasiewicz return container_of(to_config_group(item), struct os_desc, group); 73187213d38SAndrzej Pietrasiewicz } 73287213d38SAndrzej Pietrasiewicz 73345b6a73fSChristoph Hellwig static inline struct gadget_info *os_desc_item_to_gadget_info( 73445b6a73fSChristoph Hellwig struct config_item *item) 73587213d38SAndrzej Pietrasiewicz { 73645b6a73fSChristoph Hellwig return to_gadget_info(to_os_desc(item)->group.cg_item.ci_parent); 73787213d38SAndrzej Pietrasiewicz } 73887213d38SAndrzej Pietrasiewicz 73945b6a73fSChristoph Hellwig static ssize_t os_desc_use_show(struct config_item *item, char *page) 74045b6a73fSChristoph Hellwig { 741e800e8cbSStefan Agner return sprintf(page, "%d\n", 74245b6a73fSChristoph Hellwig os_desc_item_to_gadget_info(item)->use_os_desc); 74345b6a73fSChristoph Hellwig } 74445b6a73fSChristoph Hellwig 74545b6a73fSChristoph Hellwig static ssize_t os_desc_use_store(struct config_item *item, const char *page, 74687213d38SAndrzej Pietrasiewicz size_t len) 74787213d38SAndrzej Pietrasiewicz { 74845b6a73fSChristoph Hellwig struct gadget_info *gi = os_desc_item_to_gadget_info(item); 74987213d38SAndrzej Pietrasiewicz int ret; 75087213d38SAndrzej Pietrasiewicz bool use; 75187213d38SAndrzej Pietrasiewicz 75287213d38SAndrzej Pietrasiewicz mutex_lock(&gi->lock); 75387213d38SAndrzej Pietrasiewicz ret = strtobool(page, &use); 75487213d38SAndrzej Pietrasiewicz if (!ret) { 75587213d38SAndrzej Pietrasiewicz gi->use_os_desc = use; 75687213d38SAndrzej Pietrasiewicz ret = len; 75787213d38SAndrzej Pietrasiewicz } 75887213d38SAndrzej Pietrasiewicz mutex_unlock(&gi->lock); 75987213d38SAndrzej Pietrasiewicz 76087213d38SAndrzej Pietrasiewicz return ret; 76187213d38SAndrzej Pietrasiewicz } 76287213d38SAndrzej Pietrasiewicz 76345b6a73fSChristoph Hellwig static ssize_t os_desc_b_vendor_code_show(struct config_item *item, char *page) 76487213d38SAndrzej Pietrasiewicz { 765e800e8cbSStefan Agner return sprintf(page, "0x%02x\n", 76645b6a73fSChristoph Hellwig os_desc_item_to_gadget_info(item)->b_vendor_code); 76787213d38SAndrzej Pietrasiewicz } 76887213d38SAndrzej Pietrasiewicz 76945b6a73fSChristoph Hellwig static ssize_t os_desc_b_vendor_code_store(struct config_item *item, 77087213d38SAndrzej Pietrasiewicz const char *page, size_t len) 77187213d38SAndrzej Pietrasiewicz { 77245b6a73fSChristoph Hellwig struct gadget_info *gi = os_desc_item_to_gadget_info(item); 77387213d38SAndrzej Pietrasiewicz int ret; 77487213d38SAndrzej Pietrasiewicz u8 b_vendor_code; 77587213d38SAndrzej Pietrasiewicz 77687213d38SAndrzej Pietrasiewicz mutex_lock(&gi->lock); 77787213d38SAndrzej Pietrasiewicz ret = kstrtou8(page, 0, &b_vendor_code); 77887213d38SAndrzej Pietrasiewicz if (!ret) { 77987213d38SAndrzej Pietrasiewicz gi->b_vendor_code = b_vendor_code; 78087213d38SAndrzej Pietrasiewicz ret = len; 78187213d38SAndrzej Pietrasiewicz } 78287213d38SAndrzej Pietrasiewicz mutex_unlock(&gi->lock); 78387213d38SAndrzej Pietrasiewicz 78487213d38SAndrzej Pietrasiewicz return ret; 78587213d38SAndrzej Pietrasiewicz } 78687213d38SAndrzej Pietrasiewicz 78745b6a73fSChristoph Hellwig static ssize_t os_desc_qw_sign_show(struct config_item *item, char *page) 78887213d38SAndrzej Pietrasiewicz { 78945b6a73fSChristoph Hellwig struct gadget_info *gi = os_desc_item_to_gadget_info(item); 79076180d71SStefan Agner int res; 79187213d38SAndrzej Pietrasiewicz 79276180d71SStefan Agner res = utf16s_to_utf8s((wchar_t *) gi->qw_sign, OS_STRING_QW_SIGN_LEN, 79376180d71SStefan Agner UTF16_LITTLE_ENDIAN, page, PAGE_SIZE - 1); 79476180d71SStefan Agner page[res++] = '\n'; 79576180d71SStefan Agner 79676180d71SStefan Agner return res; 79787213d38SAndrzej Pietrasiewicz } 79887213d38SAndrzej Pietrasiewicz 79945b6a73fSChristoph Hellwig static ssize_t os_desc_qw_sign_store(struct config_item *item, const char *page, 80087213d38SAndrzej Pietrasiewicz size_t len) 80187213d38SAndrzej Pietrasiewicz { 80245b6a73fSChristoph Hellwig struct gadget_info *gi = os_desc_item_to_gadget_info(item); 80387213d38SAndrzej Pietrasiewicz int res, l; 80487213d38SAndrzej Pietrasiewicz 80587213d38SAndrzej Pietrasiewicz l = min((int)len, OS_STRING_QW_SIGN_LEN >> 1); 80687213d38SAndrzej Pietrasiewicz if (page[l - 1] == '\n') 80787213d38SAndrzej Pietrasiewicz --l; 80887213d38SAndrzej Pietrasiewicz 80987213d38SAndrzej Pietrasiewicz mutex_lock(&gi->lock); 81087213d38SAndrzej Pietrasiewicz res = utf8s_to_utf16s(page, l, 81187213d38SAndrzej Pietrasiewicz UTF16_LITTLE_ENDIAN, (wchar_t *) gi->qw_sign, 81287213d38SAndrzej Pietrasiewicz OS_STRING_QW_SIGN_LEN); 81387213d38SAndrzej Pietrasiewicz if (res > 0) 81487213d38SAndrzej Pietrasiewicz res = len; 81587213d38SAndrzej Pietrasiewicz mutex_unlock(&gi->lock); 81687213d38SAndrzej Pietrasiewicz 81787213d38SAndrzej Pietrasiewicz return res; 81887213d38SAndrzej Pietrasiewicz } 81987213d38SAndrzej Pietrasiewicz 82045b6a73fSChristoph Hellwig CONFIGFS_ATTR(os_desc_, use); 82145b6a73fSChristoph Hellwig CONFIGFS_ATTR(os_desc_, b_vendor_code); 82245b6a73fSChristoph Hellwig CONFIGFS_ATTR(os_desc_, qw_sign); 82387213d38SAndrzej Pietrasiewicz 82487213d38SAndrzej Pietrasiewicz static struct configfs_attribute *os_desc_attrs[] = { 82545b6a73fSChristoph Hellwig &os_desc_attr_use, 82645b6a73fSChristoph Hellwig &os_desc_attr_b_vendor_code, 82745b6a73fSChristoph Hellwig &os_desc_attr_qw_sign, 82887213d38SAndrzej Pietrasiewicz NULL, 82987213d38SAndrzej Pietrasiewicz }; 83087213d38SAndrzej Pietrasiewicz 83187213d38SAndrzej Pietrasiewicz static void os_desc_attr_release(struct config_item *item) 83287213d38SAndrzej Pietrasiewicz { 83387213d38SAndrzej Pietrasiewicz struct os_desc *os_desc = to_os_desc(item); 83487213d38SAndrzej Pietrasiewicz kfree(os_desc); 83587213d38SAndrzej Pietrasiewicz } 83687213d38SAndrzej Pietrasiewicz 837da424314SAndrzej Pietrasiewicz static int os_desc_link(struct config_item *os_desc_ci, 838da424314SAndrzej Pietrasiewicz struct config_item *usb_cfg_ci) 839da424314SAndrzej Pietrasiewicz { 840da424314SAndrzej Pietrasiewicz struct gadget_info *gi = container_of(to_config_group(os_desc_ci), 841da424314SAndrzej Pietrasiewicz struct gadget_info, os_desc_group); 842da424314SAndrzej Pietrasiewicz struct usb_composite_dev *cdev = &gi->cdev; 843da424314SAndrzej Pietrasiewicz struct config_usb_cfg *c_target = 844da424314SAndrzej Pietrasiewicz container_of(to_config_group(usb_cfg_ci), 845da424314SAndrzej Pietrasiewicz struct config_usb_cfg, group); 846da424314SAndrzej Pietrasiewicz struct usb_configuration *c; 847da424314SAndrzej Pietrasiewicz int ret; 848da424314SAndrzej Pietrasiewicz 849da424314SAndrzej Pietrasiewicz mutex_lock(&gi->lock); 850da424314SAndrzej Pietrasiewicz list_for_each_entry(c, &cdev->configs, list) { 851da424314SAndrzej Pietrasiewicz if (c == &c_target->c) 852da424314SAndrzej Pietrasiewicz break; 853da424314SAndrzej Pietrasiewicz } 854da424314SAndrzej Pietrasiewicz if (c != &c_target->c) { 855da424314SAndrzej Pietrasiewicz ret = -EINVAL; 856da424314SAndrzej Pietrasiewicz goto out; 857da424314SAndrzej Pietrasiewicz } 858da424314SAndrzej Pietrasiewicz 859da424314SAndrzej Pietrasiewicz if (cdev->os_desc_config) { 860da424314SAndrzej Pietrasiewicz ret = -EBUSY; 861da424314SAndrzej Pietrasiewicz goto out; 862da424314SAndrzej Pietrasiewicz } 863da424314SAndrzej Pietrasiewicz 864da424314SAndrzej Pietrasiewicz cdev->os_desc_config = &c_target->c; 865da424314SAndrzej Pietrasiewicz ret = 0; 866da424314SAndrzej Pietrasiewicz 867da424314SAndrzej Pietrasiewicz out: 868da424314SAndrzej Pietrasiewicz mutex_unlock(&gi->lock); 869da424314SAndrzej Pietrasiewicz return ret; 870da424314SAndrzej Pietrasiewicz } 871da424314SAndrzej Pietrasiewicz 872e16769d4SAndrzej Pietrasiewicz static void os_desc_unlink(struct config_item *os_desc_ci, 873da424314SAndrzej Pietrasiewicz struct config_item *usb_cfg_ci) 874da424314SAndrzej Pietrasiewicz { 875da424314SAndrzej Pietrasiewicz struct gadget_info *gi = container_of(to_config_group(os_desc_ci), 876da424314SAndrzej Pietrasiewicz struct gadget_info, os_desc_group); 877da424314SAndrzej Pietrasiewicz struct usb_composite_dev *cdev = &gi->cdev; 878da424314SAndrzej Pietrasiewicz 879da424314SAndrzej Pietrasiewicz mutex_lock(&gi->lock); 880afdaadc3SRuslan Bilovol if (gi->composite.gadget_driver.udc_name) 881da424314SAndrzej Pietrasiewicz unregister_gadget(gi); 882da424314SAndrzej Pietrasiewicz cdev->os_desc_config = NULL; 883afdaadc3SRuslan Bilovol WARN_ON(gi->composite.gadget_driver.udc_name); 884da424314SAndrzej Pietrasiewicz mutex_unlock(&gi->lock); 885da424314SAndrzej Pietrasiewicz } 886da424314SAndrzej Pietrasiewicz 88787213d38SAndrzej Pietrasiewicz static struct configfs_item_operations os_desc_ops = { 88887213d38SAndrzej Pietrasiewicz .release = os_desc_attr_release, 889da424314SAndrzej Pietrasiewicz .allow_link = os_desc_link, 890da424314SAndrzej Pietrasiewicz .drop_link = os_desc_unlink, 89187213d38SAndrzej Pietrasiewicz }; 89287213d38SAndrzej Pietrasiewicz 89387213d38SAndrzej Pietrasiewicz static struct config_item_type os_desc_type = { 89487213d38SAndrzej Pietrasiewicz .ct_item_ops = &os_desc_ops, 89587213d38SAndrzej Pietrasiewicz .ct_attrs = os_desc_attrs, 89687213d38SAndrzej Pietrasiewicz .ct_owner = THIS_MODULE, 89787213d38SAndrzej Pietrasiewicz }; 89887213d38SAndrzej Pietrasiewicz 8997419485fSAndrzej Pietrasiewicz static inline struct usb_os_desc_ext_prop 9007419485fSAndrzej Pietrasiewicz *to_usb_os_desc_ext_prop(struct config_item *item) 9017419485fSAndrzej Pietrasiewicz { 9027419485fSAndrzej Pietrasiewicz return container_of(item, struct usb_os_desc_ext_prop, item); 9037419485fSAndrzej Pietrasiewicz } 9047419485fSAndrzej Pietrasiewicz 90545b6a73fSChristoph Hellwig static ssize_t ext_prop_type_show(struct config_item *item, char *page) 9067419485fSAndrzej Pietrasiewicz { 907e800e8cbSStefan Agner return sprintf(page, "%d\n", to_usb_os_desc_ext_prop(item)->type); 9087419485fSAndrzej Pietrasiewicz } 9097419485fSAndrzej Pietrasiewicz 91045b6a73fSChristoph Hellwig static ssize_t ext_prop_type_store(struct config_item *item, 9117419485fSAndrzej Pietrasiewicz const char *page, size_t len) 9127419485fSAndrzej Pietrasiewicz { 91345b6a73fSChristoph Hellwig struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); 9147419485fSAndrzej Pietrasiewicz struct usb_os_desc *desc = to_usb_os_desc(ext_prop->item.ci_parent); 9157419485fSAndrzej Pietrasiewicz u8 type; 9167419485fSAndrzej Pietrasiewicz int ret; 9177419485fSAndrzej Pietrasiewicz 9187419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 9197419485fSAndrzej Pietrasiewicz mutex_lock(desc->opts_mutex); 9207419485fSAndrzej Pietrasiewicz ret = kstrtou8(page, 0, &type); 9217419485fSAndrzej Pietrasiewicz if (ret) 9227419485fSAndrzej Pietrasiewicz goto end; 9237419485fSAndrzej Pietrasiewicz if (type < USB_EXT_PROP_UNICODE || type > USB_EXT_PROP_UNICODE_MULTI) { 9247419485fSAndrzej Pietrasiewicz ret = -EINVAL; 9257419485fSAndrzej Pietrasiewicz goto end; 9267419485fSAndrzej Pietrasiewicz } 9277419485fSAndrzej Pietrasiewicz 9287419485fSAndrzej Pietrasiewicz if ((ext_prop->type == USB_EXT_PROP_BINARY || 9297419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_LE32 || 9307419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_BE32) && 9317419485fSAndrzej Pietrasiewicz (type == USB_EXT_PROP_UNICODE || 9327419485fSAndrzej Pietrasiewicz type == USB_EXT_PROP_UNICODE_ENV || 9337419485fSAndrzej Pietrasiewicz type == USB_EXT_PROP_UNICODE_LINK)) 9347419485fSAndrzej Pietrasiewicz ext_prop->data_len <<= 1; 9357419485fSAndrzej Pietrasiewicz else if ((ext_prop->type == USB_EXT_PROP_UNICODE || 9367419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_UNICODE_ENV || 9377419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_UNICODE_LINK) && 9387419485fSAndrzej Pietrasiewicz (type == USB_EXT_PROP_BINARY || 9397419485fSAndrzej Pietrasiewicz type == USB_EXT_PROP_LE32 || 9407419485fSAndrzej Pietrasiewicz type == USB_EXT_PROP_BE32)) 9417419485fSAndrzej Pietrasiewicz ext_prop->data_len >>= 1; 9427419485fSAndrzej Pietrasiewicz ext_prop->type = type; 9437419485fSAndrzej Pietrasiewicz ret = len; 9447419485fSAndrzej Pietrasiewicz 9457419485fSAndrzej Pietrasiewicz end: 9467419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 9477419485fSAndrzej Pietrasiewicz mutex_unlock(desc->opts_mutex); 9487419485fSAndrzej Pietrasiewicz return ret; 9497419485fSAndrzej Pietrasiewicz } 9507419485fSAndrzej Pietrasiewicz 95145b6a73fSChristoph Hellwig static ssize_t ext_prop_data_show(struct config_item *item, char *page) 9527419485fSAndrzej Pietrasiewicz { 95345b6a73fSChristoph Hellwig struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); 9547419485fSAndrzej Pietrasiewicz int len = ext_prop->data_len; 9557419485fSAndrzej Pietrasiewicz 9567419485fSAndrzej Pietrasiewicz if (ext_prop->type == USB_EXT_PROP_UNICODE || 9577419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_UNICODE_ENV || 9587419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_UNICODE_LINK) 9597419485fSAndrzej Pietrasiewicz len >>= 1; 9607419485fSAndrzej Pietrasiewicz memcpy(page, ext_prop->data, len); 9617419485fSAndrzej Pietrasiewicz 9627419485fSAndrzej Pietrasiewicz return len; 9637419485fSAndrzej Pietrasiewicz } 9647419485fSAndrzej Pietrasiewicz 96545b6a73fSChristoph Hellwig static ssize_t ext_prop_data_store(struct config_item *item, 9667419485fSAndrzej Pietrasiewicz const char *page, size_t len) 9677419485fSAndrzej Pietrasiewicz { 96845b6a73fSChristoph Hellwig struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); 9697419485fSAndrzej Pietrasiewicz struct usb_os_desc *desc = to_usb_os_desc(ext_prop->item.ci_parent); 9707419485fSAndrzej Pietrasiewicz char *new_data; 9717419485fSAndrzej Pietrasiewicz size_t ret_len = len; 9727419485fSAndrzej Pietrasiewicz 9737419485fSAndrzej Pietrasiewicz if (page[len - 1] == '\n' || page[len - 1] == '\0') 9747419485fSAndrzej Pietrasiewicz --len; 97558b949e0SBenoit Taine new_data = kmemdup(page, len, GFP_KERNEL); 9767419485fSAndrzej Pietrasiewicz if (!new_data) 9777419485fSAndrzej Pietrasiewicz return -ENOMEM; 9787419485fSAndrzej Pietrasiewicz 9797419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 9807419485fSAndrzej Pietrasiewicz mutex_lock(desc->opts_mutex); 9817419485fSAndrzej Pietrasiewicz kfree(ext_prop->data); 9827419485fSAndrzej Pietrasiewicz ext_prop->data = new_data; 9837419485fSAndrzej Pietrasiewicz desc->ext_prop_len -= ext_prop->data_len; 9847419485fSAndrzej Pietrasiewicz ext_prop->data_len = len; 9857419485fSAndrzej Pietrasiewicz desc->ext_prop_len += ext_prop->data_len; 9867419485fSAndrzej Pietrasiewicz if (ext_prop->type == USB_EXT_PROP_UNICODE || 9877419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_UNICODE_ENV || 9887419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_UNICODE_LINK) { 9897419485fSAndrzej Pietrasiewicz desc->ext_prop_len -= ext_prop->data_len; 9907419485fSAndrzej Pietrasiewicz ext_prop->data_len <<= 1; 9917419485fSAndrzej Pietrasiewicz ext_prop->data_len += 2; 9927419485fSAndrzej Pietrasiewicz desc->ext_prop_len += ext_prop->data_len; 9937419485fSAndrzej Pietrasiewicz } 9947419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 9957419485fSAndrzej Pietrasiewicz mutex_unlock(desc->opts_mutex); 9967419485fSAndrzej Pietrasiewicz return ret_len; 9977419485fSAndrzej Pietrasiewicz } 9987419485fSAndrzej Pietrasiewicz 99945b6a73fSChristoph Hellwig CONFIGFS_ATTR(ext_prop_, type); 100045b6a73fSChristoph Hellwig CONFIGFS_ATTR(ext_prop_, data); 10017419485fSAndrzej Pietrasiewicz 10027419485fSAndrzej Pietrasiewicz static struct configfs_attribute *ext_prop_attrs[] = { 100345b6a73fSChristoph Hellwig &ext_prop_attr_type, 100445b6a73fSChristoph Hellwig &ext_prop_attr_data, 10057419485fSAndrzej Pietrasiewicz NULL, 10067419485fSAndrzej Pietrasiewicz }; 10077419485fSAndrzej Pietrasiewicz 10087419485fSAndrzej Pietrasiewicz static void usb_os_desc_ext_prop_release(struct config_item *item) 10097419485fSAndrzej Pietrasiewicz { 10107419485fSAndrzej Pietrasiewicz struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); 10117419485fSAndrzej Pietrasiewicz 10127419485fSAndrzej Pietrasiewicz kfree(ext_prop); /* frees a whole chunk */ 10137419485fSAndrzej Pietrasiewicz } 10147419485fSAndrzej Pietrasiewicz 10157419485fSAndrzej Pietrasiewicz static struct configfs_item_operations ext_prop_ops = { 10167419485fSAndrzej Pietrasiewicz .release = usb_os_desc_ext_prop_release, 10177419485fSAndrzej Pietrasiewicz }; 10187419485fSAndrzej Pietrasiewicz 10197419485fSAndrzej Pietrasiewicz static struct config_item *ext_prop_make( 10207419485fSAndrzej Pietrasiewicz struct config_group *group, 10217419485fSAndrzej Pietrasiewicz const char *name) 10227419485fSAndrzej Pietrasiewicz { 10237419485fSAndrzej Pietrasiewicz struct usb_os_desc_ext_prop *ext_prop; 10247419485fSAndrzej Pietrasiewicz struct config_item_type *ext_prop_type; 10257419485fSAndrzej Pietrasiewicz struct usb_os_desc *desc; 10267419485fSAndrzej Pietrasiewicz char *vlabuf; 10277419485fSAndrzej Pietrasiewicz 10287419485fSAndrzej Pietrasiewicz vla_group(data_chunk); 10297419485fSAndrzej Pietrasiewicz vla_item(data_chunk, struct usb_os_desc_ext_prop, ext_prop, 1); 10307419485fSAndrzej Pietrasiewicz vla_item(data_chunk, struct config_item_type, ext_prop_type, 1); 10317419485fSAndrzej Pietrasiewicz 10327419485fSAndrzej Pietrasiewicz vlabuf = kzalloc(vla_group_size(data_chunk), GFP_KERNEL); 10337419485fSAndrzej Pietrasiewicz if (!vlabuf) 10347419485fSAndrzej Pietrasiewicz return ERR_PTR(-ENOMEM); 10357419485fSAndrzej Pietrasiewicz 10367419485fSAndrzej Pietrasiewicz ext_prop = vla_ptr(vlabuf, data_chunk, ext_prop); 10377419485fSAndrzej Pietrasiewicz ext_prop_type = vla_ptr(vlabuf, data_chunk, ext_prop_type); 10387419485fSAndrzej Pietrasiewicz 10397419485fSAndrzej Pietrasiewicz desc = container_of(group, struct usb_os_desc, group); 10407419485fSAndrzej Pietrasiewicz ext_prop_type->ct_item_ops = &ext_prop_ops; 10417419485fSAndrzej Pietrasiewicz ext_prop_type->ct_attrs = ext_prop_attrs; 10427419485fSAndrzej Pietrasiewicz ext_prop_type->ct_owner = desc->owner; 10437419485fSAndrzej Pietrasiewicz 10447419485fSAndrzej Pietrasiewicz config_item_init_type_name(&ext_prop->item, name, ext_prop_type); 10457419485fSAndrzej Pietrasiewicz 10467419485fSAndrzej Pietrasiewicz ext_prop->name = kstrdup(name, GFP_KERNEL); 10477419485fSAndrzej Pietrasiewicz if (!ext_prop->name) { 10487419485fSAndrzej Pietrasiewicz kfree(vlabuf); 10497419485fSAndrzej Pietrasiewicz return ERR_PTR(-ENOMEM); 10507419485fSAndrzej Pietrasiewicz } 10517419485fSAndrzej Pietrasiewicz desc->ext_prop_len += 14; 10527419485fSAndrzej Pietrasiewicz ext_prop->name_len = 2 * strlen(ext_prop->name) + 2; 10537419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 10547419485fSAndrzej Pietrasiewicz mutex_lock(desc->opts_mutex); 10557419485fSAndrzej Pietrasiewicz desc->ext_prop_len += ext_prop->name_len; 10567419485fSAndrzej Pietrasiewicz list_add_tail(&ext_prop->entry, &desc->ext_prop); 10577419485fSAndrzej Pietrasiewicz ++desc->ext_prop_count; 10587419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 10597419485fSAndrzej Pietrasiewicz mutex_unlock(desc->opts_mutex); 10607419485fSAndrzej Pietrasiewicz 10617419485fSAndrzej Pietrasiewicz return &ext_prop->item; 10627419485fSAndrzej Pietrasiewicz } 10637419485fSAndrzej Pietrasiewicz 10647419485fSAndrzej Pietrasiewicz static void ext_prop_drop(struct config_group *group, struct config_item *item) 10657419485fSAndrzej Pietrasiewicz { 10667419485fSAndrzej Pietrasiewicz struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); 10677419485fSAndrzej Pietrasiewicz struct usb_os_desc *desc = to_usb_os_desc(&group->cg_item); 10687419485fSAndrzej Pietrasiewicz 10697419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 10707419485fSAndrzej Pietrasiewicz mutex_lock(desc->opts_mutex); 10717419485fSAndrzej Pietrasiewicz list_del(&ext_prop->entry); 10727419485fSAndrzej Pietrasiewicz --desc->ext_prop_count; 10737419485fSAndrzej Pietrasiewicz kfree(ext_prop->name); 10747419485fSAndrzej Pietrasiewicz desc->ext_prop_len -= (ext_prop->name_len + ext_prop->data_len + 14); 10757419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 10767419485fSAndrzej Pietrasiewicz mutex_unlock(desc->opts_mutex); 10777419485fSAndrzej Pietrasiewicz config_item_put(item); 10787419485fSAndrzej Pietrasiewicz } 10797419485fSAndrzej Pietrasiewicz 10807419485fSAndrzej Pietrasiewicz static struct configfs_group_operations interf_grp_ops = { 10817419485fSAndrzej Pietrasiewicz .make_item = &ext_prop_make, 10827419485fSAndrzej Pietrasiewicz .drop_item = &ext_prop_drop, 10837419485fSAndrzej Pietrasiewicz }; 10847419485fSAndrzej Pietrasiewicz 108545b6a73fSChristoph Hellwig static ssize_t interf_grp_compatible_id_show(struct config_item *item, 1086da424314SAndrzej Pietrasiewicz char *page) 1087da424314SAndrzej Pietrasiewicz { 108845b6a73fSChristoph Hellwig memcpy(page, to_usb_os_desc(item)->ext_compat_id, 8); 1089da424314SAndrzej Pietrasiewicz return 8; 1090da424314SAndrzej Pietrasiewicz } 1091da424314SAndrzej Pietrasiewicz 109245b6a73fSChristoph Hellwig static ssize_t interf_grp_compatible_id_store(struct config_item *item, 1093da424314SAndrzej Pietrasiewicz const char *page, size_t len) 1094da424314SAndrzej Pietrasiewicz { 109545b6a73fSChristoph Hellwig struct usb_os_desc *desc = to_usb_os_desc(item); 1096da424314SAndrzej Pietrasiewicz int l; 1097da424314SAndrzej Pietrasiewicz 1098da424314SAndrzej Pietrasiewicz l = min_t(int, 8, len); 1099da424314SAndrzej Pietrasiewicz if (page[l - 1] == '\n') 1100da424314SAndrzej Pietrasiewicz --l; 1101da424314SAndrzej Pietrasiewicz if (desc->opts_mutex) 1102da424314SAndrzej Pietrasiewicz mutex_lock(desc->opts_mutex); 1103da424314SAndrzej Pietrasiewicz memcpy(desc->ext_compat_id, page, l); 1104da424314SAndrzej Pietrasiewicz 1105da424314SAndrzej Pietrasiewicz if (desc->opts_mutex) 1106da424314SAndrzej Pietrasiewicz mutex_unlock(desc->opts_mutex); 1107da424314SAndrzej Pietrasiewicz 1108da424314SAndrzej Pietrasiewicz return len; 1109da424314SAndrzej Pietrasiewicz } 1110da424314SAndrzej Pietrasiewicz 111145b6a73fSChristoph Hellwig static ssize_t interf_grp_sub_compatible_id_show(struct config_item *item, 1112da424314SAndrzej Pietrasiewicz char *page) 1113da424314SAndrzej Pietrasiewicz { 111445b6a73fSChristoph Hellwig memcpy(page, to_usb_os_desc(item)->ext_compat_id + 8, 8); 1115da424314SAndrzej Pietrasiewicz return 8; 1116da424314SAndrzej Pietrasiewicz } 1117da424314SAndrzej Pietrasiewicz 111845b6a73fSChristoph Hellwig static ssize_t interf_grp_sub_compatible_id_store(struct config_item *item, 1119da424314SAndrzej Pietrasiewicz const char *page, size_t len) 1120da424314SAndrzej Pietrasiewicz { 112145b6a73fSChristoph Hellwig struct usb_os_desc *desc = to_usb_os_desc(item); 1122da424314SAndrzej Pietrasiewicz int l; 1123da424314SAndrzej Pietrasiewicz 1124da424314SAndrzej Pietrasiewicz l = min_t(int, 8, len); 1125da424314SAndrzej Pietrasiewicz if (page[l - 1] == '\n') 1126da424314SAndrzej Pietrasiewicz --l; 1127da424314SAndrzej Pietrasiewicz if (desc->opts_mutex) 1128da424314SAndrzej Pietrasiewicz mutex_lock(desc->opts_mutex); 1129da424314SAndrzej Pietrasiewicz memcpy(desc->ext_compat_id + 8, page, l); 1130da424314SAndrzej Pietrasiewicz 1131da424314SAndrzej Pietrasiewicz if (desc->opts_mutex) 1132da424314SAndrzej Pietrasiewicz mutex_unlock(desc->opts_mutex); 1133da424314SAndrzej Pietrasiewicz 1134da424314SAndrzej Pietrasiewicz return len; 1135da424314SAndrzej Pietrasiewicz } 1136da424314SAndrzej Pietrasiewicz 113745b6a73fSChristoph Hellwig CONFIGFS_ATTR(interf_grp_, compatible_id); 113845b6a73fSChristoph Hellwig CONFIGFS_ATTR(interf_grp_, sub_compatible_id); 1139da424314SAndrzej Pietrasiewicz 1140da424314SAndrzej Pietrasiewicz static struct configfs_attribute *interf_grp_attrs[] = { 114145b6a73fSChristoph Hellwig &interf_grp_attr_compatible_id, 114245b6a73fSChristoph Hellwig &interf_grp_attr_sub_compatible_id, 1143da424314SAndrzej Pietrasiewicz NULL 1144da424314SAndrzej Pietrasiewicz }; 1145da424314SAndrzej Pietrasiewicz 1146ff74745eSAndrew Gabbasov struct config_group *usb_os_desc_prepare_interf_dir( 1147ff74745eSAndrew Gabbasov struct config_group *parent, 1148da424314SAndrzej Pietrasiewicz int n_interf, 1149da424314SAndrzej Pietrasiewicz struct usb_os_desc **desc, 115014574b54SAndrzej Pietrasiewicz char **names, 1151da424314SAndrzej Pietrasiewicz struct module *owner) 1152da424314SAndrzej Pietrasiewicz { 11531ae1602dSChristoph Hellwig struct config_group *os_desc_group; 1154da424314SAndrzej Pietrasiewicz struct config_item_type *os_desc_type, *interface_type; 1155da424314SAndrzej Pietrasiewicz 1156da424314SAndrzej Pietrasiewicz vla_group(data_chunk); 1157da424314SAndrzej Pietrasiewicz vla_item(data_chunk, struct config_group, os_desc_group, 1); 1158da424314SAndrzej Pietrasiewicz vla_item(data_chunk, struct config_item_type, os_desc_type, 1); 1159da424314SAndrzej Pietrasiewicz vla_item(data_chunk, struct config_item_type, interface_type, 1); 1160da424314SAndrzej Pietrasiewicz 1161da424314SAndrzej Pietrasiewicz char *vlabuf = kzalloc(vla_group_size(data_chunk), GFP_KERNEL); 1162da424314SAndrzej Pietrasiewicz if (!vlabuf) 1163ff74745eSAndrew Gabbasov return ERR_PTR(-ENOMEM); 1164da424314SAndrzej Pietrasiewicz 1165da424314SAndrzej Pietrasiewicz os_desc_group = vla_ptr(vlabuf, data_chunk, os_desc_group); 1166da424314SAndrzej Pietrasiewicz os_desc_type = vla_ptr(vlabuf, data_chunk, os_desc_type); 1167da424314SAndrzej Pietrasiewicz interface_type = vla_ptr(vlabuf, data_chunk, interface_type); 1168da424314SAndrzej Pietrasiewicz 1169da424314SAndrzej Pietrasiewicz os_desc_type->ct_owner = owner; 1170da424314SAndrzej Pietrasiewicz config_group_init_type_name(os_desc_group, "os_desc", os_desc_type); 11711ae1602dSChristoph Hellwig configfs_add_default_group(os_desc_group, parent); 1172da424314SAndrzej Pietrasiewicz 11737419485fSAndrzej Pietrasiewicz interface_type->ct_group_ops = &interf_grp_ops; 1174da424314SAndrzej Pietrasiewicz interface_type->ct_attrs = interf_grp_attrs; 1175da424314SAndrzej Pietrasiewicz interface_type->ct_owner = owner; 1176da424314SAndrzej Pietrasiewicz 1177da424314SAndrzej Pietrasiewicz while (n_interf--) { 1178da424314SAndrzej Pietrasiewicz struct usb_os_desc *d; 1179da424314SAndrzej Pietrasiewicz 1180da424314SAndrzej Pietrasiewicz d = desc[n_interf]; 11817419485fSAndrzej Pietrasiewicz d->owner = owner; 1182da424314SAndrzej Pietrasiewicz config_group_init_type_name(&d->group, "", interface_type); 118314574b54SAndrzej Pietrasiewicz config_item_set_name(&d->group.cg_item, "interface.%s", 118414574b54SAndrzej Pietrasiewicz names[n_interf]); 11851ae1602dSChristoph Hellwig configfs_add_default_group(&d->group, os_desc_group); 1186da424314SAndrzej Pietrasiewicz } 1187da424314SAndrzej Pietrasiewicz 1188ff74745eSAndrew Gabbasov return os_desc_group; 1189da424314SAndrzej Pietrasiewicz } 1190da424314SAndrzej Pietrasiewicz EXPORT_SYMBOL(usb_os_desc_prepare_interf_dir); 1191da424314SAndrzej Pietrasiewicz 119288af8bbeSSebastian Andrzej Siewior static int configfs_do_nothing(struct usb_composite_dev *cdev) 119388af8bbeSSebastian Andrzej Siewior { 119475bfe23aSDavid Rientjes WARN_ON(1); 119588af8bbeSSebastian Andrzej Siewior return -EINVAL; 119688af8bbeSSebastian Andrzej Siewior } 119788af8bbeSSebastian Andrzej Siewior 119888af8bbeSSebastian Andrzej Siewior int composite_dev_prepare(struct usb_composite_driver *composite, 119988af8bbeSSebastian Andrzej Siewior struct usb_composite_dev *dev); 120088af8bbeSSebastian Andrzej Siewior 1201da424314SAndrzej Pietrasiewicz int composite_os_desc_req_prepare(struct usb_composite_dev *cdev, 1202da424314SAndrzej Pietrasiewicz struct usb_ep *ep0); 1203da424314SAndrzej Pietrasiewicz 120488af8bbeSSebastian Andrzej Siewior static void purge_configs_funcs(struct gadget_info *gi) 120588af8bbeSSebastian Andrzej Siewior { 120688af8bbeSSebastian Andrzej Siewior struct usb_configuration *c; 120788af8bbeSSebastian Andrzej Siewior 120888af8bbeSSebastian Andrzej Siewior list_for_each_entry(c, &gi->cdev.configs, list) { 120988af8bbeSSebastian Andrzej Siewior struct usb_function *f, *tmp; 121088af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg; 121188af8bbeSSebastian Andrzej Siewior 121288af8bbeSSebastian Andrzej Siewior cfg = container_of(c, struct config_usb_cfg, c); 121388af8bbeSSebastian Andrzej Siewior 121488af8bbeSSebastian Andrzej Siewior list_for_each_entry_safe(f, tmp, &c->functions, list) { 121588af8bbeSSebastian Andrzej Siewior 121688af8bbeSSebastian Andrzej Siewior list_move_tail(&f->list, &cfg->func_list); 121788af8bbeSSebastian Andrzej Siewior if (f->unbind) { 1218da7b895dSRomain Izard dev_dbg(&gi->cdev.gadget->dev, 1219a08f5dbfSRomain Izard "unbind function '%s'/%p\n", 1220a08f5dbfSRomain Izard f->name, f); 122188af8bbeSSebastian Andrzej Siewior f->unbind(c, f); 122288af8bbeSSebastian Andrzej Siewior } 122388af8bbeSSebastian Andrzej Siewior } 122488af8bbeSSebastian Andrzej Siewior c->next_interface_id = 0; 1225903124feSKrzysztof Opasiak memset(c->interface, 0, sizeof(c->interface)); 1226554eead5SJohn Youn c->superspeed_plus = 0; 122788af8bbeSSebastian Andrzej Siewior c->superspeed = 0; 122888af8bbeSSebastian Andrzej Siewior c->highspeed = 0; 122988af8bbeSSebastian Andrzej Siewior c->fullspeed = 0; 123088af8bbeSSebastian Andrzej Siewior } 123188af8bbeSSebastian Andrzej Siewior } 123288af8bbeSSebastian Andrzej Siewior 123388af8bbeSSebastian Andrzej Siewior static int configfs_composite_bind(struct usb_gadget *gadget, 123488af8bbeSSebastian Andrzej Siewior struct usb_gadget_driver *gdriver) 123588af8bbeSSebastian Andrzej Siewior { 123688af8bbeSSebastian Andrzej Siewior struct usb_composite_driver *composite = to_cdriver(gdriver); 123788af8bbeSSebastian Andrzej Siewior struct gadget_info *gi = container_of(composite, 123888af8bbeSSebastian Andrzej Siewior struct gadget_info, composite); 123988af8bbeSSebastian Andrzej Siewior struct usb_composite_dev *cdev = &gi->cdev; 124088af8bbeSSebastian Andrzej Siewior struct usb_configuration *c; 124188af8bbeSSebastian Andrzej Siewior struct usb_string *s; 124288af8bbeSSebastian Andrzej Siewior unsigned i; 124388af8bbeSSebastian Andrzej Siewior int ret; 124488af8bbeSSebastian Andrzej Siewior 124588af8bbeSSebastian Andrzej Siewior /* the gi->lock is hold by the caller */ 124688af8bbeSSebastian Andrzej Siewior cdev->gadget = gadget; 124788af8bbeSSebastian Andrzej Siewior set_gadget_data(gadget, cdev); 124888af8bbeSSebastian Andrzej Siewior ret = composite_dev_prepare(composite, cdev); 124988af8bbeSSebastian Andrzej Siewior if (ret) 125088af8bbeSSebastian Andrzej Siewior return ret; 125188af8bbeSSebastian Andrzej Siewior /* and now the gadget bind */ 125288af8bbeSSebastian Andrzej Siewior ret = -EINVAL; 125388af8bbeSSebastian Andrzej Siewior 125488af8bbeSSebastian Andrzej Siewior if (list_empty(&gi->cdev.configs)) { 125588af8bbeSSebastian Andrzej Siewior pr_err("Need at least one configuration in %s.\n", 125688af8bbeSSebastian Andrzej Siewior gi->composite.name); 125788af8bbeSSebastian Andrzej Siewior goto err_comp_cleanup; 125888af8bbeSSebastian Andrzej Siewior } 125988af8bbeSSebastian Andrzej Siewior 126088af8bbeSSebastian Andrzej Siewior 126188af8bbeSSebastian Andrzej Siewior list_for_each_entry(c, &gi->cdev.configs, list) { 126288af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg; 126388af8bbeSSebastian Andrzej Siewior 126488af8bbeSSebastian Andrzej Siewior cfg = container_of(c, struct config_usb_cfg, c); 126588af8bbeSSebastian Andrzej Siewior if (list_empty(&cfg->func_list)) { 126688af8bbeSSebastian Andrzej Siewior pr_err("Config %s/%d of %s needs at least one function.\n", 126788af8bbeSSebastian Andrzej Siewior c->label, c->bConfigurationValue, 126888af8bbeSSebastian Andrzej Siewior gi->composite.name); 126988af8bbeSSebastian Andrzej Siewior goto err_comp_cleanup; 127088af8bbeSSebastian Andrzej Siewior } 127188af8bbeSSebastian Andrzej Siewior } 127288af8bbeSSebastian Andrzej Siewior 127388af8bbeSSebastian Andrzej Siewior /* init all strings */ 127488af8bbeSSebastian Andrzej Siewior if (!list_empty(&gi->string_list)) { 127588af8bbeSSebastian Andrzej Siewior struct gadget_strings *gs; 127688af8bbeSSebastian Andrzej Siewior 127788af8bbeSSebastian Andrzej Siewior i = 0; 127888af8bbeSSebastian Andrzej Siewior list_for_each_entry(gs, &gi->string_list, list) { 127988af8bbeSSebastian Andrzej Siewior 128088af8bbeSSebastian Andrzej Siewior gi->gstrings[i] = &gs->stringtab_dev; 128188af8bbeSSebastian Andrzej Siewior gs->stringtab_dev.strings = gs->strings; 128288af8bbeSSebastian Andrzej Siewior gs->strings[USB_GADGET_MANUFACTURER_IDX].s = 128388af8bbeSSebastian Andrzej Siewior gs->manufacturer; 128488af8bbeSSebastian Andrzej Siewior gs->strings[USB_GADGET_PRODUCT_IDX].s = gs->product; 128588af8bbeSSebastian Andrzej Siewior gs->strings[USB_GADGET_SERIAL_IDX].s = gs->serialnumber; 128688af8bbeSSebastian Andrzej Siewior i++; 128788af8bbeSSebastian Andrzej Siewior } 128888af8bbeSSebastian Andrzej Siewior gi->gstrings[i] = NULL; 128988af8bbeSSebastian Andrzej Siewior s = usb_gstrings_attach(&gi->cdev, gi->gstrings, 129088af8bbeSSebastian Andrzej Siewior USB_GADGET_FIRST_AVAIL_IDX); 1291fea77077SWei Yongjun if (IS_ERR(s)) { 1292fea77077SWei Yongjun ret = PTR_ERR(s); 129388af8bbeSSebastian Andrzej Siewior goto err_comp_cleanup; 1294fea77077SWei Yongjun } 129588af8bbeSSebastian Andrzej Siewior 129688af8bbeSSebastian Andrzej Siewior gi->cdev.desc.iManufacturer = s[USB_GADGET_MANUFACTURER_IDX].id; 129788af8bbeSSebastian Andrzej Siewior gi->cdev.desc.iProduct = s[USB_GADGET_PRODUCT_IDX].id; 129888af8bbeSSebastian Andrzej Siewior gi->cdev.desc.iSerialNumber = s[USB_GADGET_SERIAL_IDX].id; 129988af8bbeSSebastian Andrzej Siewior } 130088af8bbeSSebastian Andrzej Siewior 130187213d38SAndrzej Pietrasiewicz if (gi->use_os_desc) { 130287213d38SAndrzej Pietrasiewicz cdev->use_os_string = true; 130387213d38SAndrzej Pietrasiewicz cdev->b_vendor_code = gi->b_vendor_code; 130487213d38SAndrzej Pietrasiewicz memcpy(cdev->qw_sign, gi->qw_sign, OS_STRING_QW_SIGN_LEN); 130587213d38SAndrzej Pietrasiewicz } 130687213d38SAndrzej Pietrasiewicz 130741ce84c8SLi Jun if (gadget_is_otg(gadget) && !otg_desc[0]) { 130841ce84c8SLi Jun struct usb_descriptor_header *usb_desc; 130941ce84c8SLi Jun 131041ce84c8SLi Jun usb_desc = usb_otg_descriptor_alloc(gadget); 131141ce84c8SLi Jun if (!usb_desc) { 131241ce84c8SLi Jun ret = -ENOMEM; 131341ce84c8SLi Jun goto err_comp_cleanup; 131441ce84c8SLi Jun } 131541ce84c8SLi Jun usb_otg_descriptor_init(gadget, usb_desc); 131641ce84c8SLi Jun otg_desc[0] = usb_desc; 131741ce84c8SLi Jun otg_desc[1] = NULL; 131841ce84c8SLi Jun } 131941ce84c8SLi Jun 132088af8bbeSSebastian Andrzej Siewior /* Go through all configs, attach all functions */ 132188af8bbeSSebastian Andrzej Siewior list_for_each_entry(c, &gi->cdev.configs, list) { 132288af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg; 132388af8bbeSSebastian Andrzej Siewior struct usb_function *f; 132488af8bbeSSebastian Andrzej Siewior struct usb_function *tmp; 132588af8bbeSSebastian Andrzej Siewior struct gadget_config_name *cn; 132688af8bbeSSebastian Andrzej Siewior 132741ce84c8SLi Jun if (gadget_is_otg(gadget)) 132841ce84c8SLi Jun c->descriptors = otg_desc; 132941ce84c8SLi Jun 133088af8bbeSSebastian Andrzej Siewior cfg = container_of(c, struct config_usb_cfg, c); 133188af8bbeSSebastian Andrzej Siewior if (!list_empty(&cfg->string_list)) { 133288af8bbeSSebastian Andrzej Siewior i = 0; 133388af8bbeSSebastian Andrzej Siewior list_for_each_entry(cn, &cfg->string_list, list) { 133488af8bbeSSebastian Andrzej Siewior cfg->gstrings[i] = &cn->stringtab_dev; 133588af8bbeSSebastian Andrzej Siewior cn->stringtab_dev.strings = &cn->strings; 133688af8bbeSSebastian Andrzej Siewior cn->strings.s = cn->configuration; 133788af8bbeSSebastian Andrzej Siewior i++; 133888af8bbeSSebastian Andrzej Siewior } 133988af8bbeSSebastian Andrzej Siewior cfg->gstrings[i] = NULL; 134088af8bbeSSebastian Andrzej Siewior s = usb_gstrings_attach(&gi->cdev, cfg->gstrings, 1); 1341fea77077SWei Yongjun if (IS_ERR(s)) { 1342fea77077SWei Yongjun ret = PTR_ERR(s); 134388af8bbeSSebastian Andrzej Siewior goto err_comp_cleanup; 1344fea77077SWei Yongjun } 134588af8bbeSSebastian Andrzej Siewior c->iConfiguration = s[0].id; 134688af8bbeSSebastian Andrzej Siewior } 134788af8bbeSSebastian Andrzej Siewior 134888af8bbeSSebastian Andrzej Siewior list_for_each_entry_safe(f, tmp, &cfg->func_list, list) { 134988af8bbeSSebastian Andrzej Siewior list_del(&f->list); 135088af8bbeSSebastian Andrzej Siewior ret = usb_add_function(c, f); 13515a68e9b5SAndrzej Pietrasiewicz if (ret) { 13525a68e9b5SAndrzej Pietrasiewicz list_add(&f->list, &cfg->func_list); 135388af8bbeSSebastian Andrzej Siewior goto err_purge_funcs; 135488af8bbeSSebastian Andrzej Siewior } 13555a68e9b5SAndrzej Pietrasiewicz } 135688af8bbeSSebastian Andrzej Siewior usb_ep_autoconfig_reset(cdev->gadget); 135788af8bbeSSebastian Andrzej Siewior } 1358da424314SAndrzej Pietrasiewicz if (cdev->use_os_string) { 1359da424314SAndrzej Pietrasiewicz ret = composite_os_desc_req_prepare(cdev, gadget->ep0); 1360da424314SAndrzej Pietrasiewicz if (ret) 1361da424314SAndrzej Pietrasiewicz goto err_purge_funcs; 1362da424314SAndrzej Pietrasiewicz } 1363da424314SAndrzej Pietrasiewicz 136488af8bbeSSebastian Andrzej Siewior usb_ep_autoconfig_reset(cdev->gadget); 136588af8bbeSSebastian Andrzej Siewior return 0; 136688af8bbeSSebastian Andrzej Siewior 136788af8bbeSSebastian Andrzej Siewior err_purge_funcs: 136888af8bbeSSebastian Andrzej Siewior purge_configs_funcs(gi); 136988af8bbeSSebastian Andrzej Siewior err_comp_cleanup: 137088af8bbeSSebastian Andrzej Siewior composite_dev_cleanup(cdev); 137188af8bbeSSebastian Andrzej Siewior return ret; 137288af8bbeSSebastian Andrzej Siewior } 137388af8bbeSSebastian Andrzej Siewior 137488af8bbeSSebastian Andrzej Siewior static void configfs_composite_unbind(struct usb_gadget *gadget) 137588af8bbeSSebastian Andrzej Siewior { 137688af8bbeSSebastian Andrzej Siewior struct usb_composite_dev *cdev; 137788af8bbeSSebastian Andrzej Siewior struct gadget_info *gi; 137888af8bbeSSebastian Andrzej Siewior 137988af8bbeSSebastian Andrzej Siewior /* the gi->lock is hold by the caller */ 138088af8bbeSSebastian Andrzej Siewior 138188af8bbeSSebastian Andrzej Siewior cdev = get_gadget_data(gadget); 138288af8bbeSSebastian Andrzej Siewior gi = container_of(cdev, struct gadget_info, cdev); 138388af8bbeSSebastian Andrzej Siewior 138441ce84c8SLi Jun kfree(otg_desc[0]); 138541ce84c8SLi Jun otg_desc[0] = NULL; 138688af8bbeSSebastian Andrzej Siewior purge_configs_funcs(gi); 138788af8bbeSSebastian Andrzej Siewior composite_dev_cleanup(cdev); 138888af8bbeSSebastian Andrzej Siewior usb_ep_autoconfig_reset(cdev->gadget); 138988af8bbeSSebastian Andrzej Siewior cdev->gadget = NULL; 139088af8bbeSSebastian Andrzej Siewior set_gadget_data(gadget, NULL); 139188af8bbeSSebastian Andrzej Siewior } 139288af8bbeSSebastian Andrzej Siewior 139388af8bbeSSebastian Andrzej Siewior static const struct usb_gadget_driver configfs_driver_template = { 139488af8bbeSSebastian Andrzej Siewior .bind = configfs_composite_bind, 139588af8bbeSSebastian Andrzej Siewior .unbind = configfs_composite_unbind, 139688af8bbeSSebastian Andrzej Siewior 139788af8bbeSSebastian Andrzej Siewior .setup = composite_setup, 139802f751b4SPeter Chen .reset = composite_disconnect, 139988af8bbeSSebastian Andrzej Siewior .disconnect = composite_disconnect, 140088af8bbeSSebastian Andrzej Siewior 14013a571870SAndrzej Pietrasiewicz .suspend = composite_suspend, 14023a571870SAndrzej Pietrasiewicz .resume = composite_resume, 14033a571870SAndrzej Pietrasiewicz 140488af8bbeSSebastian Andrzej Siewior .max_speed = USB_SPEED_SUPER, 140588af8bbeSSebastian Andrzej Siewior .driver = { 140688af8bbeSSebastian Andrzej Siewior .owner = THIS_MODULE, 140788af8bbeSSebastian Andrzej Siewior .name = "configfs-gadget", 140888af8bbeSSebastian Andrzej Siewior }, 1409f1bddbb3SKrzysztof Opasiak .match_existing_only = 1, 141088af8bbeSSebastian Andrzej Siewior }; 141188af8bbeSSebastian Andrzej Siewior 141288af8bbeSSebastian Andrzej Siewior static struct config_group *gadgets_make( 141388af8bbeSSebastian Andrzej Siewior struct config_group *group, 141488af8bbeSSebastian Andrzej Siewior const char *name) 141588af8bbeSSebastian Andrzej Siewior { 141688af8bbeSSebastian Andrzej Siewior struct gadget_info *gi; 141788af8bbeSSebastian Andrzej Siewior 141888af8bbeSSebastian Andrzej Siewior gi = kzalloc(sizeof(*gi), GFP_KERNEL); 141988af8bbeSSebastian Andrzej Siewior if (!gi) 142088af8bbeSSebastian Andrzej Siewior return ERR_PTR(-ENOMEM); 142188af8bbeSSebastian Andrzej Siewior 14221ae1602dSChristoph Hellwig config_group_init_type_name(&gi->group, name, &gadget_root_type); 142388af8bbeSSebastian Andrzej Siewior 142488af8bbeSSebastian Andrzej Siewior config_group_init_type_name(&gi->functions_group, "functions", 142588af8bbeSSebastian Andrzej Siewior &functions_type); 14261ae1602dSChristoph Hellwig configfs_add_default_group(&gi->functions_group, &gi->group); 14271ae1602dSChristoph Hellwig 142888af8bbeSSebastian Andrzej Siewior config_group_init_type_name(&gi->configs_group, "configs", 142988af8bbeSSebastian Andrzej Siewior &config_desc_type); 14301ae1602dSChristoph Hellwig configfs_add_default_group(&gi->configs_group, &gi->group); 14311ae1602dSChristoph Hellwig 143288af8bbeSSebastian Andrzej Siewior config_group_init_type_name(&gi->strings_group, "strings", 143388af8bbeSSebastian Andrzej Siewior &gadget_strings_strings_type); 14341ae1602dSChristoph Hellwig configfs_add_default_group(&gi->strings_group, &gi->group); 14351ae1602dSChristoph Hellwig 143687213d38SAndrzej Pietrasiewicz config_group_init_type_name(&gi->os_desc_group, "os_desc", 143787213d38SAndrzej Pietrasiewicz &os_desc_type); 14381ae1602dSChristoph Hellwig configfs_add_default_group(&gi->os_desc_group, &gi->group); 143988af8bbeSSebastian Andrzej Siewior 144088af8bbeSSebastian Andrzej Siewior gi->composite.bind = configfs_do_nothing; 144188af8bbeSSebastian Andrzej Siewior gi->composite.unbind = configfs_do_nothing; 144288af8bbeSSebastian Andrzej Siewior gi->composite.suspend = NULL; 144388af8bbeSSebastian Andrzej Siewior gi->composite.resume = NULL; 144488af8bbeSSebastian Andrzej Siewior gi->composite.max_speed = USB_SPEED_SUPER; 144588af8bbeSSebastian Andrzej Siewior 144688af8bbeSSebastian Andrzej Siewior mutex_init(&gi->lock); 144788af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&gi->string_list); 144888af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&gi->available_func); 144988af8bbeSSebastian Andrzej Siewior 145088af8bbeSSebastian Andrzej Siewior composite_init_dev(&gi->cdev); 145188af8bbeSSebastian Andrzej Siewior gi->cdev.desc.bLength = USB_DT_DEVICE_SIZE; 145288af8bbeSSebastian Andrzej Siewior gi->cdev.desc.bDescriptorType = USB_DT_DEVICE; 145388af8bbeSSebastian Andrzej Siewior gi->cdev.desc.bcdDevice = cpu_to_le16(get_default_bcdDevice()); 145488af8bbeSSebastian Andrzej Siewior 145588af8bbeSSebastian Andrzej Siewior gi->composite.gadget_driver = configfs_driver_template; 145688af8bbeSSebastian Andrzej Siewior 145788af8bbeSSebastian Andrzej Siewior gi->composite.gadget_driver.function = kstrdup(name, GFP_KERNEL); 145888af8bbeSSebastian Andrzej Siewior gi->composite.name = gi->composite.gadget_driver.function; 145988af8bbeSSebastian Andrzej Siewior 146088af8bbeSSebastian Andrzej Siewior if (!gi->composite.gadget_driver.function) 146188af8bbeSSebastian Andrzej Siewior goto err; 146288af8bbeSSebastian Andrzej Siewior 146388af8bbeSSebastian Andrzej Siewior return &gi->group; 146488af8bbeSSebastian Andrzej Siewior err: 146588af8bbeSSebastian Andrzej Siewior kfree(gi); 146688af8bbeSSebastian Andrzej Siewior return ERR_PTR(-ENOMEM); 146788af8bbeSSebastian Andrzej Siewior } 146888af8bbeSSebastian Andrzej Siewior 146988af8bbeSSebastian Andrzej Siewior static void gadgets_drop(struct config_group *group, struct config_item *item) 147088af8bbeSSebastian Andrzej Siewior { 147188af8bbeSSebastian Andrzej Siewior config_item_put(item); 147288af8bbeSSebastian Andrzej Siewior } 147388af8bbeSSebastian Andrzej Siewior 147488af8bbeSSebastian Andrzej Siewior static struct configfs_group_operations gadgets_ops = { 147588af8bbeSSebastian Andrzej Siewior .make_group = &gadgets_make, 147688af8bbeSSebastian Andrzej Siewior .drop_item = &gadgets_drop, 147788af8bbeSSebastian Andrzej Siewior }; 147888af8bbeSSebastian Andrzej Siewior 1479*4ad01412SBhumika Goyal static const struct config_item_type gadgets_type = { 148088af8bbeSSebastian Andrzej Siewior .ct_group_ops = &gadgets_ops, 148188af8bbeSSebastian Andrzej Siewior .ct_owner = THIS_MODULE, 148288af8bbeSSebastian Andrzej Siewior }; 148388af8bbeSSebastian Andrzej Siewior 148488af8bbeSSebastian Andrzej Siewior static struct configfs_subsystem gadget_subsys = { 148588af8bbeSSebastian Andrzej Siewior .su_group = { 148688af8bbeSSebastian Andrzej Siewior .cg_item = { 148788af8bbeSSebastian Andrzej Siewior .ci_namebuf = "usb_gadget", 148888af8bbeSSebastian Andrzej Siewior .ci_type = &gadgets_type, 148988af8bbeSSebastian Andrzej Siewior }, 149088af8bbeSSebastian Andrzej Siewior }, 149188af8bbeSSebastian Andrzej Siewior .su_mutex = __MUTEX_INITIALIZER(gadget_subsys.su_mutex), 149288af8bbeSSebastian Andrzej Siewior }; 149388af8bbeSSebastian Andrzej Siewior 1494092a4bd0SAndrzej Pietrasiewicz void unregister_gadget_item(struct config_item *item) 1495092a4bd0SAndrzej Pietrasiewicz { 1496092a4bd0SAndrzej Pietrasiewicz struct gadget_info *gi = to_gadget_info(item); 1497092a4bd0SAndrzej Pietrasiewicz 1498cee51c33SWinter Wang mutex_lock(&gi->lock); 1499092a4bd0SAndrzej Pietrasiewicz unregister_gadget(gi); 1500cee51c33SWinter Wang mutex_unlock(&gi->lock); 1501092a4bd0SAndrzej Pietrasiewicz } 15020700faafSFelipe Balbi EXPORT_SYMBOL_GPL(unregister_gadget_item); 1503092a4bd0SAndrzej Pietrasiewicz 150488af8bbeSSebastian Andrzej Siewior static int __init gadget_cfs_init(void) 150588af8bbeSSebastian Andrzej Siewior { 150688af8bbeSSebastian Andrzej Siewior int ret; 150788af8bbeSSebastian Andrzej Siewior 150888af8bbeSSebastian Andrzej Siewior config_group_init(&gadget_subsys.su_group); 150988af8bbeSSebastian Andrzej Siewior 151088af8bbeSSebastian Andrzej Siewior ret = configfs_register_subsystem(&gadget_subsys); 151188af8bbeSSebastian Andrzej Siewior return ret; 151288af8bbeSSebastian Andrzej Siewior } 151388af8bbeSSebastian Andrzej Siewior module_init(gadget_cfs_init); 151488af8bbeSSebastian Andrzej Siewior 151588af8bbeSSebastian Andrzej Siewior static void __exit gadget_cfs_exit(void) 151688af8bbeSSebastian Andrzej Siewior { 151788af8bbeSSebastian Andrzej Siewior configfs_unregister_subsystem(&gadget_subsys); 151888af8bbeSSebastian Andrzej Siewior } 151988af8bbeSSebastian Andrzej Siewior module_exit(gadget_cfs_exit); 1520