15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 288af8bbeSSebastian Andrzej Siewior #include <linux/configfs.h> 388af8bbeSSebastian Andrzej Siewior #include <linux/module.h> 488af8bbeSSebastian Andrzej Siewior #include <linux/slab.h> 588af8bbeSSebastian Andrzej Siewior #include <linux/device.h> 6a8bc8cc1SChristophe JAILLET #include <linux/kstrtox.h> 787213d38SAndrzej Pietrasiewicz #include <linux/nls.h> 888af8bbeSSebastian Andrzej Siewior #include <linux/usb/composite.h> 988af8bbeSSebastian Andrzej Siewior #include <linux/usb/gadget_configfs.h> 1093c47394SJó Ágila Bitsch #include <linux/usb/webusb.h> 110009e99aSRashika Kheria #include "configfs.h" 12da424314SAndrzej Pietrasiewicz #include "u_f.h" 137419485fSAndrzej Pietrasiewicz #include "u_os_desc.h" 1488af8bbeSSebastian Andrzej Siewior 1588af8bbeSSebastian Andrzej Siewior int check_user_usb_string(const char *name, 1688af8bbeSSebastian Andrzej Siewior struct usb_gadget_strings *stringtab_dev) 1788af8bbeSSebastian Andrzej Siewior { 1888af8bbeSSebastian Andrzej Siewior u16 num; 1988af8bbeSSebastian Andrzej Siewior int ret; 2088af8bbeSSebastian Andrzej Siewior 2188af8bbeSSebastian Andrzej Siewior ret = kstrtou16(name, 0, &num); 2288af8bbeSSebastian Andrzej Siewior if (ret) 2388af8bbeSSebastian Andrzej Siewior return ret; 2488af8bbeSSebastian Andrzej Siewior 2517309a6aSTao Ren if (!usb_validate_langid(num)) 2688af8bbeSSebastian Andrzej Siewior return -EINVAL; 2788af8bbeSSebastian Andrzej Siewior 2888af8bbeSSebastian Andrzej Siewior stringtab_dev->language = num; 2988af8bbeSSebastian Andrzej Siewior return 0; 3088af8bbeSSebastian Andrzej Siewior } 3188af8bbeSSebastian Andrzej Siewior 3288af8bbeSSebastian Andrzej Siewior #define MAX_NAME_LEN 40 3388af8bbeSSebastian Andrzej Siewior #define MAX_USB_STRING_LANGS 2 3488af8bbeSSebastian Andrzej Siewior 3541ce84c8SLi Jun static const struct usb_descriptor_header *otg_desc[2]; 3641ce84c8SLi Jun 3788af8bbeSSebastian Andrzej Siewior struct gadget_info { 3888af8bbeSSebastian Andrzej Siewior struct config_group group; 3988af8bbeSSebastian Andrzej Siewior struct config_group functions_group; 4088af8bbeSSebastian Andrzej Siewior struct config_group configs_group; 4188af8bbeSSebastian Andrzej Siewior struct config_group strings_group; 4287213d38SAndrzej Pietrasiewicz struct config_group os_desc_group; 4393c47394SJó Ágila Bitsch struct config_group webusb_group; 4488af8bbeSSebastian Andrzej Siewior 4588af8bbeSSebastian Andrzej Siewior struct mutex lock; 4688af8bbeSSebastian Andrzej Siewior struct usb_gadget_strings *gstrings[MAX_USB_STRING_LANGS + 1]; 4788af8bbeSSebastian Andrzej Siewior struct list_head string_list; 4888af8bbeSSebastian Andrzej Siewior struct list_head available_func; 4988af8bbeSSebastian Andrzej Siewior 5088af8bbeSSebastian Andrzej Siewior struct usb_composite_driver composite; 5188af8bbeSSebastian Andrzej Siewior struct usb_composite_dev cdev; 5287213d38SAndrzej Pietrasiewicz bool use_os_desc; 5387213d38SAndrzej Pietrasiewicz char b_vendor_code; 5487213d38SAndrzej Pietrasiewicz char qw_sign[OS_STRING_QW_SIGN_LEN]; 5593c47394SJó Ágila Bitsch bool use_webusb; 5693c47394SJó Ágila Bitsch u16 bcd_webusb_version; 5793c47394SJó Ágila Bitsch u8 b_webusb_vendor_code; 5893c47394SJó Ágila Bitsch char landing_page[WEBUSB_URL_RAW_MAX_LENGTH]; 5993c47394SJó Ágila Bitsch 601a1c851bSPeter Chen spinlock_t spinlock; 611a1c851bSPeter Chen bool unbind; 6288af8bbeSSebastian Andrzej Siewior }; 6388af8bbeSSebastian Andrzej Siewior 6445b6a73fSChristoph Hellwig static inline struct gadget_info *to_gadget_info(struct config_item *item) 6545b6a73fSChristoph Hellwig { 6645b6a73fSChristoph Hellwig return container_of(to_config_group(item), struct gadget_info, group); 6745b6a73fSChristoph Hellwig } 6845b6a73fSChristoph Hellwig 6988af8bbeSSebastian Andrzej Siewior struct config_usb_cfg { 7088af8bbeSSebastian Andrzej Siewior struct config_group group; 7188af8bbeSSebastian Andrzej Siewior struct config_group strings_group; 7288af8bbeSSebastian Andrzej Siewior struct list_head string_list; 7388af8bbeSSebastian Andrzej Siewior struct usb_configuration c; 7488af8bbeSSebastian Andrzej Siewior struct list_head func_list; 7588af8bbeSSebastian Andrzej Siewior struct usb_gadget_strings *gstrings[MAX_USB_STRING_LANGS + 1]; 7688af8bbeSSebastian Andrzej Siewior }; 7788af8bbeSSebastian Andrzej Siewior 7845b6a73fSChristoph Hellwig static inline struct config_usb_cfg *to_config_usb_cfg(struct config_item *item) 7945b6a73fSChristoph Hellwig { 8045b6a73fSChristoph Hellwig return container_of(to_config_group(item), struct config_usb_cfg, 8145b6a73fSChristoph Hellwig group); 8245b6a73fSChristoph Hellwig } 8345b6a73fSChristoph Hellwig 84260d88b7SLinyu Yuan static inline struct gadget_info *cfg_to_gadget_info(struct config_usb_cfg *cfg) 85260d88b7SLinyu Yuan { 86260d88b7SLinyu Yuan return container_of(cfg->c.cdev, struct gadget_info, cdev); 87260d88b7SLinyu Yuan } 88260d88b7SLinyu Yuan 896e2a512dSDaniel Scally struct gadget_language { 9088af8bbeSSebastian Andrzej Siewior struct usb_gadget_strings stringtab_dev; 9188af8bbeSSebastian Andrzej Siewior struct usb_string strings[USB_GADGET_FIRST_AVAIL_IDX]; 9288af8bbeSSebastian Andrzej Siewior char *manufacturer; 9388af8bbeSSebastian Andrzej Siewior char *product; 9488af8bbeSSebastian Andrzej Siewior char *serialnumber; 9588af8bbeSSebastian Andrzej Siewior 9688af8bbeSSebastian Andrzej Siewior struct config_group group; 9788af8bbeSSebastian Andrzej Siewior struct list_head list; 9815a7cf8cSDaniel Scally struct list_head gadget_strings; 9915a7cf8cSDaniel Scally unsigned int nstrings; 10088af8bbeSSebastian Andrzej Siewior }; 10188af8bbeSSebastian Andrzej Siewior 10288af8bbeSSebastian Andrzej Siewior struct gadget_config_name { 10388af8bbeSSebastian Andrzej Siewior struct usb_gadget_strings stringtab_dev; 10488af8bbeSSebastian Andrzej Siewior struct usb_string strings; 10588af8bbeSSebastian Andrzej Siewior char *configuration; 10688af8bbeSSebastian Andrzej Siewior 10788af8bbeSSebastian Andrzej Siewior struct config_group group; 10888af8bbeSSebastian Andrzej Siewior struct list_head list; 10988af8bbeSSebastian Andrzej Siewior }; 11088af8bbeSSebastian Andrzej Siewior 11198f153a1SJim Lin #define USB_MAX_STRING_WITH_NULL_LEN (USB_MAX_STRING_LEN+1) 11298f153a1SJim Lin 11388af8bbeSSebastian Andrzej Siewior static int usb_string_copy(const char *s, char **s_copy) 11488af8bbeSSebastian Andrzej Siewior { 11588af8bbeSSebastian Andrzej Siewior int ret; 11688af8bbeSSebastian Andrzej Siewior char *str; 11788af8bbeSSebastian Andrzej Siewior char *copy = *s_copy; 11888af8bbeSSebastian Andrzej Siewior ret = strlen(s); 11981c74628SMacpaul Lin if (ret > USB_MAX_STRING_LEN) 12088af8bbeSSebastian Andrzej Siewior return -EOVERFLOW; 12188af8bbeSSebastian Andrzej Siewior 12298f153a1SJim Lin if (copy) { 12398f153a1SJim Lin str = copy; 12498f153a1SJim Lin } else { 12598f153a1SJim Lin str = kmalloc(USB_MAX_STRING_WITH_NULL_LEN, GFP_KERNEL); 12688af8bbeSSebastian Andrzej Siewior if (!str) 12788af8bbeSSebastian Andrzej Siewior return -ENOMEM; 12898f153a1SJim Lin } 12998f153a1SJim Lin strcpy(str, s); 13088af8bbeSSebastian Andrzej Siewior if (str[ret - 1] == '\n') 13188af8bbeSSebastian Andrzej Siewior str[ret - 1] = '\0'; 13288af8bbeSSebastian Andrzej Siewior *s_copy = str; 13388af8bbeSSebastian Andrzej Siewior return 0; 13488af8bbeSSebastian Andrzej Siewior } 13588af8bbeSSebastian Andrzej Siewior 13688af8bbeSSebastian Andrzej Siewior #define GI_DEVICE_DESC_SIMPLE_R_u8(__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%02x\n", \ 14145b6a73fSChristoph Hellwig to_gadget_info(item)->cdev.desc.__name); \ 14288af8bbeSSebastian Andrzej Siewior } 14388af8bbeSSebastian Andrzej Siewior 14488af8bbeSSebastian Andrzej Siewior #define GI_DEVICE_DESC_SIMPLE_R_u16(__name) \ 14545b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_##__name##_show(struct config_item *item, \ 14688af8bbeSSebastian Andrzej Siewior char *page) \ 14788af8bbeSSebastian Andrzej Siewior { \ 14845b6a73fSChristoph Hellwig return sprintf(page, "0x%04x\n", \ 14945b6a73fSChristoph Hellwig le16_to_cpup(&to_gadget_info(item)->cdev.desc.__name)); \ 15088af8bbeSSebastian Andrzej Siewior } 15188af8bbeSSebastian Andrzej Siewior 15288af8bbeSSebastian Andrzej Siewior 15388af8bbeSSebastian Andrzej Siewior #define GI_DEVICE_DESC_SIMPLE_W_u8(_name) \ 15445b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_##_name##_store(struct config_item *item, \ 15588af8bbeSSebastian Andrzej Siewior const char *page, size_t len) \ 15688af8bbeSSebastian Andrzej Siewior { \ 15788af8bbeSSebastian Andrzej Siewior u8 val; \ 15888af8bbeSSebastian Andrzej Siewior int ret; \ 15988af8bbeSSebastian Andrzej Siewior ret = kstrtou8(page, 0, &val); \ 16088af8bbeSSebastian Andrzej Siewior if (ret) \ 16188af8bbeSSebastian Andrzej Siewior return ret; \ 16245b6a73fSChristoph Hellwig to_gadget_info(item)->cdev.desc._name = val; \ 16388af8bbeSSebastian Andrzej Siewior return len; \ 16488af8bbeSSebastian Andrzej Siewior } 16588af8bbeSSebastian Andrzej Siewior 16688af8bbeSSebastian Andrzej Siewior #define GI_DEVICE_DESC_SIMPLE_W_u16(_name) \ 16745b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_##_name##_store(struct config_item *item, \ 16888af8bbeSSebastian Andrzej Siewior const char *page, size_t len) \ 16988af8bbeSSebastian Andrzej Siewior { \ 17088af8bbeSSebastian Andrzej Siewior u16 val; \ 17188af8bbeSSebastian Andrzej Siewior int ret; \ 17288af8bbeSSebastian Andrzej Siewior ret = kstrtou16(page, 0, &val); \ 17388af8bbeSSebastian Andrzej Siewior if (ret) \ 17488af8bbeSSebastian Andrzej Siewior return ret; \ 17545b6a73fSChristoph Hellwig to_gadget_info(item)->cdev.desc._name = cpu_to_le16p(&val); \ 17688af8bbeSSebastian Andrzej Siewior return len; \ 17788af8bbeSSebastian Andrzej Siewior } 17888af8bbeSSebastian Andrzej Siewior 17988af8bbeSSebastian Andrzej Siewior #define GI_DEVICE_DESC_SIMPLE_RW(_name, _type) \ 18088af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_R_##_type(_name) \ 18188af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_W_##_type(_name) 18288af8bbeSSebastian Andrzej Siewior 18388af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_R_u16(bcdUSB); 18488af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(bDeviceClass, u8); 18588af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(bDeviceSubClass, u8); 18688af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(bDeviceProtocol, u8); 18788af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(bMaxPacketSize0, u8); 18888af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(idVendor, u16); 18988af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(idProduct, u16); 19088af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_R_u16(bcdDevice); 19188af8bbeSSebastian Andrzej Siewior 19288af8bbeSSebastian Andrzej Siewior static ssize_t is_valid_bcd(u16 bcd_val) 19388af8bbeSSebastian Andrzej Siewior { 19488af8bbeSSebastian Andrzej Siewior if ((bcd_val & 0xf) > 9) 19588af8bbeSSebastian Andrzej Siewior return -EINVAL; 19688af8bbeSSebastian Andrzej Siewior if (((bcd_val >> 4) & 0xf) > 9) 19788af8bbeSSebastian Andrzej Siewior return -EINVAL; 19888af8bbeSSebastian Andrzej Siewior if (((bcd_val >> 8) & 0xf) > 9) 19988af8bbeSSebastian Andrzej Siewior return -EINVAL; 20088af8bbeSSebastian Andrzej Siewior if (((bcd_val >> 12) & 0xf) > 9) 20188af8bbeSSebastian Andrzej Siewior return -EINVAL; 20288af8bbeSSebastian Andrzej Siewior return 0; 20388af8bbeSSebastian Andrzej Siewior } 20488af8bbeSSebastian Andrzej Siewior 20545b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_bcdDevice_store(struct config_item *item, 20688af8bbeSSebastian Andrzej Siewior const char *page, size_t len) 20788af8bbeSSebastian Andrzej Siewior { 20888af8bbeSSebastian Andrzej Siewior u16 bcdDevice; 20988af8bbeSSebastian Andrzej Siewior int ret; 21088af8bbeSSebastian Andrzej Siewior 21188af8bbeSSebastian Andrzej Siewior ret = kstrtou16(page, 0, &bcdDevice); 21288af8bbeSSebastian Andrzej Siewior if (ret) 21388af8bbeSSebastian Andrzej Siewior return ret; 21488af8bbeSSebastian Andrzej Siewior ret = is_valid_bcd(bcdDevice); 21588af8bbeSSebastian Andrzej Siewior if (ret) 21688af8bbeSSebastian Andrzej Siewior return ret; 21788af8bbeSSebastian Andrzej Siewior 21845b6a73fSChristoph Hellwig to_gadget_info(item)->cdev.desc.bcdDevice = cpu_to_le16(bcdDevice); 21988af8bbeSSebastian Andrzej Siewior return len; 22088af8bbeSSebastian Andrzej Siewior } 22188af8bbeSSebastian Andrzej Siewior 22245b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_bcdUSB_store(struct config_item *item, 22388af8bbeSSebastian Andrzej Siewior const char *page, size_t len) 22488af8bbeSSebastian Andrzej Siewior { 22588af8bbeSSebastian Andrzej Siewior u16 bcdUSB; 22688af8bbeSSebastian Andrzej Siewior int ret; 22788af8bbeSSebastian Andrzej Siewior 22888af8bbeSSebastian Andrzej Siewior ret = kstrtou16(page, 0, &bcdUSB); 22988af8bbeSSebastian Andrzej Siewior if (ret) 23088af8bbeSSebastian Andrzej Siewior return ret; 23188af8bbeSSebastian Andrzej Siewior ret = is_valid_bcd(bcdUSB); 23288af8bbeSSebastian Andrzej Siewior if (ret) 23388af8bbeSSebastian Andrzej Siewior return ret; 23488af8bbeSSebastian Andrzej Siewior 23545b6a73fSChristoph Hellwig to_gadget_info(item)->cdev.desc.bcdUSB = cpu_to_le16(bcdUSB); 23688af8bbeSSebastian Andrzej Siewior return len; 23788af8bbeSSebastian Andrzej Siewior } 23888af8bbeSSebastian Andrzej Siewior 23945b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_UDC_show(struct config_item *item, char *page) 24088af8bbeSSebastian Andrzej Siewior { 24164e6bbffSEddie Hung struct gadget_info *gi = to_gadget_info(item); 24264e6bbffSEddie Hung char *udc_name; 24364e6bbffSEddie Hung int ret; 244afdaadc3SRuslan Bilovol 24564e6bbffSEddie Hung mutex_lock(&gi->lock); 24664e6bbffSEddie Hung udc_name = gi->composite.gadget_driver.udc_name; 24764e6bbffSEddie Hung ret = sprintf(page, "%s\n", udc_name ?: ""); 24864e6bbffSEddie Hung mutex_unlock(&gi->lock); 24964e6bbffSEddie Hung 25064e6bbffSEddie Hung return ret; 25188af8bbeSSebastian Andrzej Siewior } 25288af8bbeSSebastian Andrzej Siewior 25388af8bbeSSebastian Andrzej Siewior static int unregister_gadget(struct gadget_info *gi) 25488af8bbeSSebastian Andrzej Siewior { 25588af8bbeSSebastian Andrzej Siewior int ret; 25688af8bbeSSebastian Andrzej Siewior 257afdaadc3SRuslan Bilovol if (!gi->composite.gadget_driver.udc_name) 25888af8bbeSSebastian Andrzej Siewior return -ENODEV; 25988af8bbeSSebastian Andrzej Siewior 26088af8bbeSSebastian Andrzej Siewior ret = usb_gadget_unregister_driver(&gi->composite.gadget_driver); 26188af8bbeSSebastian Andrzej Siewior if (ret) 26288af8bbeSSebastian Andrzej Siewior return ret; 263afdaadc3SRuslan Bilovol kfree(gi->composite.gadget_driver.udc_name); 264afdaadc3SRuslan Bilovol gi->composite.gadget_driver.udc_name = NULL; 26588af8bbeSSebastian Andrzej Siewior return 0; 26688af8bbeSSebastian Andrzej Siewior } 26788af8bbeSSebastian Andrzej Siewior 26845b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_UDC_store(struct config_item *item, 26988af8bbeSSebastian Andrzej Siewior const char *page, size_t len) 27088af8bbeSSebastian Andrzej Siewior { 27145b6a73fSChristoph Hellwig struct gadget_info *gi = to_gadget_info(item); 27288af8bbeSSebastian Andrzej Siewior char *name; 27388af8bbeSSebastian Andrzej Siewior int ret; 27488af8bbeSSebastian Andrzej Siewior 27515753588SKyungtae Kim if (strlen(page) < len) 27615753588SKyungtae Kim return -EOVERFLOW; 27715753588SKyungtae Kim 27888af8bbeSSebastian Andrzej Siewior name = kstrdup(page, GFP_KERNEL); 27988af8bbeSSebastian Andrzej Siewior if (!name) 28088af8bbeSSebastian Andrzej Siewior return -ENOMEM; 28188af8bbeSSebastian Andrzej Siewior if (name[len - 1] == '\n') 28288af8bbeSSebastian Andrzej Siewior name[len - 1] = '\0'; 28388af8bbeSSebastian Andrzej Siewior 28488af8bbeSSebastian Andrzej Siewior mutex_lock(&gi->lock); 28588af8bbeSSebastian Andrzej Siewior 28688af8bbeSSebastian Andrzej Siewior if (!strlen(name)) { 28788af8bbeSSebastian Andrzej Siewior ret = unregister_gadget(gi); 28888af8bbeSSebastian Andrzej Siewior if (ret) 28988af8bbeSSebastian Andrzej Siewior goto err; 29038355b2aSJohn Keeping kfree(name); 29188af8bbeSSebastian Andrzej Siewior } else { 292afdaadc3SRuslan Bilovol if (gi->composite.gadget_driver.udc_name) { 29388af8bbeSSebastian Andrzej Siewior ret = -EBUSY; 29488af8bbeSSebastian Andrzej Siewior goto err; 29588af8bbeSSebastian Andrzej Siewior } 296afdaadc3SRuslan Bilovol gi->composite.gadget_driver.udc_name = name; 297af1969a2SAlan Stern ret = usb_gadget_register_driver(&gi->composite.gadget_driver); 298afdaadc3SRuslan Bilovol if (ret) { 299afdaadc3SRuslan Bilovol gi->composite.gadget_driver.udc_name = NULL; 30088af8bbeSSebastian Andrzej Siewior goto err; 301afdaadc3SRuslan Bilovol } 30288af8bbeSSebastian Andrzej Siewior } 30388af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 30488af8bbeSSebastian Andrzej Siewior return len; 30588af8bbeSSebastian Andrzej Siewior err: 30688af8bbeSSebastian Andrzej Siewior kfree(name); 30788af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 30888af8bbeSSebastian Andrzej Siewior return ret; 30988af8bbeSSebastian Andrzej Siewior } 31088af8bbeSSebastian Andrzej Siewior 311a0249703SThinh Nguyen static ssize_t gadget_dev_desc_max_speed_show(struct config_item *item, 312a0249703SThinh Nguyen char *page) 313a0249703SThinh Nguyen { 314a0249703SThinh Nguyen enum usb_device_speed speed = to_gadget_info(item)->composite.max_speed; 315a0249703SThinh Nguyen 316a0249703SThinh Nguyen return sprintf(page, "%s\n", usb_speed_string(speed)); 317a0249703SThinh Nguyen } 318a0249703SThinh Nguyen 319a0249703SThinh Nguyen static ssize_t gadget_dev_desc_max_speed_store(struct config_item *item, 320a0249703SThinh Nguyen const char *page, size_t len) 321a0249703SThinh Nguyen { 322a0249703SThinh Nguyen struct gadget_info *gi = to_gadget_info(item); 323a0249703SThinh Nguyen 324a0249703SThinh Nguyen mutex_lock(&gi->lock); 325a0249703SThinh Nguyen 326a0249703SThinh Nguyen /* Prevent changing of max_speed after the driver is binded */ 327a0249703SThinh Nguyen if (gi->composite.gadget_driver.udc_name) 328a0249703SThinh Nguyen goto err; 329a0249703SThinh Nguyen 330a0249703SThinh Nguyen if (strncmp(page, "super-speed-plus", 16) == 0) 331a0249703SThinh Nguyen gi->composite.max_speed = USB_SPEED_SUPER_PLUS; 332a0249703SThinh Nguyen else if (strncmp(page, "super-speed", 11) == 0) 333a0249703SThinh Nguyen gi->composite.max_speed = USB_SPEED_SUPER; 334a0249703SThinh Nguyen else if (strncmp(page, "high-speed", 10) == 0) 335a0249703SThinh Nguyen gi->composite.max_speed = USB_SPEED_HIGH; 336a0249703SThinh Nguyen else if (strncmp(page, "full-speed", 10) == 0) 337a0249703SThinh Nguyen gi->composite.max_speed = USB_SPEED_FULL; 338a0249703SThinh Nguyen else if (strncmp(page, "low-speed", 9) == 0) 339a0249703SThinh Nguyen gi->composite.max_speed = USB_SPEED_LOW; 340a0249703SThinh Nguyen else 341a0249703SThinh Nguyen goto err; 342a0249703SThinh Nguyen 343a0249703SThinh Nguyen gi->composite.gadget_driver.max_speed = gi->composite.max_speed; 344a0249703SThinh Nguyen 345a0249703SThinh Nguyen mutex_unlock(&gi->lock); 346a0249703SThinh Nguyen return len; 347a0249703SThinh Nguyen err: 348a0249703SThinh Nguyen mutex_unlock(&gi->lock); 349a0249703SThinh Nguyen return -EINVAL; 350a0249703SThinh Nguyen } 351a0249703SThinh Nguyen 35245b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, bDeviceClass); 35345b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, bDeviceSubClass); 35445b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, bDeviceProtocol); 35545b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, bMaxPacketSize0); 35645b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, idVendor); 35745b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, idProduct); 35845b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, bcdDevice); 35945b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, bcdUSB); 36045b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, UDC); 361a0249703SThinh Nguyen CONFIGFS_ATTR(gadget_dev_desc_, max_speed); 36288af8bbeSSebastian Andrzej Siewior 36388af8bbeSSebastian Andrzej Siewior static struct configfs_attribute *gadget_root_attrs[] = { 36445b6a73fSChristoph Hellwig &gadget_dev_desc_attr_bDeviceClass, 36545b6a73fSChristoph Hellwig &gadget_dev_desc_attr_bDeviceSubClass, 36645b6a73fSChristoph Hellwig &gadget_dev_desc_attr_bDeviceProtocol, 36745b6a73fSChristoph Hellwig &gadget_dev_desc_attr_bMaxPacketSize0, 36845b6a73fSChristoph Hellwig &gadget_dev_desc_attr_idVendor, 36945b6a73fSChristoph Hellwig &gadget_dev_desc_attr_idProduct, 37045b6a73fSChristoph Hellwig &gadget_dev_desc_attr_bcdDevice, 37145b6a73fSChristoph Hellwig &gadget_dev_desc_attr_bcdUSB, 37245b6a73fSChristoph Hellwig &gadget_dev_desc_attr_UDC, 373a0249703SThinh Nguyen &gadget_dev_desc_attr_max_speed, 37488af8bbeSSebastian Andrzej Siewior NULL, 37588af8bbeSSebastian Andrzej Siewior }; 37688af8bbeSSebastian Andrzej Siewior 3776e2a512dSDaniel Scally static inline struct gadget_language *to_gadget_language(struct config_item *item) 37888af8bbeSSebastian Andrzej Siewior { 3796e2a512dSDaniel Scally return container_of(to_config_group(item), struct gadget_language, 38088af8bbeSSebastian Andrzej Siewior group); 38188af8bbeSSebastian Andrzej Siewior } 38288af8bbeSSebastian Andrzej Siewior 38388af8bbeSSebastian Andrzej Siewior static inline struct gadget_config_name *to_gadget_config_name( 38488af8bbeSSebastian Andrzej Siewior struct config_item *item) 38588af8bbeSSebastian Andrzej Siewior { 38688af8bbeSSebastian Andrzej Siewior return container_of(to_config_group(item), struct gadget_config_name, 38788af8bbeSSebastian Andrzej Siewior group); 38888af8bbeSSebastian Andrzej Siewior } 38988af8bbeSSebastian Andrzej Siewior 39088af8bbeSSebastian Andrzej Siewior static inline struct usb_function_instance *to_usb_function_instance( 39188af8bbeSSebastian Andrzej Siewior struct config_item *item) 39288af8bbeSSebastian Andrzej Siewior { 39388af8bbeSSebastian Andrzej Siewior return container_of(to_config_group(item), 39488af8bbeSSebastian Andrzej Siewior struct usb_function_instance, group); 39588af8bbeSSebastian Andrzej Siewior } 39688af8bbeSSebastian Andrzej Siewior 39788af8bbeSSebastian Andrzej Siewior static void gadget_info_attr_release(struct config_item *item) 39888af8bbeSSebastian Andrzej Siewior { 39988af8bbeSSebastian Andrzej Siewior struct gadget_info *gi = to_gadget_info(item); 40088af8bbeSSebastian Andrzej Siewior 40188af8bbeSSebastian Andrzej Siewior WARN_ON(!list_empty(&gi->cdev.configs)); 40288af8bbeSSebastian Andrzej Siewior WARN_ON(!list_empty(&gi->string_list)); 40388af8bbeSSebastian Andrzej Siewior WARN_ON(!list_empty(&gi->available_func)); 40488af8bbeSSebastian Andrzej Siewior kfree(gi->composite.gadget_driver.function); 4057c075538SChanh Nguyen kfree(gi->composite.gadget_driver.driver.name); 40688af8bbeSSebastian Andrzej Siewior kfree(gi); 40788af8bbeSSebastian Andrzej Siewior } 40888af8bbeSSebastian Andrzej Siewior 40988af8bbeSSebastian Andrzej Siewior static struct configfs_item_operations gadget_root_item_ops = { 41088af8bbeSSebastian Andrzej Siewior .release = gadget_info_attr_release, 41188af8bbeSSebastian Andrzej Siewior }; 41288af8bbeSSebastian Andrzej Siewior 41388af8bbeSSebastian Andrzej Siewior static void gadget_config_attr_release(struct config_item *item) 41488af8bbeSSebastian Andrzej Siewior { 41588af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg = to_config_usb_cfg(item); 41688af8bbeSSebastian Andrzej Siewior 41788af8bbeSSebastian Andrzej Siewior WARN_ON(!list_empty(&cfg->c.functions)); 41888af8bbeSSebastian Andrzej Siewior list_del(&cfg->c.list); 41988af8bbeSSebastian Andrzej Siewior kfree(cfg->c.label); 42088af8bbeSSebastian Andrzej Siewior kfree(cfg); 42188af8bbeSSebastian Andrzej Siewior } 42288af8bbeSSebastian Andrzej Siewior 42388af8bbeSSebastian Andrzej Siewior static int config_usb_cfg_link( 42488af8bbeSSebastian Andrzej Siewior struct config_item *usb_cfg_ci, 42588af8bbeSSebastian Andrzej Siewior struct config_item *usb_func_ci) 42688af8bbeSSebastian Andrzej Siewior { 42788af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg = to_config_usb_cfg(usb_cfg_ci); 428260d88b7SLinyu Yuan struct gadget_info *gi = cfg_to_gadget_info(cfg); 42988af8bbeSSebastian Andrzej Siewior 4305284acccSLinyu Yuan struct usb_function_instance *fi = 4315284acccSLinyu Yuan to_usb_function_instance(usb_func_ci); 43236f4c25cSJakob Koschel struct usb_function_instance *a_fi = NULL, *iter; 43388af8bbeSSebastian Andrzej Siewior struct usb_function *f; 43488af8bbeSSebastian Andrzej Siewior int ret; 43588af8bbeSSebastian Andrzej Siewior 43688af8bbeSSebastian Andrzej Siewior mutex_lock(&gi->lock); 43788af8bbeSSebastian Andrzej Siewior /* 43888af8bbeSSebastian Andrzej Siewior * Make sure this function is from within our _this_ gadget and not 43988af8bbeSSebastian Andrzej Siewior * from another gadget or a random directory. 44088af8bbeSSebastian Andrzej Siewior * Also a function instance can only be linked once. 44188af8bbeSSebastian Andrzej Siewior */ 44289e7252dSUdipto Goswami 44389e7252dSUdipto Goswami if (gi->composite.gadget_driver.udc_name) { 44489e7252dSUdipto Goswami ret = -EINVAL; 44589e7252dSUdipto Goswami goto out; 44689e7252dSUdipto Goswami } 44789e7252dSUdipto Goswami 44836f4c25cSJakob Koschel list_for_each_entry(iter, &gi->available_func, cfs_list) { 44936f4c25cSJakob Koschel if (iter != fi) 45036f4c25cSJakob Koschel continue; 45136f4c25cSJakob Koschel a_fi = iter; 45288af8bbeSSebastian Andrzej Siewior break; 45388af8bbeSSebastian Andrzej Siewior } 45436f4c25cSJakob Koschel if (!a_fi) { 45588af8bbeSSebastian Andrzej Siewior ret = -EINVAL; 45688af8bbeSSebastian Andrzej Siewior goto out; 45788af8bbeSSebastian Andrzej Siewior } 45888af8bbeSSebastian Andrzej Siewior 45988af8bbeSSebastian Andrzej Siewior list_for_each_entry(f, &cfg->func_list, list) { 46088af8bbeSSebastian Andrzej Siewior if (f->fi == fi) { 46188af8bbeSSebastian Andrzej Siewior ret = -EEXIST; 46288af8bbeSSebastian Andrzej Siewior goto out; 46388af8bbeSSebastian Andrzej Siewior } 46488af8bbeSSebastian Andrzej Siewior } 46588af8bbeSSebastian Andrzej Siewior 46688af8bbeSSebastian Andrzej Siewior f = usb_get_function(fi); 46788af8bbeSSebastian Andrzej Siewior if (IS_ERR(f)) { 46888af8bbeSSebastian Andrzej Siewior ret = PTR_ERR(f); 46988af8bbeSSebastian Andrzej Siewior goto out; 47088af8bbeSSebastian Andrzej Siewior } 47188af8bbeSSebastian Andrzej Siewior 47288af8bbeSSebastian Andrzej Siewior /* stash the function until we bind it to the gadget */ 47388af8bbeSSebastian Andrzej Siewior list_add_tail(&f->list, &cfg->func_list); 47488af8bbeSSebastian Andrzej Siewior ret = 0; 47588af8bbeSSebastian Andrzej Siewior out: 47688af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 47788af8bbeSSebastian Andrzej Siewior return ret; 47888af8bbeSSebastian Andrzej Siewior } 47988af8bbeSSebastian Andrzej Siewior 480e16769d4SAndrzej Pietrasiewicz static void config_usb_cfg_unlink( 48188af8bbeSSebastian Andrzej Siewior struct config_item *usb_cfg_ci, 48288af8bbeSSebastian Andrzej Siewior struct config_item *usb_func_ci) 48388af8bbeSSebastian Andrzej Siewior { 48488af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg = to_config_usb_cfg(usb_cfg_ci); 485260d88b7SLinyu Yuan struct gadget_info *gi = cfg_to_gadget_info(cfg); 48688af8bbeSSebastian Andrzej Siewior 4875284acccSLinyu Yuan struct usb_function_instance *fi = 4885284acccSLinyu Yuan to_usb_function_instance(usb_func_ci); 48988af8bbeSSebastian Andrzej Siewior struct usb_function *f; 49088af8bbeSSebastian Andrzej Siewior 49188af8bbeSSebastian Andrzej Siewior /* 49288af8bbeSSebastian Andrzej Siewior * ideally I would like to forbid to unlink functions while a gadget is 49388af8bbeSSebastian Andrzej Siewior * bound to an UDC. Since this isn't possible at the moment, we simply 49488af8bbeSSebastian Andrzej Siewior * force an unbind, the function is available here and then we can 49588af8bbeSSebastian Andrzej Siewior * remove the function. 49688af8bbeSSebastian Andrzej Siewior */ 49788af8bbeSSebastian Andrzej Siewior mutex_lock(&gi->lock); 498afdaadc3SRuslan Bilovol if (gi->composite.gadget_driver.udc_name) 49988af8bbeSSebastian Andrzej Siewior unregister_gadget(gi); 500afdaadc3SRuslan Bilovol WARN_ON(gi->composite.gadget_driver.udc_name); 50188af8bbeSSebastian Andrzej Siewior 50288af8bbeSSebastian Andrzej Siewior list_for_each_entry(f, &cfg->func_list, list) { 50388af8bbeSSebastian Andrzej Siewior if (f->fi == fi) { 50488af8bbeSSebastian Andrzej Siewior list_del(&f->list); 50588af8bbeSSebastian Andrzej Siewior usb_put_function(f); 50688af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 507e16769d4SAndrzej Pietrasiewicz return; 50888af8bbeSSebastian Andrzej Siewior } 50988af8bbeSSebastian Andrzej Siewior } 51088af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 51175bfe23aSDavid Rientjes WARN(1, "Unable to locate function to unbind\n"); 51288af8bbeSSebastian Andrzej Siewior } 51388af8bbeSSebastian Andrzej Siewior 51488af8bbeSSebastian Andrzej Siewior static struct configfs_item_operations gadget_config_item_ops = { 51588af8bbeSSebastian Andrzej Siewior .release = gadget_config_attr_release, 51688af8bbeSSebastian Andrzej Siewior .allow_link = config_usb_cfg_link, 51788af8bbeSSebastian Andrzej Siewior .drop_link = config_usb_cfg_unlink, 51888af8bbeSSebastian Andrzej Siewior }; 51988af8bbeSSebastian Andrzej Siewior 52088af8bbeSSebastian Andrzej Siewior 52145b6a73fSChristoph Hellwig static ssize_t gadget_config_desc_MaxPower_show(struct config_item *item, 52288af8bbeSSebastian Andrzej Siewior char *page) 52388af8bbeSSebastian Andrzej Siewior { 524c26f1c10SLinyu Yuan struct config_usb_cfg *cfg = to_config_usb_cfg(item); 525c26f1c10SLinyu Yuan 526c26f1c10SLinyu Yuan return sprintf(page, "%u\n", cfg->c.MaxPower); 52788af8bbeSSebastian Andrzej Siewior } 52888af8bbeSSebastian Andrzej Siewior 52945b6a73fSChristoph Hellwig static ssize_t gadget_config_desc_MaxPower_store(struct config_item *item, 53088af8bbeSSebastian Andrzej Siewior const char *page, size_t len) 53188af8bbeSSebastian Andrzej Siewior { 532c26f1c10SLinyu Yuan struct config_usb_cfg *cfg = to_config_usb_cfg(item); 53388af8bbeSSebastian Andrzej Siewior u16 val; 53488af8bbeSSebastian Andrzej Siewior int ret; 53588af8bbeSSebastian Andrzej Siewior ret = kstrtou16(page, 0, &val); 53688af8bbeSSebastian Andrzej Siewior if (ret) 53788af8bbeSSebastian Andrzej Siewior return ret; 53888af8bbeSSebastian Andrzej Siewior if (DIV_ROUND_UP(val, 8) > 0xff) 53988af8bbeSSebastian Andrzej Siewior return -ERANGE; 540c26f1c10SLinyu Yuan cfg->c.MaxPower = val; 54188af8bbeSSebastian Andrzej Siewior return len; 54288af8bbeSSebastian Andrzej Siewior } 54388af8bbeSSebastian Andrzej Siewior 54445b6a73fSChristoph Hellwig static ssize_t gadget_config_desc_bmAttributes_show(struct config_item *item, 54588af8bbeSSebastian Andrzej Siewior char *page) 54688af8bbeSSebastian Andrzej Siewior { 547c26f1c10SLinyu Yuan struct config_usb_cfg *cfg = to_config_usb_cfg(item); 548c26f1c10SLinyu Yuan 549c26f1c10SLinyu Yuan return sprintf(page, "0x%02x\n", cfg->c.bmAttributes); 55088af8bbeSSebastian Andrzej Siewior } 55188af8bbeSSebastian Andrzej Siewior 55245b6a73fSChristoph Hellwig static ssize_t gadget_config_desc_bmAttributes_store(struct config_item *item, 55388af8bbeSSebastian Andrzej Siewior const char *page, size_t len) 55488af8bbeSSebastian Andrzej Siewior { 555c26f1c10SLinyu Yuan struct config_usb_cfg *cfg = to_config_usb_cfg(item); 55688af8bbeSSebastian Andrzej Siewior u8 val; 55788af8bbeSSebastian Andrzej Siewior int ret; 55888af8bbeSSebastian Andrzej Siewior ret = kstrtou8(page, 0, &val); 55988af8bbeSSebastian Andrzej Siewior if (ret) 56088af8bbeSSebastian Andrzej Siewior return ret; 56188af8bbeSSebastian Andrzej Siewior if (!(val & USB_CONFIG_ATT_ONE)) 56288af8bbeSSebastian Andrzej Siewior return -EINVAL; 56388af8bbeSSebastian Andrzej Siewior if (val & ~(USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER | 56488af8bbeSSebastian Andrzej Siewior USB_CONFIG_ATT_WAKEUP)) 56588af8bbeSSebastian Andrzej Siewior return -EINVAL; 566c26f1c10SLinyu Yuan cfg->c.bmAttributes = val; 56788af8bbeSSebastian Andrzej Siewior return len; 56888af8bbeSSebastian Andrzej Siewior } 56988af8bbeSSebastian Andrzej Siewior 57045b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_config_desc_, MaxPower); 57145b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_config_desc_, bmAttributes); 57288af8bbeSSebastian Andrzej Siewior 57388af8bbeSSebastian Andrzej Siewior static struct configfs_attribute *gadget_config_attrs[] = { 57445b6a73fSChristoph Hellwig &gadget_config_desc_attr_MaxPower, 57545b6a73fSChristoph Hellwig &gadget_config_desc_attr_bmAttributes, 57688af8bbeSSebastian Andrzej Siewior NULL, 57788af8bbeSSebastian Andrzej Siewior }; 57888af8bbeSSebastian Andrzej Siewior 5794ad01412SBhumika Goyal static const struct config_item_type gadget_config_type = { 58088af8bbeSSebastian Andrzej Siewior .ct_item_ops = &gadget_config_item_ops, 58188af8bbeSSebastian Andrzej Siewior .ct_attrs = gadget_config_attrs, 58288af8bbeSSebastian Andrzej Siewior .ct_owner = THIS_MODULE, 58388af8bbeSSebastian Andrzej Siewior }; 58488af8bbeSSebastian Andrzej Siewior 5854ad01412SBhumika Goyal static const struct config_item_type gadget_root_type = { 58688af8bbeSSebastian Andrzej Siewior .ct_item_ops = &gadget_root_item_ops, 58788af8bbeSSebastian Andrzej Siewior .ct_attrs = gadget_root_attrs, 58888af8bbeSSebastian Andrzej Siewior .ct_owner = THIS_MODULE, 58988af8bbeSSebastian Andrzej Siewior }; 59088af8bbeSSebastian Andrzej Siewior 59188af8bbeSSebastian Andrzej Siewior static void composite_init_dev(struct usb_composite_dev *cdev) 59288af8bbeSSebastian Andrzej Siewior { 59388af8bbeSSebastian Andrzej Siewior spin_lock_init(&cdev->lock); 59488af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&cdev->configs); 59588af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&cdev->gstrings); 59688af8bbeSSebastian Andrzej Siewior } 59788af8bbeSSebastian Andrzej Siewior 59888af8bbeSSebastian Andrzej Siewior static struct config_group *function_make( 59988af8bbeSSebastian Andrzej Siewior struct config_group *group, 60088af8bbeSSebastian Andrzej Siewior const char *name) 60188af8bbeSSebastian Andrzej Siewior { 60288af8bbeSSebastian Andrzej Siewior struct gadget_info *gi; 60388af8bbeSSebastian Andrzej Siewior struct usb_function_instance *fi; 60488af8bbeSSebastian Andrzej Siewior char buf[MAX_NAME_LEN]; 60588af8bbeSSebastian Andrzej Siewior char *func_name; 60688af8bbeSSebastian Andrzej Siewior char *instance_name; 60788af8bbeSSebastian Andrzej Siewior int ret; 60888af8bbeSSebastian Andrzej Siewior 60988af8bbeSSebastian Andrzej Siewior ret = snprintf(buf, MAX_NAME_LEN, "%s", name); 61088af8bbeSSebastian Andrzej Siewior if (ret >= MAX_NAME_LEN) 61188af8bbeSSebastian Andrzej Siewior return ERR_PTR(-ENAMETOOLONG); 61288af8bbeSSebastian Andrzej Siewior 61388af8bbeSSebastian Andrzej Siewior func_name = buf; 61488af8bbeSSebastian Andrzej Siewior instance_name = strchr(func_name, '.'); 61588af8bbeSSebastian Andrzej Siewior if (!instance_name) { 61688af8bbeSSebastian Andrzej Siewior pr_err("Unable to locate . in FUNC.INSTANCE\n"); 61788af8bbeSSebastian Andrzej Siewior return ERR_PTR(-EINVAL); 61888af8bbeSSebastian Andrzej Siewior } 61988af8bbeSSebastian Andrzej Siewior *instance_name = '\0'; 62088af8bbeSSebastian Andrzej Siewior instance_name++; 62188af8bbeSSebastian Andrzej Siewior 62288af8bbeSSebastian Andrzej Siewior fi = usb_get_function_instance(func_name); 62388af8bbeSSebastian Andrzej Siewior if (IS_ERR(fi)) 624a3469411SDuan Jiong return ERR_CAST(fi); 62588af8bbeSSebastian Andrzej Siewior 6263958b792SNicolas Iooss ret = config_item_set_name(&fi->group.cg_item, "%s", name); 62788af8bbeSSebastian Andrzej Siewior if (ret) { 62888af8bbeSSebastian Andrzej Siewior usb_put_function_instance(fi); 62988af8bbeSSebastian Andrzej Siewior return ERR_PTR(ret); 63088af8bbeSSebastian Andrzej Siewior } 6311933861dSAndrzej Pietrasiewicz if (fi->set_inst_name) { 6321933861dSAndrzej Pietrasiewicz ret = fi->set_inst_name(fi, instance_name); 6331933861dSAndrzej Pietrasiewicz if (ret) { 6341933861dSAndrzej Pietrasiewicz usb_put_function_instance(fi); 6351933861dSAndrzej Pietrasiewicz return ERR_PTR(ret); 6361933861dSAndrzej Pietrasiewicz } 6371933861dSAndrzej Pietrasiewicz } 63888af8bbeSSebastian Andrzej Siewior 63988af8bbeSSebastian Andrzej Siewior gi = container_of(group, struct gadget_info, functions_group); 64088af8bbeSSebastian Andrzej Siewior 64188af8bbeSSebastian Andrzej Siewior mutex_lock(&gi->lock); 64288af8bbeSSebastian Andrzej Siewior list_add_tail(&fi->cfs_list, &gi->available_func); 64388af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 64488af8bbeSSebastian Andrzej Siewior return &fi->group; 64588af8bbeSSebastian Andrzej Siewior } 64688af8bbeSSebastian Andrzej Siewior 64788af8bbeSSebastian Andrzej Siewior static void function_drop( 64888af8bbeSSebastian Andrzej Siewior struct config_group *group, 64988af8bbeSSebastian Andrzej Siewior struct config_item *item) 65088af8bbeSSebastian Andrzej Siewior { 65188af8bbeSSebastian Andrzej Siewior struct usb_function_instance *fi = to_usb_function_instance(item); 65288af8bbeSSebastian Andrzej Siewior struct gadget_info *gi; 65388af8bbeSSebastian Andrzej Siewior 65488af8bbeSSebastian Andrzej Siewior gi = container_of(group, struct gadget_info, functions_group); 65588af8bbeSSebastian Andrzej Siewior 65688af8bbeSSebastian Andrzej Siewior mutex_lock(&gi->lock); 65788af8bbeSSebastian Andrzej Siewior list_del(&fi->cfs_list); 65888af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 65988af8bbeSSebastian Andrzej Siewior config_item_put(item); 66088af8bbeSSebastian Andrzej Siewior } 66188af8bbeSSebastian Andrzej Siewior 66288af8bbeSSebastian Andrzej Siewior static struct configfs_group_operations functions_ops = { 66388af8bbeSSebastian Andrzej Siewior .make_group = &function_make, 66488af8bbeSSebastian Andrzej Siewior .drop_item = &function_drop, 66588af8bbeSSebastian Andrzej Siewior }; 66688af8bbeSSebastian Andrzej Siewior 6674ad01412SBhumika Goyal static const struct config_item_type functions_type = { 66888af8bbeSSebastian Andrzej Siewior .ct_group_ops = &functions_ops, 66988af8bbeSSebastian Andrzej Siewior .ct_owner = THIS_MODULE, 67088af8bbeSSebastian Andrzej Siewior }; 67188af8bbeSSebastian Andrzej Siewior 67288af8bbeSSebastian Andrzej Siewior GS_STRINGS_RW(gadget_config_name, configuration); 67388af8bbeSSebastian Andrzej Siewior 67488af8bbeSSebastian Andrzej Siewior static struct configfs_attribute *gadget_config_name_langid_attrs[] = { 67545b6a73fSChristoph Hellwig &gadget_config_name_attr_configuration, 67688af8bbeSSebastian Andrzej Siewior NULL, 67788af8bbeSSebastian Andrzej Siewior }; 67888af8bbeSSebastian Andrzej Siewior 67988af8bbeSSebastian Andrzej Siewior static void gadget_config_name_attr_release(struct config_item *item) 68088af8bbeSSebastian Andrzej Siewior { 68188af8bbeSSebastian Andrzej Siewior struct gadget_config_name *cn = to_gadget_config_name(item); 68288af8bbeSSebastian Andrzej Siewior 68388af8bbeSSebastian Andrzej Siewior kfree(cn->configuration); 68488af8bbeSSebastian Andrzej Siewior 68588af8bbeSSebastian Andrzej Siewior list_del(&cn->list); 68688af8bbeSSebastian Andrzej Siewior kfree(cn); 68788af8bbeSSebastian Andrzej Siewior } 68888af8bbeSSebastian Andrzej Siewior 68988af8bbeSSebastian Andrzej Siewior USB_CONFIG_STRING_RW_OPS(gadget_config_name); 69088af8bbeSSebastian Andrzej Siewior USB_CONFIG_STRINGS_LANG(gadget_config_name, config_usb_cfg); 69188af8bbeSSebastian Andrzej Siewior 69288af8bbeSSebastian Andrzej Siewior static struct config_group *config_desc_make( 69388af8bbeSSebastian Andrzej Siewior struct config_group *group, 69488af8bbeSSebastian Andrzej Siewior const char *name) 69588af8bbeSSebastian Andrzej Siewior { 69688af8bbeSSebastian Andrzej Siewior struct gadget_info *gi; 69788af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg; 69888af8bbeSSebastian Andrzej Siewior char buf[MAX_NAME_LEN]; 69988af8bbeSSebastian Andrzej Siewior char *num_str; 70088af8bbeSSebastian Andrzej Siewior u8 num; 70188af8bbeSSebastian Andrzej Siewior int ret; 70288af8bbeSSebastian Andrzej Siewior 70388af8bbeSSebastian Andrzej Siewior gi = container_of(group, struct gadget_info, configs_group); 70488af8bbeSSebastian Andrzej Siewior ret = snprintf(buf, MAX_NAME_LEN, "%s", name); 70588af8bbeSSebastian Andrzej Siewior if (ret >= MAX_NAME_LEN) 70688af8bbeSSebastian Andrzej Siewior return ERR_PTR(-ENAMETOOLONG); 70788af8bbeSSebastian Andrzej Siewior 70888af8bbeSSebastian Andrzej Siewior num_str = strchr(buf, '.'); 70988af8bbeSSebastian Andrzej Siewior if (!num_str) { 71088af8bbeSSebastian Andrzej Siewior pr_err("Unable to locate . in name.bConfigurationValue\n"); 71188af8bbeSSebastian Andrzej Siewior return ERR_PTR(-EINVAL); 71288af8bbeSSebastian Andrzej Siewior } 71388af8bbeSSebastian Andrzej Siewior 71488af8bbeSSebastian Andrzej Siewior *num_str = '\0'; 71588af8bbeSSebastian Andrzej Siewior num_str++; 71688af8bbeSSebastian Andrzej Siewior 71788af8bbeSSebastian Andrzej Siewior if (!strlen(buf)) 71888af8bbeSSebastian Andrzej Siewior return ERR_PTR(-EINVAL); 71988af8bbeSSebastian Andrzej Siewior 72088af8bbeSSebastian Andrzej Siewior ret = kstrtou8(num_str, 0, &num); 72188af8bbeSSebastian Andrzej Siewior if (ret) 72288af8bbeSSebastian Andrzej Siewior return ERR_PTR(ret); 72388af8bbeSSebastian Andrzej Siewior 72488af8bbeSSebastian Andrzej Siewior cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); 72588af8bbeSSebastian Andrzej Siewior if (!cfg) 72688af8bbeSSebastian Andrzej Siewior return ERR_PTR(-ENOMEM); 72788af8bbeSSebastian Andrzej Siewior cfg->c.label = kstrdup(buf, GFP_KERNEL); 72888af8bbeSSebastian Andrzej Siewior if (!cfg->c.label) { 72988af8bbeSSebastian Andrzej Siewior ret = -ENOMEM; 73088af8bbeSSebastian Andrzej Siewior goto err; 73188af8bbeSSebastian Andrzej Siewior } 73288af8bbeSSebastian Andrzej Siewior cfg->c.bConfigurationValue = num; 73388af8bbeSSebastian Andrzej Siewior cfg->c.MaxPower = CONFIG_USB_GADGET_VBUS_DRAW; 73488af8bbeSSebastian Andrzej Siewior cfg->c.bmAttributes = USB_CONFIG_ATT_ONE; 73588af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&cfg->string_list); 73688af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&cfg->func_list); 73788af8bbeSSebastian Andrzej Siewior 73888af8bbeSSebastian Andrzej Siewior config_group_init_type_name(&cfg->group, name, 73988af8bbeSSebastian Andrzej Siewior &gadget_config_type); 7401ae1602dSChristoph Hellwig 74188af8bbeSSebastian Andrzej Siewior config_group_init_type_name(&cfg->strings_group, "strings", 74288af8bbeSSebastian Andrzej Siewior &gadget_config_name_strings_type); 7431ae1602dSChristoph Hellwig configfs_add_default_group(&cfg->strings_group, &cfg->group); 74488af8bbeSSebastian Andrzej Siewior 74588af8bbeSSebastian Andrzej Siewior ret = usb_add_config_only(&gi->cdev, &cfg->c); 74688af8bbeSSebastian Andrzej Siewior if (ret) 74788af8bbeSSebastian Andrzej Siewior goto err; 74888af8bbeSSebastian Andrzej Siewior 74988af8bbeSSebastian Andrzej Siewior return &cfg->group; 75088af8bbeSSebastian Andrzej Siewior err: 75188af8bbeSSebastian Andrzej Siewior kfree(cfg->c.label); 75288af8bbeSSebastian Andrzej Siewior kfree(cfg); 75388af8bbeSSebastian Andrzej Siewior return ERR_PTR(ret); 75488af8bbeSSebastian Andrzej Siewior } 75588af8bbeSSebastian Andrzej Siewior 75688af8bbeSSebastian Andrzej Siewior static void config_desc_drop( 75788af8bbeSSebastian Andrzej Siewior struct config_group *group, 75888af8bbeSSebastian Andrzej Siewior struct config_item *item) 75988af8bbeSSebastian Andrzej Siewior { 76088af8bbeSSebastian Andrzej Siewior config_item_put(item); 76188af8bbeSSebastian Andrzej Siewior } 76288af8bbeSSebastian Andrzej Siewior 76388af8bbeSSebastian Andrzej Siewior static struct configfs_group_operations config_desc_ops = { 76488af8bbeSSebastian Andrzej Siewior .make_group = &config_desc_make, 76588af8bbeSSebastian Andrzej Siewior .drop_item = &config_desc_drop, 76688af8bbeSSebastian Andrzej Siewior }; 76788af8bbeSSebastian Andrzej Siewior 7684ad01412SBhumika Goyal static const struct config_item_type config_desc_type = { 76988af8bbeSSebastian Andrzej Siewior .ct_group_ops = &config_desc_ops, 77088af8bbeSSebastian Andrzej Siewior .ct_owner = THIS_MODULE, 77188af8bbeSSebastian Andrzej Siewior }; 77288af8bbeSSebastian Andrzej Siewior 7736e2a512dSDaniel Scally GS_STRINGS_RW(gadget_language, manufacturer); 7746e2a512dSDaniel Scally GS_STRINGS_RW(gadget_language, product); 7756e2a512dSDaniel Scally GS_STRINGS_RW(gadget_language, serialnumber); 77688af8bbeSSebastian Andrzej Siewior 7776e2a512dSDaniel Scally static struct configfs_attribute *gadget_language_langid_attrs[] = { 7786e2a512dSDaniel Scally &gadget_language_attr_manufacturer, 7796e2a512dSDaniel Scally &gadget_language_attr_product, 7806e2a512dSDaniel Scally &gadget_language_attr_serialnumber, 78188af8bbeSSebastian Andrzej Siewior NULL, 78288af8bbeSSebastian Andrzej Siewior }; 78388af8bbeSSebastian Andrzej Siewior 7846e2a512dSDaniel Scally static void gadget_language_attr_release(struct config_item *item) 78588af8bbeSSebastian Andrzej Siewior { 7866e2a512dSDaniel Scally struct gadget_language *gs = to_gadget_language(item); 78788af8bbeSSebastian Andrzej Siewior 78888af8bbeSSebastian Andrzej Siewior kfree(gs->manufacturer); 78988af8bbeSSebastian Andrzej Siewior kfree(gs->product); 79088af8bbeSSebastian Andrzej Siewior kfree(gs->serialnumber); 79188af8bbeSSebastian Andrzej Siewior 79288af8bbeSSebastian Andrzej Siewior list_del(&gs->list); 79388af8bbeSSebastian Andrzej Siewior kfree(gs); 79488af8bbeSSebastian Andrzej Siewior } 79588af8bbeSSebastian Andrzej Siewior 79615a7cf8cSDaniel Scally static struct configfs_item_operations gadget_language_langid_item_ops = { 79715a7cf8cSDaniel Scally .release = gadget_language_attr_release, 79815a7cf8cSDaniel Scally }; 79915a7cf8cSDaniel Scally 80015a7cf8cSDaniel Scally static ssize_t gadget_string_id_show(struct config_item *item, char *page) 80115a7cf8cSDaniel Scally { 80215a7cf8cSDaniel Scally struct gadget_string *string = to_gadget_string(item); 80315a7cf8cSDaniel Scally int ret; 80415a7cf8cSDaniel Scally 80515a7cf8cSDaniel Scally ret = sprintf(page, "%u\n", string->usb_string.id); 80615a7cf8cSDaniel Scally return ret; 80715a7cf8cSDaniel Scally } 80815a7cf8cSDaniel Scally CONFIGFS_ATTR_RO(gadget_string_, id); 80915a7cf8cSDaniel Scally 81015a7cf8cSDaniel Scally static ssize_t gadget_string_s_show(struct config_item *item, char *page) 81115a7cf8cSDaniel Scally { 81215a7cf8cSDaniel Scally struct gadget_string *string = to_gadget_string(item); 81315a7cf8cSDaniel Scally int ret; 81415a7cf8cSDaniel Scally 81515a7cf8cSDaniel Scally ret = snprintf(page, sizeof(string->string), "%s\n", string->string); 81615a7cf8cSDaniel Scally return ret; 81715a7cf8cSDaniel Scally } 81815a7cf8cSDaniel Scally 81915a7cf8cSDaniel Scally static ssize_t gadget_string_s_store(struct config_item *item, const char *page, 82015a7cf8cSDaniel Scally size_t len) 82115a7cf8cSDaniel Scally { 82215a7cf8cSDaniel Scally struct gadget_string *string = to_gadget_string(item); 82315a7cf8cSDaniel Scally int size = min(sizeof(string->string), len + 1); 82415a7cf8cSDaniel Scally 82515a7cf8cSDaniel Scally if (len > USB_MAX_STRING_LEN) 82615a7cf8cSDaniel Scally return -EINVAL; 82715a7cf8cSDaniel Scally 828*8488a831SDaniel Scally return strscpy(string->string, page, size); 82915a7cf8cSDaniel Scally } 83015a7cf8cSDaniel Scally CONFIGFS_ATTR(gadget_string_, s); 83115a7cf8cSDaniel Scally 83215a7cf8cSDaniel Scally static struct configfs_attribute *gadget_string_attrs[] = { 83315a7cf8cSDaniel Scally &gadget_string_attr_id, 83415a7cf8cSDaniel Scally &gadget_string_attr_s, 83515a7cf8cSDaniel Scally NULL, 83615a7cf8cSDaniel Scally }; 83715a7cf8cSDaniel Scally 83815a7cf8cSDaniel Scally static void gadget_string_release(struct config_item *item) 83915a7cf8cSDaniel Scally { 84015a7cf8cSDaniel Scally struct gadget_string *string = to_gadget_string(item); 84115a7cf8cSDaniel Scally 84215a7cf8cSDaniel Scally kfree(string); 84315a7cf8cSDaniel Scally } 84415a7cf8cSDaniel Scally 84515a7cf8cSDaniel Scally static struct configfs_item_operations gadget_string_item_ops = { 84615a7cf8cSDaniel Scally .release = gadget_string_release, 84715a7cf8cSDaniel Scally }; 84815a7cf8cSDaniel Scally 84915a7cf8cSDaniel Scally static const struct config_item_type gadget_string_type = { 85015a7cf8cSDaniel Scally .ct_item_ops = &gadget_string_item_ops, 85115a7cf8cSDaniel Scally .ct_attrs = gadget_string_attrs, 85215a7cf8cSDaniel Scally .ct_owner = THIS_MODULE, 85315a7cf8cSDaniel Scally }; 85415a7cf8cSDaniel Scally 85515a7cf8cSDaniel Scally static struct config_item *gadget_language_string_make(struct config_group *group, 85615a7cf8cSDaniel Scally const char *name) 85715a7cf8cSDaniel Scally { 85815a7cf8cSDaniel Scally struct gadget_language *language; 85915a7cf8cSDaniel Scally struct gadget_string *string; 86015a7cf8cSDaniel Scally 86115a7cf8cSDaniel Scally language = to_gadget_language(&group->cg_item); 86215a7cf8cSDaniel Scally 86315a7cf8cSDaniel Scally string = kzalloc(sizeof(*string), GFP_KERNEL); 86415a7cf8cSDaniel Scally if (!string) 86515a7cf8cSDaniel Scally return ERR_PTR(-ENOMEM); 86615a7cf8cSDaniel Scally 86715a7cf8cSDaniel Scally string->usb_string.id = language->nstrings++; 86815a7cf8cSDaniel Scally string->usb_string.s = string->string; 86915a7cf8cSDaniel Scally list_add_tail(&string->list, &language->gadget_strings); 87015a7cf8cSDaniel Scally 87115a7cf8cSDaniel Scally config_item_init_type_name(&string->item, name, &gadget_string_type); 87215a7cf8cSDaniel Scally 87315a7cf8cSDaniel Scally return &string->item; 87415a7cf8cSDaniel Scally } 87515a7cf8cSDaniel Scally 87615a7cf8cSDaniel Scally static void gadget_language_string_drop(struct config_group *group, 87715a7cf8cSDaniel Scally struct config_item *item) 87815a7cf8cSDaniel Scally { 87915a7cf8cSDaniel Scally struct gadget_language *language; 88015a7cf8cSDaniel Scally struct gadget_string *string; 88115a7cf8cSDaniel Scally unsigned int i = USB_GADGET_FIRST_AVAIL_IDX; 88215a7cf8cSDaniel Scally 88315a7cf8cSDaniel Scally language = to_gadget_language(&group->cg_item); 88415a7cf8cSDaniel Scally string = to_gadget_string(item); 88515a7cf8cSDaniel Scally 88615a7cf8cSDaniel Scally list_del(&string->list); 88715a7cf8cSDaniel Scally language->nstrings--; 88815a7cf8cSDaniel Scally 88915a7cf8cSDaniel Scally /* Reset the ids for the language's strings to guarantee a continuous set */ 89015a7cf8cSDaniel Scally list_for_each_entry(string, &language->gadget_strings, list) 89115a7cf8cSDaniel Scally string->usb_string.id = i++; 89215a7cf8cSDaniel Scally } 89315a7cf8cSDaniel Scally 89415a7cf8cSDaniel Scally static struct configfs_group_operations gadget_language_langid_group_ops = { 89515a7cf8cSDaniel Scally .make_item = gadget_language_string_make, 89615a7cf8cSDaniel Scally .drop_item = gadget_language_string_drop, 89715a7cf8cSDaniel Scally }; 89815a7cf8cSDaniel Scally 89915a7cf8cSDaniel Scally static struct config_item_type gadget_language_type = { 90015a7cf8cSDaniel Scally .ct_item_ops = &gadget_language_langid_item_ops, 90115a7cf8cSDaniel Scally .ct_group_ops = &gadget_language_langid_group_ops, 90215a7cf8cSDaniel Scally .ct_attrs = gadget_language_langid_attrs, 90315a7cf8cSDaniel Scally .ct_owner = THIS_MODULE, 90415a7cf8cSDaniel Scally }; 90515a7cf8cSDaniel Scally 90615a7cf8cSDaniel Scally static struct config_group *gadget_language_make(struct config_group *group, 90715a7cf8cSDaniel Scally const char *name) 90815a7cf8cSDaniel Scally { 90915a7cf8cSDaniel Scally struct gadget_info *gi; 91015a7cf8cSDaniel Scally struct gadget_language *gs; 91115a7cf8cSDaniel Scally struct gadget_language *new; 91215a7cf8cSDaniel Scally int langs = 0; 91315a7cf8cSDaniel Scally int ret; 91415a7cf8cSDaniel Scally 91515a7cf8cSDaniel Scally new = kzalloc(sizeof(*new), GFP_KERNEL); 91615a7cf8cSDaniel Scally if (!new) 91715a7cf8cSDaniel Scally return ERR_PTR(-ENOMEM); 91815a7cf8cSDaniel Scally 91915a7cf8cSDaniel Scally ret = check_user_usb_string(name, &new->stringtab_dev); 92015a7cf8cSDaniel Scally if (ret) 92115a7cf8cSDaniel Scally goto err; 92215a7cf8cSDaniel Scally config_group_init_type_name(&new->group, name, 92315a7cf8cSDaniel Scally &gadget_language_type); 92415a7cf8cSDaniel Scally 92515a7cf8cSDaniel Scally gi = container_of(group, struct gadget_info, strings_group); 92615a7cf8cSDaniel Scally ret = -EEXIST; 92715a7cf8cSDaniel Scally list_for_each_entry(gs, &gi->string_list, list) { 92815a7cf8cSDaniel Scally if (gs->stringtab_dev.language == new->stringtab_dev.language) 92915a7cf8cSDaniel Scally goto err; 93015a7cf8cSDaniel Scally langs++; 93115a7cf8cSDaniel Scally } 93215a7cf8cSDaniel Scally ret = -EOVERFLOW; 93315a7cf8cSDaniel Scally if (langs >= MAX_USB_STRING_LANGS) 93415a7cf8cSDaniel Scally goto err; 93515a7cf8cSDaniel Scally 93615a7cf8cSDaniel Scally list_add_tail(&new->list, &gi->string_list); 93715a7cf8cSDaniel Scally INIT_LIST_HEAD(&new->gadget_strings); 93815a7cf8cSDaniel Scally 93915a7cf8cSDaniel Scally /* We have the default manufacturer, product and serialnumber strings */ 94015a7cf8cSDaniel Scally new->nstrings = 3; 94115a7cf8cSDaniel Scally return &new->group; 94215a7cf8cSDaniel Scally err: 94315a7cf8cSDaniel Scally kfree(new); 94415a7cf8cSDaniel Scally return ERR_PTR(ret); 94515a7cf8cSDaniel Scally } 94615a7cf8cSDaniel Scally 94715a7cf8cSDaniel Scally static void gadget_language_drop(struct config_group *group, 94815a7cf8cSDaniel Scally struct config_item *item) 94915a7cf8cSDaniel Scally { 95015a7cf8cSDaniel Scally config_item_put(item); 95115a7cf8cSDaniel Scally } 95215a7cf8cSDaniel Scally 95315a7cf8cSDaniel Scally static struct configfs_group_operations gadget_language_group_ops = { 95415a7cf8cSDaniel Scally .make_group = &gadget_language_make, 95515a7cf8cSDaniel Scally .drop_item = &gadget_language_drop, 95615a7cf8cSDaniel Scally }; 95715a7cf8cSDaniel Scally 95815a7cf8cSDaniel Scally static struct config_item_type gadget_language_strings_type = { 95915a7cf8cSDaniel Scally .ct_group_ops = &gadget_language_group_ops, 96015a7cf8cSDaniel Scally .ct_owner = THIS_MODULE, 96115a7cf8cSDaniel Scally }; 96288af8bbeSSebastian Andrzej Siewior 96393c47394SJó Ágila Bitsch static inline struct gadget_info *webusb_item_to_gadget_info( 96493c47394SJó Ágila Bitsch struct config_item *item) 96593c47394SJó Ágila Bitsch { 96693c47394SJó Ágila Bitsch return container_of(to_config_group(item), 96793c47394SJó Ágila Bitsch struct gadget_info, webusb_group); 96893c47394SJó Ágila Bitsch } 96993c47394SJó Ágila Bitsch 97093c47394SJó Ágila Bitsch static ssize_t webusb_use_show(struct config_item *item, char *page) 97193c47394SJó Ágila Bitsch { 97293c47394SJó Ágila Bitsch return sysfs_emit(page, "%d\n", 97393c47394SJó Ágila Bitsch webusb_item_to_gadget_info(item)->use_webusb); 97493c47394SJó Ágila Bitsch } 97593c47394SJó Ágila Bitsch 97693c47394SJó Ágila Bitsch static ssize_t webusb_use_store(struct config_item *item, const char *page, 97793c47394SJó Ágila Bitsch size_t len) 97893c47394SJó Ágila Bitsch { 97993c47394SJó Ágila Bitsch struct gadget_info *gi = webusb_item_to_gadget_info(item); 98093c47394SJó Ágila Bitsch int ret; 98193c47394SJó Ágila Bitsch bool use; 98293c47394SJó Ágila Bitsch 98393c47394SJó Ágila Bitsch ret = kstrtobool(page, &use); 9846f7fb48dSAndy Shevchenko if (ret) 9856f7fb48dSAndy Shevchenko return ret; 9866f7fb48dSAndy Shevchenko 9876f7fb48dSAndy Shevchenko mutex_lock(&gi->lock); 98893c47394SJó Ágila Bitsch gi->use_webusb = use; 98993c47394SJó Ágila Bitsch mutex_unlock(&gi->lock); 99093c47394SJó Ágila Bitsch 9916f7fb48dSAndy Shevchenko return len; 99293c47394SJó Ágila Bitsch } 99393c47394SJó Ágila Bitsch 99493c47394SJó Ágila Bitsch static ssize_t webusb_bcdVersion_show(struct config_item *item, char *page) 99593c47394SJó Ágila Bitsch { 99693c47394SJó Ágila Bitsch return sysfs_emit(page, "0x%04x\n", 99793c47394SJó Ágila Bitsch webusb_item_to_gadget_info(item)->bcd_webusb_version); 99893c47394SJó Ágila Bitsch } 99993c47394SJó Ágila Bitsch 100093c47394SJó Ágila Bitsch static ssize_t webusb_bcdVersion_store(struct config_item *item, 100193c47394SJó Ágila Bitsch const char *page, size_t len) 100293c47394SJó Ágila Bitsch { 100393c47394SJó Ágila Bitsch struct gadget_info *gi = webusb_item_to_gadget_info(item); 100493c47394SJó Ágila Bitsch u16 bcdVersion; 100593c47394SJó Ágila Bitsch int ret; 100693c47394SJó Ágila Bitsch 100793c47394SJó Ágila Bitsch ret = kstrtou16(page, 0, &bcdVersion); 100893c47394SJó Ágila Bitsch if (ret) 10096f7fb48dSAndy Shevchenko return ret; 10106f7fb48dSAndy Shevchenko 101193c47394SJó Ágila Bitsch ret = is_valid_bcd(bcdVersion); 101293c47394SJó Ágila Bitsch if (ret) 1013582cef43SAndy Shevchenko return ret; 101493c47394SJó Ágila Bitsch 1015582cef43SAndy Shevchenko mutex_lock(&gi->lock); 101693c47394SJó Ágila Bitsch gi->bcd_webusb_version = bcdVersion; 101793c47394SJó Ágila Bitsch mutex_unlock(&gi->lock); 101893c47394SJó Ágila Bitsch 1019582cef43SAndy Shevchenko return len; 102093c47394SJó Ágila Bitsch } 102193c47394SJó Ágila Bitsch 102293c47394SJó Ágila Bitsch static ssize_t webusb_bVendorCode_show(struct config_item *item, char *page) 102393c47394SJó Ágila Bitsch { 102493c47394SJó Ágila Bitsch return sysfs_emit(page, "0x%02x\n", 102593c47394SJó Ágila Bitsch webusb_item_to_gadget_info(item)->b_webusb_vendor_code); 102693c47394SJó Ágila Bitsch } 102793c47394SJó Ágila Bitsch 102893c47394SJó Ágila Bitsch static ssize_t webusb_bVendorCode_store(struct config_item *item, 102993c47394SJó Ágila Bitsch const char *page, size_t len) 103093c47394SJó Ágila Bitsch { 103193c47394SJó Ágila Bitsch struct gadget_info *gi = webusb_item_to_gadget_info(item); 103293c47394SJó Ágila Bitsch int ret; 103393c47394SJó Ágila Bitsch u8 b_vendor_code; 103493c47394SJó Ágila Bitsch 103593c47394SJó Ágila Bitsch ret = kstrtou8(page, 0, &b_vendor_code); 10366f7fb48dSAndy Shevchenko if (ret) 10376f7fb48dSAndy Shevchenko return ret; 10386f7fb48dSAndy Shevchenko 10396f7fb48dSAndy Shevchenko mutex_lock(&gi->lock); 104093c47394SJó Ágila Bitsch gi->b_webusb_vendor_code = b_vendor_code; 104193c47394SJó Ágila Bitsch mutex_unlock(&gi->lock); 104293c47394SJó Ágila Bitsch 10436f7fb48dSAndy Shevchenko return len; 104493c47394SJó Ágila Bitsch } 104593c47394SJó Ágila Bitsch 104693c47394SJó Ágila Bitsch static ssize_t webusb_landingPage_show(struct config_item *item, char *page) 104793c47394SJó Ágila Bitsch { 104893c47394SJó Ágila Bitsch return sysfs_emit(page, "%s\n", webusb_item_to_gadget_info(item)->landing_page); 104993c47394SJó Ágila Bitsch } 105093c47394SJó Ágila Bitsch 105193c47394SJó Ágila Bitsch static ssize_t webusb_landingPage_store(struct config_item *item, const char *page, 105293c47394SJó Ágila Bitsch size_t len) 105393c47394SJó Ágila Bitsch { 105493c47394SJó Ágila Bitsch struct gadget_info *gi = webusb_item_to_gadget_info(item); 105593c47394SJó Ágila Bitsch unsigned int bytes_to_strip = 0; 105693c47394SJó Ágila Bitsch int l = len; 105793c47394SJó Ágila Bitsch 105893c47394SJó Ágila Bitsch if (page[l - 1] == '\n') { 105993c47394SJó Ágila Bitsch --l; 106093c47394SJó Ágila Bitsch ++bytes_to_strip; 106193c47394SJó Ágila Bitsch } 106293c47394SJó Ágila Bitsch 106393c47394SJó Ágila Bitsch if (l > sizeof(gi->landing_page)) { 106493c47394SJó Ágila Bitsch pr_err("webusb: landingPage URL too long\n"); 106593c47394SJó Ágila Bitsch return -EINVAL; 106693c47394SJó Ágila Bitsch } 106793c47394SJó Ágila Bitsch 106893c47394SJó Ágila Bitsch // validation 106993c47394SJó Ágila Bitsch if (strncasecmp(page, "https://", 8) == 0) 107093c47394SJó Ágila Bitsch bytes_to_strip = 8; 107193c47394SJó Ágila Bitsch else if (strncasecmp(page, "http://", 7) == 0) 107293c47394SJó Ágila Bitsch bytes_to_strip = 7; 107393c47394SJó Ágila Bitsch else 107493c47394SJó Ágila Bitsch bytes_to_strip = 0; 107593c47394SJó Ágila Bitsch 107693c47394SJó Ágila Bitsch if (l > U8_MAX - WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + bytes_to_strip) { 107793c47394SJó Ágila Bitsch pr_err("webusb: landingPage URL %d bytes too long for given URL scheme\n", 107893c47394SJó Ágila Bitsch l - U8_MAX + WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH - bytes_to_strip); 107993c47394SJó Ágila Bitsch return -EINVAL; 108093c47394SJó Ágila Bitsch } 108193c47394SJó Ágila Bitsch 108293c47394SJó Ágila Bitsch mutex_lock(&gi->lock); 108393c47394SJó Ágila Bitsch // ensure 0 bytes are set, in case the new landing page is shorter then the old one. 10849c0e6fbdSAndy Shevchenko memcpy_and_pad(gi->landing_page, sizeof(gi->landing_page), page, l, 0); 108593c47394SJó Ágila Bitsch mutex_unlock(&gi->lock); 108693c47394SJó Ágila Bitsch 108793c47394SJó Ágila Bitsch return len; 108893c47394SJó Ágila Bitsch } 108993c47394SJó Ágila Bitsch 109093c47394SJó Ágila Bitsch CONFIGFS_ATTR(webusb_, use); 109193c47394SJó Ágila Bitsch CONFIGFS_ATTR(webusb_, bVendorCode); 109293c47394SJó Ágila Bitsch CONFIGFS_ATTR(webusb_, bcdVersion); 109393c47394SJó Ágila Bitsch CONFIGFS_ATTR(webusb_, landingPage); 109493c47394SJó Ágila Bitsch 109593c47394SJó Ágila Bitsch static struct configfs_attribute *webusb_attrs[] = { 109693c47394SJó Ágila Bitsch &webusb_attr_use, 109793c47394SJó Ágila Bitsch &webusb_attr_bcdVersion, 109893c47394SJó Ágila Bitsch &webusb_attr_bVendorCode, 109993c47394SJó Ágila Bitsch &webusb_attr_landingPage, 110093c47394SJó Ágila Bitsch NULL, 110193c47394SJó Ágila Bitsch }; 110293c47394SJó Ágila Bitsch 110393c47394SJó Ágila Bitsch static struct config_item_type webusb_type = { 110493c47394SJó Ágila Bitsch .ct_attrs = webusb_attrs, 110593c47394SJó Ágila Bitsch .ct_owner = THIS_MODULE, 110693c47394SJó Ágila Bitsch }; 110793c47394SJó Ágila Bitsch 110845b6a73fSChristoph Hellwig static inline struct gadget_info *os_desc_item_to_gadget_info( 110945b6a73fSChristoph Hellwig struct config_item *item) 111087213d38SAndrzej Pietrasiewicz { 1111167a799cSLinyu Yuan return container_of(to_config_group(item), 1112167a799cSLinyu Yuan struct gadget_info, os_desc_group); 111387213d38SAndrzej Pietrasiewicz } 111487213d38SAndrzej Pietrasiewicz 111545b6a73fSChristoph Hellwig static ssize_t os_desc_use_show(struct config_item *item, char *page) 111645b6a73fSChristoph Hellwig { 1117e800e8cbSStefan Agner return sprintf(page, "%d\n", 111845b6a73fSChristoph Hellwig os_desc_item_to_gadget_info(item)->use_os_desc); 111945b6a73fSChristoph Hellwig } 112045b6a73fSChristoph Hellwig 112145b6a73fSChristoph Hellwig static ssize_t os_desc_use_store(struct config_item *item, const char *page, 112287213d38SAndrzej Pietrasiewicz size_t len) 112387213d38SAndrzej Pietrasiewicz { 112445b6a73fSChristoph Hellwig struct gadget_info *gi = os_desc_item_to_gadget_info(item); 112587213d38SAndrzej Pietrasiewicz int ret; 112687213d38SAndrzej Pietrasiewicz bool use; 112787213d38SAndrzej Pietrasiewicz 1128a8bc8cc1SChristophe JAILLET ret = kstrtobool(page, &use); 11296f7fb48dSAndy Shevchenko if (ret) 11306f7fb48dSAndy Shevchenko return ret; 11316f7fb48dSAndy Shevchenko 11326f7fb48dSAndy Shevchenko mutex_lock(&gi->lock); 113387213d38SAndrzej Pietrasiewicz gi->use_os_desc = use; 113487213d38SAndrzej Pietrasiewicz mutex_unlock(&gi->lock); 113587213d38SAndrzej Pietrasiewicz 11366f7fb48dSAndy Shevchenko return len; 113787213d38SAndrzej Pietrasiewicz } 113887213d38SAndrzej Pietrasiewicz 113945b6a73fSChristoph Hellwig static ssize_t os_desc_b_vendor_code_show(struct config_item *item, char *page) 114087213d38SAndrzej Pietrasiewicz { 1141e800e8cbSStefan Agner return sprintf(page, "0x%02x\n", 114245b6a73fSChristoph Hellwig os_desc_item_to_gadget_info(item)->b_vendor_code); 114387213d38SAndrzej Pietrasiewicz } 114487213d38SAndrzej Pietrasiewicz 114545b6a73fSChristoph Hellwig static ssize_t os_desc_b_vendor_code_store(struct config_item *item, 114687213d38SAndrzej Pietrasiewicz const char *page, size_t len) 114787213d38SAndrzej Pietrasiewicz { 114845b6a73fSChristoph Hellwig struct gadget_info *gi = os_desc_item_to_gadget_info(item); 114987213d38SAndrzej Pietrasiewicz int ret; 115087213d38SAndrzej Pietrasiewicz u8 b_vendor_code; 115187213d38SAndrzej Pietrasiewicz 115287213d38SAndrzej Pietrasiewicz ret = kstrtou8(page, 0, &b_vendor_code); 11536f7fb48dSAndy Shevchenko if (ret) 11546f7fb48dSAndy Shevchenko return ret; 11556f7fb48dSAndy Shevchenko 11566f7fb48dSAndy Shevchenko mutex_lock(&gi->lock); 115787213d38SAndrzej Pietrasiewicz gi->b_vendor_code = b_vendor_code; 115887213d38SAndrzej Pietrasiewicz mutex_unlock(&gi->lock); 115987213d38SAndrzej Pietrasiewicz 11606f7fb48dSAndy Shevchenko return len; 116187213d38SAndrzej Pietrasiewicz } 116287213d38SAndrzej Pietrasiewicz 116345b6a73fSChristoph Hellwig static ssize_t os_desc_qw_sign_show(struct config_item *item, char *page) 116487213d38SAndrzej Pietrasiewicz { 116545b6a73fSChristoph Hellwig struct gadget_info *gi = os_desc_item_to_gadget_info(item); 116676180d71SStefan Agner int res; 116787213d38SAndrzej Pietrasiewicz 116876180d71SStefan Agner res = utf16s_to_utf8s((wchar_t *) gi->qw_sign, OS_STRING_QW_SIGN_LEN, 116976180d71SStefan Agner UTF16_LITTLE_ENDIAN, page, PAGE_SIZE - 1); 117076180d71SStefan Agner page[res++] = '\n'; 117176180d71SStefan Agner 117276180d71SStefan Agner return res; 117387213d38SAndrzej Pietrasiewicz } 117487213d38SAndrzej Pietrasiewicz 117545b6a73fSChristoph Hellwig static ssize_t os_desc_qw_sign_store(struct config_item *item, const char *page, 117687213d38SAndrzej Pietrasiewicz size_t len) 117787213d38SAndrzej Pietrasiewicz { 117845b6a73fSChristoph Hellwig struct gadget_info *gi = os_desc_item_to_gadget_info(item); 117987213d38SAndrzej Pietrasiewicz int res, l; 118087213d38SAndrzej Pietrasiewicz 118187213d38SAndrzej Pietrasiewicz l = min((int)len, OS_STRING_QW_SIGN_LEN >> 1); 118287213d38SAndrzej Pietrasiewicz if (page[l - 1] == '\n') 118387213d38SAndrzej Pietrasiewicz --l; 118487213d38SAndrzej Pietrasiewicz 118587213d38SAndrzej Pietrasiewicz mutex_lock(&gi->lock); 118687213d38SAndrzej Pietrasiewicz res = utf8s_to_utf16s(page, l, 118787213d38SAndrzej Pietrasiewicz UTF16_LITTLE_ENDIAN, (wchar_t *) gi->qw_sign, 118887213d38SAndrzej Pietrasiewicz OS_STRING_QW_SIGN_LEN); 118987213d38SAndrzej Pietrasiewicz if (res > 0) 119087213d38SAndrzej Pietrasiewicz res = len; 119187213d38SAndrzej Pietrasiewicz mutex_unlock(&gi->lock); 119287213d38SAndrzej Pietrasiewicz 119387213d38SAndrzej Pietrasiewicz return res; 119487213d38SAndrzej Pietrasiewicz } 119587213d38SAndrzej Pietrasiewicz 119645b6a73fSChristoph Hellwig CONFIGFS_ATTR(os_desc_, use); 119745b6a73fSChristoph Hellwig CONFIGFS_ATTR(os_desc_, b_vendor_code); 119845b6a73fSChristoph Hellwig CONFIGFS_ATTR(os_desc_, qw_sign); 119987213d38SAndrzej Pietrasiewicz 120087213d38SAndrzej Pietrasiewicz static struct configfs_attribute *os_desc_attrs[] = { 120145b6a73fSChristoph Hellwig &os_desc_attr_use, 120245b6a73fSChristoph Hellwig &os_desc_attr_b_vendor_code, 120345b6a73fSChristoph Hellwig &os_desc_attr_qw_sign, 120487213d38SAndrzej Pietrasiewicz NULL, 120587213d38SAndrzej Pietrasiewicz }; 120687213d38SAndrzej Pietrasiewicz 1207da424314SAndrzej Pietrasiewicz static int os_desc_link(struct config_item *os_desc_ci, 1208da424314SAndrzej Pietrasiewicz struct config_item *usb_cfg_ci) 1209da424314SAndrzej Pietrasiewicz { 1210167a799cSLinyu Yuan struct gadget_info *gi = os_desc_item_to_gadget_info(os_desc_ci); 1211da424314SAndrzej Pietrasiewicz struct usb_composite_dev *cdev = &gi->cdev; 12125d143ec4SLinyu Yuan struct config_usb_cfg *c_target = to_config_usb_cfg(usb_cfg_ci); 121336f4c25cSJakob Koschel struct usb_configuration *c = NULL, *iter; 1214da424314SAndrzej Pietrasiewicz int ret; 1215da424314SAndrzej Pietrasiewicz 1216da424314SAndrzej Pietrasiewicz mutex_lock(&gi->lock); 121736f4c25cSJakob Koschel list_for_each_entry(iter, &cdev->configs, list) { 121836f4c25cSJakob Koschel if (iter != &c_target->c) 121936f4c25cSJakob Koschel continue; 122036f4c25cSJakob Koschel c = iter; 1221da424314SAndrzej Pietrasiewicz break; 1222da424314SAndrzej Pietrasiewicz } 122336f4c25cSJakob Koschel if (!c) { 1224da424314SAndrzej Pietrasiewicz ret = -EINVAL; 1225da424314SAndrzej Pietrasiewicz goto out; 1226da424314SAndrzej Pietrasiewicz } 1227da424314SAndrzej Pietrasiewicz 1228da424314SAndrzej Pietrasiewicz if (cdev->os_desc_config) { 1229da424314SAndrzej Pietrasiewicz ret = -EBUSY; 1230da424314SAndrzej Pietrasiewicz goto out; 1231da424314SAndrzej Pietrasiewicz } 1232da424314SAndrzej Pietrasiewicz 1233da424314SAndrzej Pietrasiewicz cdev->os_desc_config = &c_target->c; 1234da424314SAndrzej Pietrasiewicz ret = 0; 1235da424314SAndrzej Pietrasiewicz 1236da424314SAndrzej Pietrasiewicz out: 1237da424314SAndrzej Pietrasiewicz mutex_unlock(&gi->lock); 1238da424314SAndrzej Pietrasiewicz return ret; 1239da424314SAndrzej Pietrasiewicz } 1240da424314SAndrzej Pietrasiewicz 1241e16769d4SAndrzej Pietrasiewicz static void os_desc_unlink(struct config_item *os_desc_ci, 1242da424314SAndrzej Pietrasiewicz struct config_item *usb_cfg_ci) 1243da424314SAndrzej Pietrasiewicz { 1244167a799cSLinyu Yuan struct gadget_info *gi = os_desc_item_to_gadget_info(os_desc_ci); 1245da424314SAndrzej Pietrasiewicz struct usb_composite_dev *cdev = &gi->cdev; 1246da424314SAndrzej Pietrasiewicz 1247da424314SAndrzej Pietrasiewicz mutex_lock(&gi->lock); 1248afdaadc3SRuslan Bilovol if (gi->composite.gadget_driver.udc_name) 1249da424314SAndrzej Pietrasiewicz unregister_gadget(gi); 1250da424314SAndrzej Pietrasiewicz cdev->os_desc_config = NULL; 1251afdaadc3SRuslan Bilovol WARN_ON(gi->composite.gadget_driver.udc_name); 1252da424314SAndrzej Pietrasiewicz mutex_unlock(&gi->lock); 1253da424314SAndrzej Pietrasiewicz } 1254da424314SAndrzej Pietrasiewicz 125587213d38SAndrzej Pietrasiewicz static struct configfs_item_operations os_desc_ops = { 1256da424314SAndrzej Pietrasiewicz .allow_link = os_desc_link, 1257da424314SAndrzej Pietrasiewicz .drop_link = os_desc_unlink, 125887213d38SAndrzej Pietrasiewicz }; 125987213d38SAndrzej Pietrasiewicz 126087213d38SAndrzej Pietrasiewicz static struct config_item_type os_desc_type = { 126187213d38SAndrzej Pietrasiewicz .ct_item_ops = &os_desc_ops, 126287213d38SAndrzej Pietrasiewicz .ct_attrs = os_desc_attrs, 126387213d38SAndrzej Pietrasiewicz .ct_owner = THIS_MODULE, 126487213d38SAndrzej Pietrasiewicz }; 126587213d38SAndrzej Pietrasiewicz 12667419485fSAndrzej Pietrasiewicz static inline struct usb_os_desc_ext_prop 12677419485fSAndrzej Pietrasiewicz *to_usb_os_desc_ext_prop(struct config_item *item) 12687419485fSAndrzej Pietrasiewicz { 12697419485fSAndrzej Pietrasiewicz return container_of(item, struct usb_os_desc_ext_prop, item); 12707419485fSAndrzej Pietrasiewicz } 12717419485fSAndrzej Pietrasiewicz 127245b6a73fSChristoph Hellwig static ssize_t ext_prop_type_show(struct config_item *item, char *page) 12737419485fSAndrzej Pietrasiewicz { 1274e800e8cbSStefan Agner return sprintf(page, "%d\n", to_usb_os_desc_ext_prop(item)->type); 12757419485fSAndrzej Pietrasiewicz } 12767419485fSAndrzej Pietrasiewicz 127745b6a73fSChristoph Hellwig static ssize_t ext_prop_type_store(struct config_item *item, 12787419485fSAndrzej Pietrasiewicz const char *page, size_t len) 12797419485fSAndrzej Pietrasiewicz { 128045b6a73fSChristoph Hellwig struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); 12817419485fSAndrzej Pietrasiewicz struct usb_os_desc *desc = to_usb_os_desc(ext_prop->item.ci_parent); 12827419485fSAndrzej Pietrasiewicz u8 type; 12837419485fSAndrzej Pietrasiewicz int ret; 12847419485fSAndrzej Pietrasiewicz 12857419485fSAndrzej Pietrasiewicz ret = kstrtou8(page, 0, &type); 12867419485fSAndrzej Pietrasiewicz if (ret) 12876f7fb48dSAndy Shevchenko return ret; 12886f7fb48dSAndy Shevchenko 12896f7fb48dSAndy Shevchenko if (type < USB_EXT_PROP_UNICODE || type > USB_EXT_PROP_UNICODE_MULTI) 12906f7fb48dSAndy Shevchenko return -EINVAL; 12916f7fb48dSAndy Shevchenko 12926f7fb48dSAndy Shevchenko if (desc->opts_mutex) 12936f7fb48dSAndy Shevchenko mutex_lock(desc->opts_mutex); 12947419485fSAndrzej Pietrasiewicz 12957419485fSAndrzej Pietrasiewicz if ((ext_prop->type == USB_EXT_PROP_BINARY || 12967419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_LE32 || 12977419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_BE32) && 12987419485fSAndrzej Pietrasiewicz (type == USB_EXT_PROP_UNICODE || 12997419485fSAndrzej Pietrasiewicz type == USB_EXT_PROP_UNICODE_ENV || 13007419485fSAndrzej Pietrasiewicz type == USB_EXT_PROP_UNICODE_LINK)) 13017419485fSAndrzej Pietrasiewicz ext_prop->data_len <<= 1; 13027419485fSAndrzej Pietrasiewicz else if ((ext_prop->type == USB_EXT_PROP_UNICODE || 13037419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_UNICODE_ENV || 13047419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_UNICODE_LINK) && 13057419485fSAndrzej Pietrasiewicz (type == USB_EXT_PROP_BINARY || 13067419485fSAndrzej Pietrasiewicz type == USB_EXT_PROP_LE32 || 13077419485fSAndrzej Pietrasiewicz type == USB_EXT_PROP_BE32)) 13087419485fSAndrzej Pietrasiewicz ext_prop->data_len >>= 1; 13097419485fSAndrzej Pietrasiewicz ext_prop->type = type; 13107419485fSAndrzej Pietrasiewicz 13117419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 13127419485fSAndrzej Pietrasiewicz mutex_unlock(desc->opts_mutex); 13136f7fb48dSAndy Shevchenko return len; 13147419485fSAndrzej Pietrasiewicz } 13157419485fSAndrzej Pietrasiewicz 131645b6a73fSChristoph Hellwig static ssize_t ext_prop_data_show(struct config_item *item, char *page) 13177419485fSAndrzej Pietrasiewicz { 131845b6a73fSChristoph Hellwig struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); 13197419485fSAndrzej Pietrasiewicz int len = ext_prop->data_len; 13207419485fSAndrzej Pietrasiewicz 13217419485fSAndrzej Pietrasiewicz if (ext_prop->type == USB_EXT_PROP_UNICODE || 13227419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_UNICODE_ENV || 13237419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_UNICODE_LINK) 13247419485fSAndrzej Pietrasiewicz len >>= 1; 13257419485fSAndrzej Pietrasiewicz memcpy(page, ext_prop->data, len); 13267419485fSAndrzej Pietrasiewicz 13277419485fSAndrzej Pietrasiewicz return len; 13287419485fSAndrzej Pietrasiewicz } 13297419485fSAndrzej Pietrasiewicz 133045b6a73fSChristoph Hellwig static ssize_t ext_prop_data_store(struct config_item *item, 13317419485fSAndrzej Pietrasiewicz const char *page, size_t len) 13327419485fSAndrzej Pietrasiewicz { 133345b6a73fSChristoph Hellwig struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); 13347419485fSAndrzej Pietrasiewicz struct usb_os_desc *desc = to_usb_os_desc(ext_prop->item.ci_parent); 13357419485fSAndrzej Pietrasiewicz char *new_data; 13367419485fSAndrzej Pietrasiewicz size_t ret_len = len; 13377419485fSAndrzej Pietrasiewicz 13387419485fSAndrzej Pietrasiewicz if (page[len - 1] == '\n' || page[len - 1] == '\0') 13397419485fSAndrzej Pietrasiewicz --len; 134058b949e0SBenoit Taine new_data = kmemdup(page, len, GFP_KERNEL); 13417419485fSAndrzej Pietrasiewicz if (!new_data) 13427419485fSAndrzej Pietrasiewicz return -ENOMEM; 13437419485fSAndrzej Pietrasiewicz 13447419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 13457419485fSAndrzej Pietrasiewicz mutex_lock(desc->opts_mutex); 13467419485fSAndrzej Pietrasiewicz kfree(ext_prop->data); 13477419485fSAndrzej Pietrasiewicz ext_prop->data = new_data; 13487419485fSAndrzej Pietrasiewicz desc->ext_prop_len -= ext_prop->data_len; 13497419485fSAndrzej Pietrasiewicz ext_prop->data_len = len; 13507419485fSAndrzej Pietrasiewicz desc->ext_prop_len += ext_prop->data_len; 13517419485fSAndrzej Pietrasiewicz if (ext_prop->type == USB_EXT_PROP_UNICODE || 13527419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_UNICODE_ENV || 13537419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_UNICODE_LINK) { 13547419485fSAndrzej Pietrasiewicz desc->ext_prop_len -= ext_prop->data_len; 13557419485fSAndrzej Pietrasiewicz ext_prop->data_len <<= 1; 13567419485fSAndrzej Pietrasiewicz ext_prop->data_len += 2; 13577419485fSAndrzej Pietrasiewicz desc->ext_prop_len += ext_prop->data_len; 13587419485fSAndrzej Pietrasiewicz } 13597419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 13607419485fSAndrzej Pietrasiewicz mutex_unlock(desc->opts_mutex); 13617419485fSAndrzej Pietrasiewicz return ret_len; 13627419485fSAndrzej Pietrasiewicz } 13637419485fSAndrzej Pietrasiewicz 136445b6a73fSChristoph Hellwig CONFIGFS_ATTR(ext_prop_, type); 136545b6a73fSChristoph Hellwig CONFIGFS_ATTR(ext_prop_, data); 13667419485fSAndrzej Pietrasiewicz 13677419485fSAndrzej Pietrasiewicz static struct configfs_attribute *ext_prop_attrs[] = { 136845b6a73fSChristoph Hellwig &ext_prop_attr_type, 136945b6a73fSChristoph Hellwig &ext_prop_attr_data, 13707419485fSAndrzej Pietrasiewicz NULL, 13717419485fSAndrzej Pietrasiewicz }; 13727419485fSAndrzej Pietrasiewicz 13737419485fSAndrzej Pietrasiewicz static void usb_os_desc_ext_prop_release(struct config_item *item) 13747419485fSAndrzej Pietrasiewicz { 13757419485fSAndrzej Pietrasiewicz struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); 13767419485fSAndrzej Pietrasiewicz 13777419485fSAndrzej Pietrasiewicz kfree(ext_prop); /* frees a whole chunk */ 13787419485fSAndrzej Pietrasiewicz } 13797419485fSAndrzej Pietrasiewicz 13807419485fSAndrzej Pietrasiewicz static struct configfs_item_operations ext_prop_ops = { 13817419485fSAndrzej Pietrasiewicz .release = usb_os_desc_ext_prop_release, 13827419485fSAndrzej Pietrasiewicz }; 13837419485fSAndrzej Pietrasiewicz 13847419485fSAndrzej Pietrasiewicz static struct config_item *ext_prop_make( 13857419485fSAndrzej Pietrasiewicz struct config_group *group, 13867419485fSAndrzej Pietrasiewicz const char *name) 13877419485fSAndrzej Pietrasiewicz { 13887419485fSAndrzej Pietrasiewicz struct usb_os_desc_ext_prop *ext_prop; 13897419485fSAndrzej Pietrasiewicz struct config_item_type *ext_prop_type; 13907419485fSAndrzej Pietrasiewicz struct usb_os_desc *desc; 13917419485fSAndrzej Pietrasiewicz char *vlabuf; 13927419485fSAndrzej Pietrasiewicz 13937419485fSAndrzej Pietrasiewicz vla_group(data_chunk); 13947419485fSAndrzej Pietrasiewicz vla_item(data_chunk, struct usb_os_desc_ext_prop, ext_prop, 1); 13957419485fSAndrzej Pietrasiewicz vla_item(data_chunk, struct config_item_type, ext_prop_type, 1); 13967419485fSAndrzej Pietrasiewicz 13977419485fSAndrzej Pietrasiewicz vlabuf = kzalloc(vla_group_size(data_chunk), GFP_KERNEL); 13987419485fSAndrzej Pietrasiewicz if (!vlabuf) 13997419485fSAndrzej Pietrasiewicz return ERR_PTR(-ENOMEM); 14007419485fSAndrzej Pietrasiewicz 14017419485fSAndrzej Pietrasiewicz ext_prop = vla_ptr(vlabuf, data_chunk, ext_prop); 14027419485fSAndrzej Pietrasiewicz ext_prop_type = vla_ptr(vlabuf, data_chunk, ext_prop_type); 14037419485fSAndrzej Pietrasiewicz 14047419485fSAndrzej Pietrasiewicz desc = container_of(group, struct usb_os_desc, group); 14057419485fSAndrzej Pietrasiewicz ext_prop_type->ct_item_ops = &ext_prop_ops; 14067419485fSAndrzej Pietrasiewicz ext_prop_type->ct_attrs = ext_prop_attrs; 14077419485fSAndrzej Pietrasiewicz ext_prop_type->ct_owner = desc->owner; 14087419485fSAndrzej Pietrasiewicz 14097419485fSAndrzej Pietrasiewicz config_item_init_type_name(&ext_prop->item, name, ext_prop_type); 14107419485fSAndrzej Pietrasiewicz 14117419485fSAndrzej Pietrasiewicz ext_prop->name = kstrdup(name, GFP_KERNEL); 14127419485fSAndrzej Pietrasiewicz if (!ext_prop->name) { 14137419485fSAndrzej Pietrasiewicz kfree(vlabuf); 14147419485fSAndrzej Pietrasiewicz return ERR_PTR(-ENOMEM); 14157419485fSAndrzej Pietrasiewicz } 14167419485fSAndrzej Pietrasiewicz desc->ext_prop_len += 14; 14177419485fSAndrzej Pietrasiewicz ext_prop->name_len = 2 * strlen(ext_prop->name) + 2; 14187419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 14197419485fSAndrzej Pietrasiewicz mutex_lock(desc->opts_mutex); 14207419485fSAndrzej Pietrasiewicz desc->ext_prop_len += ext_prop->name_len; 14217419485fSAndrzej Pietrasiewicz list_add_tail(&ext_prop->entry, &desc->ext_prop); 14227419485fSAndrzej Pietrasiewicz ++desc->ext_prop_count; 14237419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 14247419485fSAndrzej Pietrasiewicz mutex_unlock(desc->opts_mutex); 14257419485fSAndrzej Pietrasiewicz 14267419485fSAndrzej Pietrasiewicz return &ext_prop->item; 14277419485fSAndrzej Pietrasiewicz } 14287419485fSAndrzej Pietrasiewicz 14297419485fSAndrzej Pietrasiewicz static void ext_prop_drop(struct config_group *group, struct config_item *item) 14307419485fSAndrzej Pietrasiewicz { 14317419485fSAndrzej Pietrasiewicz struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); 14327419485fSAndrzej Pietrasiewicz struct usb_os_desc *desc = to_usb_os_desc(&group->cg_item); 14337419485fSAndrzej Pietrasiewicz 14347419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 14357419485fSAndrzej Pietrasiewicz mutex_lock(desc->opts_mutex); 14367419485fSAndrzej Pietrasiewicz list_del(&ext_prop->entry); 14377419485fSAndrzej Pietrasiewicz --desc->ext_prop_count; 14387419485fSAndrzej Pietrasiewicz kfree(ext_prop->name); 14397419485fSAndrzej Pietrasiewicz desc->ext_prop_len -= (ext_prop->name_len + ext_prop->data_len + 14); 14407419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 14417419485fSAndrzej Pietrasiewicz mutex_unlock(desc->opts_mutex); 14427419485fSAndrzej Pietrasiewicz config_item_put(item); 14437419485fSAndrzej Pietrasiewicz } 14447419485fSAndrzej Pietrasiewicz 14457419485fSAndrzej Pietrasiewicz static struct configfs_group_operations interf_grp_ops = { 14467419485fSAndrzej Pietrasiewicz .make_item = &ext_prop_make, 14477419485fSAndrzej Pietrasiewicz .drop_item = &ext_prop_drop, 14487419485fSAndrzej Pietrasiewicz }; 14497419485fSAndrzej Pietrasiewicz 145045b6a73fSChristoph Hellwig static ssize_t interf_grp_compatible_id_show(struct config_item *item, 1451da424314SAndrzej Pietrasiewicz char *page) 1452da424314SAndrzej Pietrasiewicz { 145345b6a73fSChristoph Hellwig memcpy(page, to_usb_os_desc(item)->ext_compat_id, 8); 1454da424314SAndrzej Pietrasiewicz return 8; 1455da424314SAndrzej Pietrasiewicz } 1456da424314SAndrzej Pietrasiewicz 145745b6a73fSChristoph Hellwig static ssize_t interf_grp_compatible_id_store(struct config_item *item, 1458da424314SAndrzej Pietrasiewicz const char *page, size_t len) 1459da424314SAndrzej Pietrasiewicz { 146045b6a73fSChristoph Hellwig struct usb_os_desc *desc = to_usb_os_desc(item); 1461da424314SAndrzej Pietrasiewicz int l; 1462da424314SAndrzej Pietrasiewicz 1463da424314SAndrzej Pietrasiewicz l = min_t(int, 8, len); 1464da424314SAndrzej Pietrasiewicz if (page[l - 1] == '\n') 1465da424314SAndrzej Pietrasiewicz --l; 1466da424314SAndrzej Pietrasiewicz if (desc->opts_mutex) 1467da424314SAndrzej Pietrasiewicz mutex_lock(desc->opts_mutex); 1468da424314SAndrzej Pietrasiewicz memcpy(desc->ext_compat_id, page, l); 1469da424314SAndrzej Pietrasiewicz 1470da424314SAndrzej Pietrasiewicz if (desc->opts_mutex) 1471da424314SAndrzej Pietrasiewicz mutex_unlock(desc->opts_mutex); 1472da424314SAndrzej Pietrasiewicz 1473da424314SAndrzej Pietrasiewicz return len; 1474da424314SAndrzej Pietrasiewicz } 1475da424314SAndrzej Pietrasiewicz 147645b6a73fSChristoph Hellwig static ssize_t interf_grp_sub_compatible_id_show(struct config_item *item, 1477da424314SAndrzej Pietrasiewicz char *page) 1478da424314SAndrzej Pietrasiewicz { 147945b6a73fSChristoph Hellwig memcpy(page, to_usb_os_desc(item)->ext_compat_id + 8, 8); 1480da424314SAndrzej Pietrasiewicz return 8; 1481da424314SAndrzej Pietrasiewicz } 1482da424314SAndrzej Pietrasiewicz 148345b6a73fSChristoph Hellwig static ssize_t interf_grp_sub_compatible_id_store(struct config_item *item, 1484da424314SAndrzej Pietrasiewicz const char *page, size_t len) 1485da424314SAndrzej Pietrasiewicz { 148645b6a73fSChristoph Hellwig struct usb_os_desc *desc = to_usb_os_desc(item); 1487da424314SAndrzej Pietrasiewicz int l; 1488da424314SAndrzej Pietrasiewicz 1489da424314SAndrzej Pietrasiewicz l = min_t(int, 8, len); 1490da424314SAndrzej Pietrasiewicz if (page[l - 1] == '\n') 1491da424314SAndrzej Pietrasiewicz --l; 1492da424314SAndrzej Pietrasiewicz if (desc->opts_mutex) 1493da424314SAndrzej Pietrasiewicz mutex_lock(desc->opts_mutex); 1494da424314SAndrzej Pietrasiewicz memcpy(desc->ext_compat_id + 8, page, l); 1495da424314SAndrzej Pietrasiewicz 1496da424314SAndrzej Pietrasiewicz if (desc->opts_mutex) 1497da424314SAndrzej Pietrasiewicz mutex_unlock(desc->opts_mutex); 1498da424314SAndrzej Pietrasiewicz 1499da424314SAndrzej Pietrasiewicz return len; 1500da424314SAndrzej Pietrasiewicz } 1501da424314SAndrzej Pietrasiewicz 150245b6a73fSChristoph Hellwig CONFIGFS_ATTR(interf_grp_, compatible_id); 150345b6a73fSChristoph Hellwig CONFIGFS_ATTR(interf_grp_, sub_compatible_id); 1504da424314SAndrzej Pietrasiewicz 1505da424314SAndrzej Pietrasiewicz static struct configfs_attribute *interf_grp_attrs[] = { 150645b6a73fSChristoph Hellwig &interf_grp_attr_compatible_id, 150745b6a73fSChristoph Hellwig &interf_grp_attr_sub_compatible_id, 1508da424314SAndrzej Pietrasiewicz NULL 1509da424314SAndrzej Pietrasiewicz }; 1510da424314SAndrzej Pietrasiewicz 1511ff74745eSAndrew Gabbasov struct config_group *usb_os_desc_prepare_interf_dir( 1512ff74745eSAndrew Gabbasov struct config_group *parent, 1513da424314SAndrzej Pietrasiewicz int n_interf, 1514da424314SAndrzej Pietrasiewicz struct usb_os_desc **desc, 151514574b54SAndrzej Pietrasiewicz char **names, 1516da424314SAndrzej Pietrasiewicz struct module *owner) 1517da424314SAndrzej Pietrasiewicz { 15181ae1602dSChristoph Hellwig struct config_group *os_desc_group; 1519da424314SAndrzej Pietrasiewicz struct config_item_type *os_desc_type, *interface_type; 1520da424314SAndrzej Pietrasiewicz 1521da424314SAndrzej Pietrasiewicz vla_group(data_chunk); 1522da424314SAndrzej Pietrasiewicz vla_item(data_chunk, struct config_group, os_desc_group, 1); 1523da424314SAndrzej Pietrasiewicz vla_item(data_chunk, struct config_item_type, os_desc_type, 1); 1524da424314SAndrzej Pietrasiewicz vla_item(data_chunk, struct config_item_type, interface_type, 1); 1525da424314SAndrzej Pietrasiewicz 1526da424314SAndrzej Pietrasiewicz char *vlabuf = kzalloc(vla_group_size(data_chunk), GFP_KERNEL); 1527da424314SAndrzej Pietrasiewicz if (!vlabuf) 1528ff74745eSAndrew Gabbasov return ERR_PTR(-ENOMEM); 1529da424314SAndrzej Pietrasiewicz 1530da424314SAndrzej Pietrasiewicz os_desc_group = vla_ptr(vlabuf, data_chunk, os_desc_group); 1531da424314SAndrzej Pietrasiewicz os_desc_type = vla_ptr(vlabuf, data_chunk, os_desc_type); 1532da424314SAndrzej Pietrasiewicz interface_type = vla_ptr(vlabuf, data_chunk, interface_type); 1533da424314SAndrzej Pietrasiewicz 1534da424314SAndrzej Pietrasiewicz os_desc_type->ct_owner = owner; 1535da424314SAndrzej Pietrasiewicz config_group_init_type_name(os_desc_group, "os_desc", os_desc_type); 15361ae1602dSChristoph Hellwig configfs_add_default_group(os_desc_group, parent); 1537da424314SAndrzej Pietrasiewicz 15387419485fSAndrzej Pietrasiewicz interface_type->ct_group_ops = &interf_grp_ops; 1539da424314SAndrzej Pietrasiewicz interface_type->ct_attrs = interf_grp_attrs; 1540da424314SAndrzej Pietrasiewicz interface_type->ct_owner = owner; 1541da424314SAndrzej Pietrasiewicz 1542da424314SAndrzej Pietrasiewicz while (n_interf--) { 1543da424314SAndrzej Pietrasiewicz struct usb_os_desc *d; 1544da424314SAndrzej Pietrasiewicz 1545da424314SAndrzej Pietrasiewicz d = desc[n_interf]; 15467419485fSAndrzej Pietrasiewicz d->owner = owner; 1547da424314SAndrzej Pietrasiewicz config_group_init_type_name(&d->group, "", interface_type); 154814574b54SAndrzej Pietrasiewicz config_item_set_name(&d->group.cg_item, "interface.%s", 154914574b54SAndrzej Pietrasiewicz names[n_interf]); 15501ae1602dSChristoph Hellwig configfs_add_default_group(&d->group, os_desc_group); 1551da424314SAndrzej Pietrasiewicz } 1552da424314SAndrzej Pietrasiewicz 1553ff74745eSAndrew Gabbasov return os_desc_group; 1554da424314SAndrzej Pietrasiewicz } 1555da424314SAndrzej Pietrasiewicz EXPORT_SYMBOL(usb_os_desc_prepare_interf_dir); 1556da424314SAndrzej Pietrasiewicz 155788af8bbeSSebastian Andrzej Siewior static int configfs_do_nothing(struct usb_composite_dev *cdev) 155888af8bbeSSebastian Andrzej Siewior { 155975bfe23aSDavid Rientjes WARN_ON(1); 156088af8bbeSSebastian Andrzej Siewior return -EINVAL; 156188af8bbeSSebastian Andrzej Siewior } 156288af8bbeSSebastian Andrzej Siewior 156388af8bbeSSebastian Andrzej Siewior int composite_dev_prepare(struct usb_composite_driver *composite, 156488af8bbeSSebastian Andrzej Siewior struct usb_composite_dev *dev); 156588af8bbeSSebastian Andrzej Siewior 1566da424314SAndrzej Pietrasiewicz int composite_os_desc_req_prepare(struct usb_composite_dev *cdev, 1567da424314SAndrzej Pietrasiewicz struct usb_ep *ep0); 1568da424314SAndrzej Pietrasiewicz 156988af8bbeSSebastian Andrzej Siewior static void purge_configs_funcs(struct gadget_info *gi) 157088af8bbeSSebastian Andrzej Siewior { 157188af8bbeSSebastian Andrzej Siewior struct usb_configuration *c; 157288af8bbeSSebastian Andrzej Siewior 157388af8bbeSSebastian Andrzej Siewior list_for_each_entry(c, &gi->cdev.configs, list) { 157488af8bbeSSebastian Andrzej Siewior struct usb_function *f, *tmp; 157588af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg; 157688af8bbeSSebastian Andrzej Siewior 157788af8bbeSSebastian Andrzej Siewior cfg = container_of(c, struct config_usb_cfg, c); 157888af8bbeSSebastian Andrzej Siewior 15796cd0fe91SChandana Kishori Chiluveru list_for_each_entry_safe_reverse(f, tmp, &c->functions, list) { 158088af8bbeSSebastian Andrzej Siewior 15816cd0fe91SChandana Kishori Chiluveru list_move(&f->list, &cfg->func_list); 158288af8bbeSSebastian Andrzej Siewior if (f->unbind) { 1583da7b895dSRomain Izard dev_dbg(&gi->cdev.gadget->dev, 1584a08f5dbfSRomain Izard "unbind function '%s'/%p\n", 1585a08f5dbfSRomain Izard f->name, f); 158688af8bbeSSebastian Andrzej Siewior f->unbind(c, f); 158788af8bbeSSebastian Andrzej Siewior } 158888af8bbeSSebastian Andrzej Siewior } 158988af8bbeSSebastian Andrzej Siewior c->next_interface_id = 0; 1590903124feSKrzysztof Opasiak memset(c->interface, 0, sizeof(c->interface)); 1591554eead5SJohn Youn c->superspeed_plus = 0; 159288af8bbeSSebastian Andrzej Siewior c->superspeed = 0; 159388af8bbeSSebastian Andrzej Siewior c->highspeed = 0; 159488af8bbeSSebastian Andrzej Siewior c->fullspeed = 0; 159588af8bbeSSebastian Andrzej Siewior } 159688af8bbeSSebastian Andrzej Siewior } 159788af8bbeSSebastian Andrzej Siewior 1598c0335632SDaniel Scally static struct usb_string * 1599c0335632SDaniel Scally configfs_attach_gadget_strings(struct gadget_info *gi) 1600c0335632SDaniel Scally { 1601c0335632SDaniel Scally struct usb_gadget_strings **gadget_strings; 1602c0335632SDaniel Scally struct gadget_language *language; 1603c0335632SDaniel Scally struct gadget_string *string; 1604c0335632SDaniel Scally unsigned int nlangs = 0; 1605c0335632SDaniel Scally struct list_head *iter; 1606c0335632SDaniel Scally struct usb_string *us; 1607c0335632SDaniel Scally unsigned int i = 0; 1608c0335632SDaniel Scally int nstrings = -1; 1609c0335632SDaniel Scally unsigned int j; 1610c0335632SDaniel Scally 1611c0335632SDaniel Scally list_for_each(iter, &gi->string_list) 1612c0335632SDaniel Scally nlangs++; 1613c0335632SDaniel Scally 1614c0335632SDaniel Scally /* Bail out early if no languages are configured */ 1615c0335632SDaniel Scally if (!nlangs) 1616c0335632SDaniel Scally return NULL; 1617c0335632SDaniel Scally 1618c0335632SDaniel Scally gadget_strings = kcalloc(nlangs + 1, /* including NULL terminator */ 1619c0335632SDaniel Scally sizeof(struct usb_gadget_strings *), GFP_KERNEL); 1620c0335632SDaniel Scally if (!gadget_strings) 1621c0335632SDaniel Scally return ERR_PTR(-ENOMEM); 1622c0335632SDaniel Scally 1623c0335632SDaniel Scally list_for_each_entry(language, &gi->string_list, list) { 1624c0335632SDaniel Scally struct usb_string *stringtab; 1625c0335632SDaniel Scally 1626c0335632SDaniel Scally if (nstrings == -1) { 1627c0335632SDaniel Scally nstrings = language->nstrings; 1628c0335632SDaniel Scally } else if (nstrings != language->nstrings) { 1629c0335632SDaniel Scally pr_err("languages must contain the same number of strings\n"); 1630c0335632SDaniel Scally us = ERR_PTR(-EINVAL); 1631c0335632SDaniel Scally goto cleanup; 1632c0335632SDaniel Scally } 1633c0335632SDaniel Scally 1634c0335632SDaniel Scally stringtab = kcalloc(language->nstrings + 1, sizeof(struct usb_string), 1635c0335632SDaniel Scally GFP_KERNEL); 1636c0335632SDaniel Scally if (!stringtab) { 1637c0335632SDaniel Scally us = ERR_PTR(-ENOMEM); 1638c0335632SDaniel Scally goto cleanup; 1639c0335632SDaniel Scally } 1640c0335632SDaniel Scally 1641c0335632SDaniel Scally stringtab[USB_GADGET_MANUFACTURER_IDX].id = USB_GADGET_MANUFACTURER_IDX; 1642c0335632SDaniel Scally stringtab[USB_GADGET_MANUFACTURER_IDX].s = language->manufacturer; 1643c0335632SDaniel Scally stringtab[USB_GADGET_PRODUCT_IDX].id = USB_GADGET_PRODUCT_IDX; 1644c0335632SDaniel Scally stringtab[USB_GADGET_PRODUCT_IDX].s = language->product; 1645c0335632SDaniel Scally stringtab[USB_GADGET_SERIAL_IDX].id = USB_GADGET_SERIAL_IDX; 1646c0335632SDaniel Scally stringtab[USB_GADGET_SERIAL_IDX].s = language->serialnumber; 1647c0335632SDaniel Scally 1648c0335632SDaniel Scally j = USB_GADGET_FIRST_AVAIL_IDX; 1649c0335632SDaniel Scally list_for_each_entry(string, &language->gadget_strings, list) { 1650c0335632SDaniel Scally memcpy(&stringtab[j], &string->usb_string, sizeof(struct usb_string)); 1651c0335632SDaniel Scally j++; 1652c0335632SDaniel Scally } 1653c0335632SDaniel Scally 1654c0335632SDaniel Scally language->stringtab_dev.strings = stringtab; 1655c0335632SDaniel Scally gadget_strings[i] = &language->stringtab_dev; 1656c0335632SDaniel Scally i++; 1657c0335632SDaniel Scally } 1658c0335632SDaniel Scally 1659c0335632SDaniel Scally us = usb_gstrings_attach(&gi->cdev, gadget_strings, nstrings); 1660c0335632SDaniel Scally 1661c0335632SDaniel Scally cleanup: 1662c0335632SDaniel Scally list_for_each_entry(language, &gi->string_list, list) { 1663c0335632SDaniel Scally kfree(language->stringtab_dev.strings); 1664c0335632SDaniel Scally language->stringtab_dev.strings = NULL; 1665c0335632SDaniel Scally } 1666c0335632SDaniel Scally 1667c0335632SDaniel Scally kfree(gadget_strings); 1668c0335632SDaniel Scally 1669c0335632SDaniel Scally return us; 1670c0335632SDaniel Scally } 1671c0335632SDaniel Scally 167288af8bbeSSebastian Andrzej Siewior static int configfs_composite_bind(struct usb_gadget *gadget, 167388af8bbeSSebastian Andrzej Siewior struct usb_gadget_driver *gdriver) 167488af8bbeSSebastian Andrzej Siewior { 167588af8bbeSSebastian Andrzej Siewior struct usb_composite_driver *composite = to_cdriver(gdriver); 167688af8bbeSSebastian Andrzej Siewior struct gadget_info *gi = container_of(composite, 167788af8bbeSSebastian Andrzej Siewior struct gadget_info, composite); 167888af8bbeSSebastian Andrzej Siewior struct usb_composite_dev *cdev = &gi->cdev; 167988af8bbeSSebastian Andrzej Siewior struct usb_configuration *c; 168088af8bbeSSebastian Andrzej Siewior struct usb_string *s; 168188af8bbeSSebastian Andrzej Siewior unsigned i; 168288af8bbeSSebastian Andrzej Siewior int ret; 168388af8bbeSSebastian Andrzej Siewior 168488af8bbeSSebastian Andrzej Siewior /* the gi->lock is hold by the caller */ 16851a1c851bSPeter Chen gi->unbind = 0; 168688af8bbeSSebastian Andrzej Siewior cdev->gadget = gadget; 168788af8bbeSSebastian Andrzej Siewior set_gadget_data(gadget, cdev); 168888af8bbeSSebastian Andrzej Siewior ret = composite_dev_prepare(composite, cdev); 168988af8bbeSSebastian Andrzej Siewior if (ret) 169088af8bbeSSebastian Andrzej Siewior return ret; 169188af8bbeSSebastian Andrzej Siewior /* and now the gadget bind */ 169288af8bbeSSebastian Andrzej Siewior ret = -EINVAL; 169388af8bbeSSebastian Andrzej Siewior 169488af8bbeSSebastian Andrzej Siewior if (list_empty(&gi->cdev.configs)) { 169588af8bbeSSebastian Andrzej Siewior pr_err("Need at least one configuration in %s.\n", 169688af8bbeSSebastian Andrzej Siewior gi->composite.name); 169788af8bbeSSebastian Andrzej Siewior goto err_comp_cleanup; 169888af8bbeSSebastian Andrzej Siewior } 169988af8bbeSSebastian Andrzej Siewior 170088af8bbeSSebastian Andrzej Siewior 170188af8bbeSSebastian Andrzej Siewior list_for_each_entry(c, &gi->cdev.configs, list) { 170288af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg; 170388af8bbeSSebastian Andrzej Siewior 170488af8bbeSSebastian Andrzej Siewior cfg = container_of(c, struct config_usb_cfg, c); 170588af8bbeSSebastian Andrzej Siewior if (list_empty(&cfg->func_list)) { 170688af8bbeSSebastian Andrzej Siewior pr_err("Config %s/%d of %s needs at least one function.\n", 170788af8bbeSSebastian Andrzej Siewior c->label, c->bConfigurationValue, 170888af8bbeSSebastian Andrzej Siewior gi->composite.name); 170988af8bbeSSebastian Andrzej Siewior goto err_comp_cleanup; 171088af8bbeSSebastian Andrzej Siewior } 171188af8bbeSSebastian Andrzej Siewior } 171288af8bbeSSebastian Andrzej Siewior 171388af8bbeSSebastian Andrzej Siewior /* init all strings */ 171488af8bbeSSebastian Andrzej Siewior if (!list_empty(&gi->string_list)) { 1715c0335632SDaniel Scally s = configfs_attach_gadget_strings(gi); 1716fea77077SWei Yongjun if (IS_ERR(s)) { 1717fea77077SWei Yongjun ret = PTR_ERR(s); 171888af8bbeSSebastian Andrzej Siewior goto err_comp_cleanup; 1719fea77077SWei Yongjun } 172088af8bbeSSebastian Andrzej Siewior 172188af8bbeSSebastian Andrzej Siewior gi->cdev.desc.iManufacturer = s[USB_GADGET_MANUFACTURER_IDX].id; 172288af8bbeSSebastian Andrzej Siewior gi->cdev.desc.iProduct = s[USB_GADGET_PRODUCT_IDX].id; 172388af8bbeSSebastian Andrzej Siewior gi->cdev.desc.iSerialNumber = s[USB_GADGET_SERIAL_IDX].id; 1724c0335632SDaniel Scally 1725c0335632SDaniel Scally gi->cdev.usb_strings = s; 172688af8bbeSSebastian Andrzej Siewior } 172788af8bbeSSebastian Andrzej Siewior 172893c47394SJó Ágila Bitsch if (gi->use_webusb) { 172993c47394SJó Ágila Bitsch cdev->use_webusb = true; 173093c47394SJó Ágila Bitsch cdev->bcd_webusb_version = gi->bcd_webusb_version; 173193c47394SJó Ágila Bitsch cdev->b_webusb_vendor_code = gi->b_webusb_vendor_code; 173293c47394SJó Ágila Bitsch memcpy(cdev->landing_page, gi->landing_page, WEBUSB_URL_RAW_MAX_LENGTH); 173393c47394SJó Ágila Bitsch } 173493c47394SJó Ágila Bitsch 173587213d38SAndrzej Pietrasiewicz if (gi->use_os_desc) { 173687213d38SAndrzej Pietrasiewicz cdev->use_os_string = true; 173787213d38SAndrzej Pietrasiewicz cdev->b_vendor_code = gi->b_vendor_code; 173887213d38SAndrzej Pietrasiewicz memcpy(cdev->qw_sign, gi->qw_sign, OS_STRING_QW_SIGN_LEN); 173987213d38SAndrzej Pietrasiewicz } 174087213d38SAndrzej Pietrasiewicz 174141ce84c8SLi Jun if (gadget_is_otg(gadget) && !otg_desc[0]) { 174241ce84c8SLi Jun struct usb_descriptor_header *usb_desc; 174341ce84c8SLi Jun 174441ce84c8SLi Jun usb_desc = usb_otg_descriptor_alloc(gadget); 174541ce84c8SLi Jun if (!usb_desc) { 174641ce84c8SLi Jun ret = -ENOMEM; 174741ce84c8SLi Jun goto err_comp_cleanup; 174841ce84c8SLi Jun } 174941ce84c8SLi Jun usb_otg_descriptor_init(gadget, usb_desc); 175041ce84c8SLi Jun otg_desc[0] = usb_desc; 175141ce84c8SLi Jun otg_desc[1] = NULL; 175241ce84c8SLi Jun } 175341ce84c8SLi Jun 175488af8bbeSSebastian Andrzej Siewior /* Go through all configs, attach all functions */ 175588af8bbeSSebastian Andrzej Siewior list_for_each_entry(c, &gi->cdev.configs, list) { 175688af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg; 175788af8bbeSSebastian Andrzej Siewior struct usb_function *f; 175888af8bbeSSebastian Andrzej Siewior struct usb_function *tmp; 175988af8bbeSSebastian Andrzej Siewior struct gadget_config_name *cn; 176088af8bbeSSebastian Andrzej Siewior 176141ce84c8SLi Jun if (gadget_is_otg(gadget)) 176241ce84c8SLi Jun c->descriptors = otg_desc; 176341ce84c8SLi Jun 176488af8bbeSSebastian Andrzej Siewior cfg = container_of(c, struct config_usb_cfg, c); 176588af8bbeSSebastian Andrzej Siewior if (!list_empty(&cfg->string_list)) { 176688af8bbeSSebastian Andrzej Siewior i = 0; 176788af8bbeSSebastian Andrzej Siewior list_for_each_entry(cn, &cfg->string_list, list) { 176888af8bbeSSebastian Andrzej Siewior cfg->gstrings[i] = &cn->stringtab_dev; 176988af8bbeSSebastian Andrzej Siewior cn->stringtab_dev.strings = &cn->strings; 177088af8bbeSSebastian Andrzej Siewior cn->strings.s = cn->configuration; 177188af8bbeSSebastian Andrzej Siewior i++; 177288af8bbeSSebastian Andrzej Siewior } 177388af8bbeSSebastian Andrzej Siewior cfg->gstrings[i] = NULL; 177488af8bbeSSebastian Andrzej Siewior s = usb_gstrings_attach(&gi->cdev, cfg->gstrings, 1); 1775fea77077SWei Yongjun if (IS_ERR(s)) { 1776fea77077SWei Yongjun ret = PTR_ERR(s); 177788af8bbeSSebastian Andrzej Siewior goto err_comp_cleanup; 1778fea77077SWei Yongjun } 177988af8bbeSSebastian Andrzej Siewior c->iConfiguration = s[0].id; 178088af8bbeSSebastian Andrzej Siewior } 178188af8bbeSSebastian Andrzej Siewior 178288af8bbeSSebastian Andrzej Siewior list_for_each_entry_safe(f, tmp, &cfg->func_list, list) { 178388af8bbeSSebastian Andrzej Siewior list_del(&f->list); 178488af8bbeSSebastian Andrzej Siewior ret = usb_add_function(c, f); 17855a68e9b5SAndrzej Pietrasiewicz if (ret) { 17865a68e9b5SAndrzej Pietrasiewicz list_add(&f->list, &cfg->func_list); 178788af8bbeSSebastian Andrzej Siewior goto err_purge_funcs; 178888af8bbeSSebastian Andrzej Siewior } 17895a68e9b5SAndrzej Pietrasiewicz } 17907adf9e3aSWesley Cheng ret = usb_gadget_check_config(cdev->gadget); 17917adf9e3aSWesley Cheng if (ret) 17927adf9e3aSWesley Cheng goto err_purge_funcs; 17937adf9e3aSWesley Cheng 179488af8bbeSSebastian Andrzej Siewior usb_ep_autoconfig_reset(cdev->gadget); 179588af8bbeSSebastian Andrzej Siewior } 1796da424314SAndrzej Pietrasiewicz if (cdev->use_os_string) { 1797da424314SAndrzej Pietrasiewicz ret = composite_os_desc_req_prepare(cdev, gadget->ep0); 1798da424314SAndrzej Pietrasiewicz if (ret) 1799da424314SAndrzej Pietrasiewicz goto err_purge_funcs; 1800da424314SAndrzej Pietrasiewicz } 1801da424314SAndrzej Pietrasiewicz 180288af8bbeSSebastian Andrzej Siewior usb_ep_autoconfig_reset(cdev->gadget); 180388af8bbeSSebastian Andrzej Siewior return 0; 180488af8bbeSSebastian Andrzej Siewior 180588af8bbeSSebastian Andrzej Siewior err_purge_funcs: 180688af8bbeSSebastian Andrzej Siewior purge_configs_funcs(gi); 180788af8bbeSSebastian Andrzej Siewior err_comp_cleanup: 180888af8bbeSSebastian Andrzej Siewior composite_dev_cleanup(cdev); 180988af8bbeSSebastian Andrzej Siewior return ret; 181088af8bbeSSebastian Andrzej Siewior } 181188af8bbeSSebastian Andrzej Siewior 181288af8bbeSSebastian Andrzej Siewior static void configfs_composite_unbind(struct usb_gadget *gadget) 181388af8bbeSSebastian Andrzej Siewior { 181488af8bbeSSebastian Andrzej Siewior struct usb_composite_dev *cdev; 181588af8bbeSSebastian Andrzej Siewior struct gadget_info *gi; 18161a1c851bSPeter Chen unsigned long flags; 181788af8bbeSSebastian Andrzej Siewior 181888af8bbeSSebastian Andrzej Siewior /* the gi->lock is hold by the caller */ 181988af8bbeSSebastian Andrzej Siewior 182088af8bbeSSebastian Andrzej Siewior cdev = get_gadget_data(gadget); 182188af8bbeSSebastian Andrzej Siewior gi = container_of(cdev, struct gadget_info, cdev); 18221a1c851bSPeter Chen spin_lock_irqsave(&gi->spinlock, flags); 18231a1c851bSPeter Chen gi->unbind = 1; 18241a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 182588af8bbeSSebastian Andrzej Siewior 182641ce84c8SLi Jun kfree(otg_desc[0]); 182741ce84c8SLi Jun otg_desc[0] = NULL; 182888af8bbeSSebastian Andrzej Siewior purge_configs_funcs(gi); 182988af8bbeSSebastian Andrzej Siewior composite_dev_cleanup(cdev); 183088af8bbeSSebastian Andrzej Siewior usb_ep_autoconfig_reset(cdev->gadget); 18311a1c851bSPeter Chen spin_lock_irqsave(&gi->spinlock, flags); 183288af8bbeSSebastian Andrzej Siewior cdev->gadget = NULL; 1833bf95c4d4SVijayavardhan Vennapusa cdev->deactivations = 0; 1834bf95c4d4SVijayavardhan Vennapusa gadget->deactivated = false; 183588af8bbeSSebastian Andrzej Siewior set_gadget_data(gadget, NULL); 18361a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 18371a1c851bSPeter Chen } 18381a1c851bSPeter Chen 18391a1c851bSPeter Chen static int configfs_composite_setup(struct usb_gadget *gadget, 18401a1c851bSPeter Chen const struct usb_ctrlrequest *ctrl) 18411a1c851bSPeter Chen { 18421a1c851bSPeter Chen struct usb_composite_dev *cdev; 18431a1c851bSPeter Chen struct gadget_info *gi; 18441a1c851bSPeter Chen unsigned long flags; 18451a1c851bSPeter Chen int ret; 18461a1c851bSPeter Chen 18471a1c851bSPeter Chen cdev = get_gadget_data(gadget); 18481a1c851bSPeter Chen if (!cdev) 18491a1c851bSPeter Chen return 0; 18501a1c851bSPeter Chen 18511a1c851bSPeter Chen gi = container_of(cdev, struct gadget_info, cdev); 18521a1c851bSPeter Chen spin_lock_irqsave(&gi->spinlock, flags); 18531a1c851bSPeter Chen cdev = get_gadget_data(gadget); 18541a1c851bSPeter Chen if (!cdev || gi->unbind) { 18551a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 18561a1c851bSPeter Chen return 0; 18571a1c851bSPeter Chen } 18581a1c851bSPeter Chen 18591a1c851bSPeter Chen ret = composite_setup(gadget, ctrl); 18601a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 18611a1c851bSPeter Chen return ret; 18621a1c851bSPeter Chen } 18631a1c851bSPeter Chen 18641a1c851bSPeter Chen static void configfs_composite_disconnect(struct usb_gadget *gadget) 18651a1c851bSPeter Chen { 18661a1c851bSPeter Chen struct usb_composite_dev *cdev; 18671a1c851bSPeter Chen struct gadget_info *gi; 18681a1c851bSPeter Chen unsigned long flags; 18691a1c851bSPeter Chen 18701a1c851bSPeter Chen cdev = get_gadget_data(gadget); 18711a1c851bSPeter Chen if (!cdev) 18721a1c851bSPeter Chen return; 18731a1c851bSPeter Chen 18741a1c851bSPeter Chen gi = container_of(cdev, struct gadget_info, cdev); 18751a1c851bSPeter Chen spin_lock_irqsave(&gi->spinlock, flags); 18761a1c851bSPeter Chen cdev = get_gadget_data(gadget); 18771a1c851bSPeter Chen if (!cdev || gi->unbind) { 18781a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 18791a1c851bSPeter Chen return; 18801a1c851bSPeter Chen } 18811a1c851bSPeter Chen 18821a1c851bSPeter Chen composite_disconnect(gadget); 18831a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 18841a1c851bSPeter Chen } 18851a1c851bSPeter Chen 18864d7aae9fSWesley Cheng static void configfs_composite_reset(struct usb_gadget *gadget) 18874d7aae9fSWesley Cheng { 18884d7aae9fSWesley Cheng struct usb_composite_dev *cdev; 18894d7aae9fSWesley Cheng struct gadget_info *gi; 18904d7aae9fSWesley Cheng unsigned long flags; 18914d7aae9fSWesley Cheng 18924d7aae9fSWesley Cheng cdev = get_gadget_data(gadget); 18934d7aae9fSWesley Cheng if (!cdev) 18944d7aae9fSWesley Cheng return; 18954d7aae9fSWesley Cheng 18964d7aae9fSWesley Cheng gi = container_of(cdev, struct gadget_info, cdev); 18974d7aae9fSWesley Cheng spin_lock_irqsave(&gi->spinlock, flags); 18984d7aae9fSWesley Cheng cdev = get_gadget_data(gadget); 18994d7aae9fSWesley Cheng if (!cdev || gi->unbind) { 19004d7aae9fSWesley Cheng spin_unlock_irqrestore(&gi->spinlock, flags); 19014d7aae9fSWesley Cheng return; 19024d7aae9fSWesley Cheng } 19034d7aae9fSWesley Cheng 19044d7aae9fSWesley Cheng composite_reset(gadget); 19054d7aae9fSWesley Cheng spin_unlock_irqrestore(&gi->spinlock, flags); 19064d7aae9fSWesley Cheng } 19074d7aae9fSWesley Cheng 19081a1c851bSPeter Chen static void configfs_composite_suspend(struct usb_gadget *gadget) 19091a1c851bSPeter Chen { 19101a1c851bSPeter Chen struct usb_composite_dev *cdev; 19111a1c851bSPeter Chen struct gadget_info *gi; 19121a1c851bSPeter Chen unsigned long flags; 19131a1c851bSPeter Chen 19141a1c851bSPeter Chen cdev = get_gadget_data(gadget); 19151a1c851bSPeter Chen if (!cdev) 19161a1c851bSPeter Chen return; 19171a1c851bSPeter Chen 19181a1c851bSPeter Chen gi = container_of(cdev, struct gadget_info, cdev); 19191a1c851bSPeter Chen spin_lock_irqsave(&gi->spinlock, flags); 19201a1c851bSPeter Chen cdev = get_gadget_data(gadget); 19211a1c851bSPeter Chen if (!cdev || gi->unbind) { 19221a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 19231a1c851bSPeter Chen return; 19241a1c851bSPeter Chen } 19251a1c851bSPeter Chen 19261a1c851bSPeter Chen composite_suspend(gadget); 19271a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 19281a1c851bSPeter Chen } 19291a1c851bSPeter Chen 19301a1c851bSPeter Chen static void configfs_composite_resume(struct usb_gadget *gadget) 19311a1c851bSPeter Chen { 19321a1c851bSPeter Chen struct usb_composite_dev *cdev; 19331a1c851bSPeter Chen struct gadget_info *gi; 19341a1c851bSPeter Chen unsigned long flags; 19351a1c851bSPeter Chen 19361a1c851bSPeter Chen cdev = get_gadget_data(gadget); 19371a1c851bSPeter Chen if (!cdev) 19381a1c851bSPeter Chen return; 19391a1c851bSPeter Chen 19401a1c851bSPeter Chen gi = container_of(cdev, struct gadget_info, cdev); 19411a1c851bSPeter Chen spin_lock_irqsave(&gi->spinlock, flags); 19421a1c851bSPeter Chen cdev = get_gadget_data(gadget); 19431a1c851bSPeter Chen if (!cdev || gi->unbind) { 19441a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 19451a1c851bSPeter Chen return; 19461a1c851bSPeter Chen } 19471a1c851bSPeter Chen 19481a1c851bSPeter Chen composite_resume(gadget); 19491a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 195088af8bbeSSebastian Andrzej Siewior } 195188af8bbeSSebastian Andrzej Siewior 195288af8bbeSSebastian Andrzej Siewior static const struct usb_gadget_driver configfs_driver_template = { 195388af8bbeSSebastian Andrzej Siewior .bind = configfs_composite_bind, 195488af8bbeSSebastian Andrzej Siewior .unbind = configfs_composite_unbind, 195588af8bbeSSebastian Andrzej Siewior 19561a1c851bSPeter Chen .setup = configfs_composite_setup, 19574d7aae9fSWesley Cheng .reset = configfs_composite_reset, 19581a1c851bSPeter Chen .disconnect = configfs_composite_disconnect, 195988af8bbeSSebastian Andrzej Siewior 19601a1c851bSPeter Chen .suspend = configfs_composite_suspend, 19611a1c851bSPeter Chen .resume = configfs_composite_resume, 19623a571870SAndrzej Pietrasiewicz 1963e2459108Staehyun.cho .max_speed = USB_SPEED_SUPER_PLUS, 196488af8bbeSSebastian Andrzej Siewior .driver = { 196588af8bbeSSebastian Andrzej Siewior .owner = THIS_MODULE, 196688af8bbeSSebastian Andrzej Siewior }, 1967f1bddbb3SKrzysztof Opasiak .match_existing_only = 1, 196888af8bbeSSebastian Andrzej Siewior }; 196988af8bbeSSebastian Andrzej Siewior 197088af8bbeSSebastian Andrzej Siewior static struct config_group *gadgets_make( 197188af8bbeSSebastian Andrzej Siewior struct config_group *group, 197288af8bbeSSebastian Andrzej Siewior const char *name) 197388af8bbeSSebastian Andrzej Siewior { 197488af8bbeSSebastian Andrzej Siewior struct gadget_info *gi; 197588af8bbeSSebastian Andrzej Siewior 197688af8bbeSSebastian Andrzej Siewior gi = kzalloc(sizeof(*gi), GFP_KERNEL); 197788af8bbeSSebastian Andrzej Siewior if (!gi) 197888af8bbeSSebastian Andrzej Siewior return ERR_PTR(-ENOMEM); 197988af8bbeSSebastian Andrzej Siewior 19801ae1602dSChristoph Hellwig config_group_init_type_name(&gi->group, name, &gadget_root_type); 198188af8bbeSSebastian Andrzej Siewior 198288af8bbeSSebastian Andrzej Siewior config_group_init_type_name(&gi->functions_group, "functions", 198388af8bbeSSebastian Andrzej Siewior &functions_type); 19841ae1602dSChristoph Hellwig configfs_add_default_group(&gi->functions_group, &gi->group); 19851ae1602dSChristoph Hellwig 198688af8bbeSSebastian Andrzej Siewior config_group_init_type_name(&gi->configs_group, "configs", 198788af8bbeSSebastian Andrzej Siewior &config_desc_type); 19881ae1602dSChristoph Hellwig configfs_add_default_group(&gi->configs_group, &gi->group); 19891ae1602dSChristoph Hellwig 199088af8bbeSSebastian Andrzej Siewior config_group_init_type_name(&gi->strings_group, "strings", 19916e2a512dSDaniel Scally &gadget_language_strings_type); 19921ae1602dSChristoph Hellwig configfs_add_default_group(&gi->strings_group, &gi->group); 19931ae1602dSChristoph Hellwig 199487213d38SAndrzej Pietrasiewicz config_group_init_type_name(&gi->os_desc_group, "os_desc", 199587213d38SAndrzej Pietrasiewicz &os_desc_type); 19961ae1602dSChristoph Hellwig configfs_add_default_group(&gi->os_desc_group, &gi->group); 199788af8bbeSSebastian Andrzej Siewior 199893c47394SJó Ágila Bitsch config_group_init_type_name(&gi->webusb_group, "webusb", 199993c47394SJó Ágila Bitsch &webusb_type); 200093c47394SJó Ágila Bitsch configfs_add_default_group(&gi->webusb_group, &gi->group); 200193c47394SJó Ágila Bitsch 200288af8bbeSSebastian Andrzej Siewior gi->composite.bind = configfs_do_nothing; 200388af8bbeSSebastian Andrzej Siewior gi->composite.unbind = configfs_do_nothing; 200488af8bbeSSebastian Andrzej Siewior gi->composite.suspend = NULL; 200588af8bbeSSebastian Andrzej Siewior gi->composite.resume = NULL; 2006e2459108Staehyun.cho gi->composite.max_speed = USB_SPEED_SUPER_PLUS; 200788af8bbeSSebastian Andrzej Siewior 2008093edc2bSWei Yongjun spin_lock_init(&gi->spinlock); 200988af8bbeSSebastian Andrzej Siewior mutex_init(&gi->lock); 201088af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&gi->string_list); 201188af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&gi->available_func); 201288af8bbeSSebastian Andrzej Siewior 201388af8bbeSSebastian Andrzej Siewior composite_init_dev(&gi->cdev); 201488af8bbeSSebastian Andrzej Siewior gi->cdev.desc.bLength = USB_DT_DEVICE_SIZE; 201588af8bbeSSebastian Andrzej Siewior gi->cdev.desc.bDescriptorType = USB_DT_DEVICE; 201688af8bbeSSebastian Andrzej Siewior gi->cdev.desc.bcdDevice = cpu_to_le16(get_default_bcdDevice()); 201788af8bbeSSebastian Andrzej Siewior 201888af8bbeSSebastian Andrzej Siewior gi->composite.gadget_driver = configfs_driver_template; 201988af8bbeSSebastian Andrzej Siewior 20207c075538SChanh Nguyen gi->composite.gadget_driver.driver.name = kasprintf(GFP_KERNEL, 20217c075538SChanh Nguyen "configfs-gadget.%s", name); 20227c075538SChanh Nguyen if (!gi->composite.gadget_driver.driver.name) 20237c075538SChanh Nguyen goto err; 20247c075538SChanh Nguyen 202588af8bbeSSebastian Andrzej Siewior gi->composite.gadget_driver.function = kstrdup(name, GFP_KERNEL); 202688af8bbeSSebastian Andrzej Siewior gi->composite.name = gi->composite.gadget_driver.function; 202788af8bbeSSebastian Andrzej Siewior 202888af8bbeSSebastian Andrzej Siewior if (!gi->composite.gadget_driver.function) 20297c075538SChanh Nguyen goto out_free_driver_name; 203088af8bbeSSebastian Andrzej Siewior 203188af8bbeSSebastian Andrzej Siewior return &gi->group; 20327c075538SChanh Nguyen 20337c075538SChanh Nguyen out_free_driver_name: 20347c075538SChanh Nguyen kfree(gi->composite.gadget_driver.driver.name); 203588af8bbeSSebastian Andrzej Siewior err: 203688af8bbeSSebastian Andrzej Siewior kfree(gi); 203788af8bbeSSebastian Andrzej Siewior return ERR_PTR(-ENOMEM); 203888af8bbeSSebastian Andrzej Siewior } 203988af8bbeSSebastian Andrzej Siewior 204088af8bbeSSebastian Andrzej Siewior static void gadgets_drop(struct config_group *group, struct config_item *item) 204188af8bbeSSebastian Andrzej Siewior { 204288af8bbeSSebastian Andrzej Siewior config_item_put(item); 204388af8bbeSSebastian Andrzej Siewior } 204488af8bbeSSebastian Andrzej Siewior 204588af8bbeSSebastian Andrzej Siewior static struct configfs_group_operations gadgets_ops = { 204688af8bbeSSebastian Andrzej Siewior .make_group = &gadgets_make, 204788af8bbeSSebastian Andrzej Siewior .drop_item = &gadgets_drop, 204888af8bbeSSebastian Andrzej Siewior }; 204988af8bbeSSebastian Andrzej Siewior 20504ad01412SBhumika Goyal static const struct config_item_type gadgets_type = { 205188af8bbeSSebastian Andrzej Siewior .ct_group_ops = &gadgets_ops, 205288af8bbeSSebastian Andrzej Siewior .ct_owner = THIS_MODULE, 205388af8bbeSSebastian Andrzej Siewior }; 205488af8bbeSSebastian Andrzej Siewior 205588af8bbeSSebastian Andrzej Siewior static struct configfs_subsystem gadget_subsys = { 205688af8bbeSSebastian Andrzej Siewior .su_group = { 205788af8bbeSSebastian Andrzej Siewior .cg_item = { 205888af8bbeSSebastian Andrzej Siewior .ci_namebuf = "usb_gadget", 205988af8bbeSSebastian Andrzej Siewior .ci_type = &gadgets_type, 206088af8bbeSSebastian Andrzej Siewior }, 206188af8bbeSSebastian Andrzej Siewior }, 206288af8bbeSSebastian Andrzej Siewior .su_mutex = __MUTEX_INITIALIZER(gadget_subsys.su_mutex), 206388af8bbeSSebastian Andrzej Siewior }; 206488af8bbeSSebastian Andrzej Siewior 2065092a4bd0SAndrzej Pietrasiewicz void unregister_gadget_item(struct config_item *item) 2066092a4bd0SAndrzej Pietrasiewicz { 2067092a4bd0SAndrzej Pietrasiewicz struct gadget_info *gi = to_gadget_info(item); 2068092a4bd0SAndrzej Pietrasiewicz 2069cee51c33SWinter Wang mutex_lock(&gi->lock); 2070092a4bd0SAndrzej Pietrasiewicz unregister_gadget(gi); 2071cee51c33SWinter Wang mutex_unlock(&gi->lock); 2072092a4bd0SAndrzej Pietrasiewicz } 20730700faafSFelipe Balbi EXPORT_SYMBOL_GPL(unregister_gadget_item); 2074092a4bd0SAndrzej Pietrasiewicz 207588af8bbeSSebastian Andrzej Siewior static int __init gadget_cfs_init(void) 207688af8bbeSSebastian Andrzej Siewior { 207788af8bbeSSebastian Andrzej Siewior int ret; 207888af8bbeSSebastian Andrzej Siewior 207988af8bbeSSebastian Andrzej Siewior config_group_init(&gadget_subsys.su_group); 208088af8bbeSSebastian Andrzej Siewior 208188af8bbeSSebastian Andrzej Siewior ret = configfs_register_subsystem(&gadget_subsys); 208288af8bbeSSebastian Andrzej Siewior return ret; 208388af8bbeSSebastian Andrzej Siewior } 208488af8bbeSSebastian Andrzej Siewior module_init(gadget_cfs_init); 208588af8bbeSSebastian Andrzej Siewior 208688af8bbeSSebastian Andrzej Siewior static void __exit gadget_cfs_exit(void) 208788af8bbeSSebastian Andrzej Siewior { 208888af8bbeSSebastian Andrzej Siewior configfs_unregister_subsystem(&gadget_subsys); 208988af8bbeSSebastian Andrzej Siewior } 209088af8bbeSSebastian Andrzej Siewior module_exit(gadget_cfs_exit); 2091