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 8988af8bbeSSebastian Andrzej Siewior struct gadget_strings { 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; 9888af8bbeSSebastian Andrzej Siewior }; 9988af8bbeSSebastian Andrzej Siewior 10088af8bbeSSebastian Andrzej Siewior struct gadget_config_name { 10188af8bbeSSebastian Andrzej Siewior struct usb_gadget_strings stringtab_dev; 10288af8bbeSSebastian Andrzej Siewior struct usb_string strings; 10388af8bbeSSebastian Andrzej Siewior char *configuration; 10488af8bbeSSebastian Andrzej Siewior 10588af8bbeSSebastian Andrzej Siewior struct config_group group; 10688af8bbeSSebastian Andrzej Siewior struct list_head list; 10788af8bbeSSebastian Andrzej Siewior }; 10888af8bbeSSebastian Andrzej Siewior 10998f153a1SJim Lin #define USB_MAX_STRING_WITH_NULL_LEN (USB_MAX_STRING_LEN+1) 11098f153a1SJim Lin 11188af8bbeSSebastian Andrzej Siewior static int usb_string_copy(const char *s, char **s_copy) 11288af8bbeSSebastian Andrzej Siewior { 11388af8bbeSSebastian Andrzej Siewior int ret; 11488af8bbeSSebastian Andrzej Siewior char *str; 11588af8bbeSSebastian Andrzej Siewior char *copy = *s_copy; 11688af8bbeSSebastian Andrzej Siewior ret = strlen(s); 11781c74628SMacpaul Lin if (ret > USB_MAX_STRING_LEN) 11888af8bbeSSebastian Andrzej Siewior return -EOVERFLOW; 11988af8bbeSSebastian Andrzej Siewior 12098f153a1SJim Lin if (copy) { 12198f153a1SJim Lin str = copy; 12298f153a1SJim Lin } else { 12398f153a1SJim Lin str = kmalloc(USB_MAX_STRING_WITH_NULL_LEN, GFP_KERNEL); 12488af8bbeSSebastian Andrzej Siewior if (!str) 12588af8bbeSSebastian Andrzej Siewior return -ENOMEM; 12698f153a1SJim Lin } 12798f153a1SJim Lin strcpy(str, s); 12888af8bbeSSebastian Andrzej Siewior if (str[ret - 1] == '\n') 12988af8bbeSSebastian Andrzej Siewior str[ret - 1] = '\0'; 13088af8bbeSSebastian Andrzej Siewior *s_copy = str; 13188af8bbeSSebastian Andrzej Siewior return 0; 13288af8bbeSSebastian Andrzej Siewior } 13388af8bbeSSebastian Andrzej Siewior 13488af8bbeSSebastian Andrzej Siewior #define GI_DEVICE_DESC_SIMPLE_R_u8(__name) \ 13545b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_##__name##_show(struct config_item *item, \ 13688af8bbeSSebastian Andrzej Siewior char *page) \ 13788af8bbeSSebastian Andrzej Siewior { \ 13845b6a73fSChristoph Hellwig return sprintf(page, "0x%02x\n", \ 13945b6a73fSChristoph Hellwig to_gadget_info(item)->cdev.desc.__name); \ 14088af8bbeSSebastian Andrzej Siewior } 14188af8bbeSSebastian Andrzej Siewior 14288af8bbeSSebastian Andrzej Siewior #define GI_DEVICE_DESC_SIMPLE_R_u16(__name) \ 14345b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_##__name##_show(struct config_item *item, \ 14488af8bbeSSebastian Andrzej Siewior char *page) \ 14588af8bbeSSebastian Andrzej Siewior { \ 14645b6a73fSChristoph Hellwig return sprintf(page, "0x%04x\n", \ 14745b6a73fSChristoph Hellwig le16_to_cpup(&to_gadget_info(item)->cdev.desc.__name)); \ 14888af8bbeSSebastian Andrzej Siewior } 14988af8bbeSSebastian Andrzej Siewior 15088af8bbeSSebastian Andrzej Siewior 15188af8bbeSSebastian Andrzej Siewior #define GI_DEVICE_DESC_SIMPLE_W_u8(_name) \ 15245b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_##_name##_store(struct config_item *item, \ 15388af8bbeSSebastian Andrzej Siewior const char *page, size_t len) \ 15488af8bbeSSebastian Andrzej Siewior { \ 15588af8bbeSSebastian Andrzej Siewior u8 val; \ 15688af8bbeSSebastian Andrzej Siewior int ret; \ 15788af8bbeSSebastian Andrzej Siewior ret = kstrtou8(page, 0, &val); \ 15888af8bbeSSebastian Andrzej Siewior if (ret) \ 15988af8bbeSSebastian Andrzej Siewior return ret; \ 16045b6a73fSChristoph Hellwig to_gadget_info(item)->cdev.desc._name = val; \ 16188af8bbeSSebastian Andrzej Siewior return len; \ 16288af8bbeSSebastian Andrzej Siewior } 16388af8bbeSSebastian Andrzej Siewior 16488af8bbeSSebastian Andrzej Siewior #define GI_DEVICE_DESC_SIMPLE_W_u16(_name) \ 16545b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_##_name##_store(struct config_item *item, \ 16688af8bbeSSebastian Andrzej Siewior const char *page, size_t len) \ 16788af8bbeSSebastian Andrzej Siewior { \ 16888af8bbeSSebastian Andrzej Siewior u16 val; \ 16988af8bbeSSebastian Andrzej Siewior int ret; \ 17088af8bbeSSebastian Andrzej Siewior ret = kstrtou16(page, 0, &val); \ 17188af8bbeSSebastian Andrzej Siewior if (ret) \ 17288af8bbeSSebastian Andrzej Siewior return ret; \ 17345b6a73fSChristoph Hellwig to_gadget_info(item)->cdev.desc._name = cpu_to_le16p(&val); \ 17488af8bbeSSebastian Andrzej Siewior return len; \ 17588af8bbeSSebastian Andrzej Siewior } 17688af8bbeSSebastian Andrzej Siewior 17788af8bbeSSebastian Andrzej Siewior #define GI_DEVICE_DESC_SIMPLE_RW(_name, _type) \ 17888af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_R_##_type(_name) \ 17988af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_W_##_type(_name) 18088af8bbeSSebastian Andrzej Siewior 18188af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_R_u16(bcdUSB); 18288af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(bDeviceClass, u8); 18388af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(bDeviceSubClass, u8); 18488af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(bDeviceProtocol, u8); 18588af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(bMaxPacketSize0, u8); 18688af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(idVendor, u16); 18788af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(idProduct, u16); 18888af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_R_u16(bcdDevice); 18988af8bbeSSebastian Andrzej Siewior 19088af8bbeSSebastian Andrzej Siewior static ssize_t is_valid_bcd(u16 bcd_val) 19188af8bbeSSebastian Andrzej Siewior { 19288af8bbeSSebastian Andrzej Siewior if ((bcd_val & 0xf) > 9) 19388af8bbeSSebastian Andrzej Siewior return -EINVAL; 19488af8bbeSSebastian Andrzej Siewior if (((bcd_val >> 4) & 0xf) > 9) 19588af8bbeSSebastian Andrzej Siewior return -EINVAL; 19688af8bbeSSebastian Andrzej Siewior if (((bcd_val >> 8) & 0xf) > 9) 19788af8bbeSSebastian Andrzej Siewior return -EINVAL; 19888af8bbeSSebastian Andrzej Siewior if (((bcd_val >> 12) & 0xf) > 9) 19988af8bbeSSebastian Andrzej Siewior return -EINVAL; 20088af8bbeSSebastian Andrzej Siewior return 0; 20188af8bbeSSebastian Andrzej Siewior } 20288af8bbeSSebastian Andrzej Siewior 20345b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_bcdDevice_store(struct config_item *item, 20488af8bbeSSebastian Andrzej Siewior const char *page, size_t len) 20588af8bbeSSebastian Andrzej Siewior { 20688af8bbeSSebastian Andrzej Siewior u16 bcdDevice; 20788af8bbeSSebastian Andrzej Siewior int ret; 20888af8bbeSSebastian Andrzej Siewior 20988af8bbeSSebastian Andrzej Siewior ret = kstrtou16(page, 0, &bcdDevice); 21088af8bbeSSebastian Andrzej Siewior if (ret) 21188af8bbeSSebastian Andrzej Siewior return ret; 21288af8bbeSSebastian Andrzej Siewior ret = is_valid_bcd(bcdDevice); 21388af8bbeSSebastian Andrzej Siewior if (ret) 21488af8bbeSSebastian Andrzej Siewior return ret; 21588af8bbeSSebastian Andrzej Siewior 21645b6a73fSChristoph Hellwig to_gadget_info(item)->cdev.desc.bcdDevice = cpu_to_le16(bcdDevice); 21788af8bbeSSebastian Andrzej Siewior return len; 21888af8bbeSSebastian Andrzej Siewior } 21988af8bbeSSebastian Andrzej Siewior 22045b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_bcdUSB_store(struct config_item *item, 22188af8bbeSSebastian Andrzej Siewior const char *page, size_t len) 22288af8bbeSSebastian Andrzej Siewior { 22388af8bbeSSebastian Andrzej Siewior u16 bcdUSB; 22488af8bbeSSebastian Andrzej Siewior int ret; 22588af8bbeSSebastian Andrzej Siewior 22688af8bbeSSebastian Andrzej Siewior ret = kstrtou16(page, 0, &bcdUSB); 22788af8bbeSSebastian Andrzej Siewior if (ret) 22888af8bbeSSebastian Andrzej Siewior return ret; 22988af8bbeSSebastian Andrzej Siewior ret = is_valid_bcd(bcdUSB); 23088af8bbeSSebastian Andrzej Siewior if (ret) 23188af8bbeSSebastian Andrzej Siewior return ret; 23288af8bbeSSebastian Andrzej Siewior 23345b6a73fSChristoph Hellwig to_gadget_info(item)->cdev.desc.bcdUSB = cpu_to_le16(bcdUSB); 23488af8bbeSSebastian Andrzej Siewior return len; 23588af8bbeSSebastian Andrzej Siewior } 23688af8bbeSSebastian Andrzej Siewior 23745b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_UDC_show(struct config_item *item, char *page) 23888af8bbeSSebastian Andrzej Siewior { 23964e6bbffSEddie Hung struct gadget_info *gi = to_gadget_info(item); 24064e6bbffSEddie Hung char *udc_name; 24164e6bbffSEddie Hung int ret; 242afdaadc3SRuslan Bilovol 24364e6bbffSEddie Hung mutex_lock(&gi->lock); 24464e6bbffSEddie Hung udc_name = gi->composite.gadget_driver.udc_name; 24564e6bbffSEddie Hung ret = sprintf(page, "%s\n", udc_name ?: ""); 24664e6bbffSEddie Hung mutex_unlock(&gi->lock); 24764e6bbffSEddie Hung 24864e6bbffSEddie Hung return ret; 24988af8bbeSSebastian Andrzej Siewior } 25088af8bbeSSebastian Andrzej Siewior 25188af8bbeSSebastian Andrzej Siewior static int unregister_gadget(struct gadget_info *gi) 25288af8bbeSSebastian Andrzej Siewior { 25388af8bbeSSebastian Andrzej Siewior int ret; 25488af8bbeSSebastian Andrzej Siewior 255afdaadc3SRuslan Bilovol if (!gi->composite.gadget_driver.udc_name) 25688af8bbeSSebastian Andrzej Siewior return -ENODEV; 25788af8bbeSSebastian Andrzej Siewior 25888af8bbeSSebastian Andrzej Siewior ret = usb_gadget_unregister_driver(&gi->composite.gadget_driver); 25988af8bbeSSebastian Andrzej Siewior if (ret) 26088af8bbeSSebastian Andrzej Siewior return ret; 261afdaadc3SRuslan Bilovol kfree(gi->composite.gadget_driver.udc_name); 262afdaadc3SRuslan Bilovol gi->composite.gadget_driver.udc_name = NULL; 26388af8bbeSSebastian Andrzej Siewior return 0; 26488af8bbeSSebastian Andrzej Siewior } 26588af8bbeSSebastian Andrzej Siewior 26645b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_UDC_store(struct config_item *item, 26788af8bbeSSebastian Andrzej Siewior const char *page, size_t len) 26888af8bbeSSebastian Andrzej Siewior { 26945b6a73fSChristoph Hellwig struct gadget_info *gi = to_gadget_info(item); 27088af8bbeSSebastian Andrzej Siewior char *name; 27188af8bbeSSebastian Andrzej Siewior int ret; 27288af8bbeSSebastian Andrzej Siewior 27315753588SKyungtae Kim if (strlen(page) < len) 27415753588SKyungtae Kim return -EOVERFLOW; 27515753588SKyungtae Kim 27688af8bbeSSebastian Andrzej Siewior name = kstrdup(page, GFP_KERNEL); 27788af8bbeSSebastian Andrzej Siewior if (!name) 27888af8bbeSSebastian Andrzej Siewior return -ENOMEM; 27988af8bbeSSebastian Andrzej Siewior if (name[len - 1] == '\n') 28088af8bbeSSebastian Andrzej Siewior name[len - 1] = '\0'; 28188af8bbeSSebastian Andrzej Siewior 28288af8bbeSSebastian Andrzej Siewior mutex_lock(&gi->lock); 28388af8bbeSSebastian Andrzej Siewior 28488af8bbeSSebastian Andrzej Siewior if (!strlen(name)) { 28588af8bbeSSebastian Andrzej Siewior ret = unregister_gadget(gi); 28688af8bbeSSebastian Andrzej Siewior if (ret) 28788af8bbeSSebastian Andrzej Siewior goto err; 28838355b2aSJohn Keeping kfree(name); 28988af8bbeSSebastian Andrzej Siewior } else { 290afdaadc3SRuslan Bilovol if (gi->composite.gadget_driver.udc_name) { 29188af8bbeSSebastian Andrzej Siewior ret = -EBUSY; 29288af8bbeSSebastian Andrzej Siewior goto err; 29388af8bbeSSebastian Andrzej Siewior } 294afdaadc3SRuslan Bilovol gi->composite.gadget_driver.udc_name = name; 295af1969a2SAlan Stern ret = usb_gadget_register_driver(&gi->composite.gadget_driver); 296afdaadc3SRuslan Bilovol if (ret) { 297afdaadc3SRuslan Bilovol gi->composite.gadget_driver.udc_name = NULL; 29888af8bbeSSebastian Andrzej Siewior goto err; 299afdaadc3SRuslan Bilovol } 30088af8bbeSSebastian Andrzej Siewior } 30188af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 30288af8bbeSSebastian Andrzej Siewior return len; 30388af8bbeSSebastian Andrzej Siewior err: 30488af8bbeSSebastian Andrzej Siewior kfree(name); 30588af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 30688af8bbeSSebastian Andrzej Siewior return ret; 30788af8bbeSSebastian Andrzej Siewior } 30888af8bbeSSebastian Andrzej Siewior 309a0249703SThinh Nguyen static ssize_t gadget_dev_desc_max_speed_show(struct config_item *item, 310a0249703SThinh Nguyen char *page) 311a0249703SThinh Nguyen { 312a0249703SThinh Nguyen enum usb_device_speed speed = to_gadget_info(item)->composite.max_speed; 313a0249703SThinh Nguyen 314a0249703SThinh Nguyen return sprintf(page, "%s\n", usb_speed_string(speed)); 315a0249703SThinh Nguyen } 316a0249703SThinh Nguyen 317a0249703SThinh Nguyen static ssize_t gadget_dev_desc_max_speed_store(struct config_item *item, 318a0249703SThinh Nguyen const char *page, size_t len) 319a0249703SThinh Nguyen { 320a0249703SThinh Nguyen struct gadget_info *gi = to_gadget_info(item); 321a0249703SThinh Nguyen 322a0249703SThinh Nguyen mutex_lock(&gi->lock); 323a0249703SThinh Nguyen 324a0249703SThinh Nguyen /* Prevent changing of max_speed after the driver is binded */ 325a0249703SThinh Nguyen if (gi->composite.gadget_driver.udc_name) 326a0249703SThinh Nguyen goto err; 327a0249703SThinh Nguyen 328a0249703SThinh Nguyen if (strncmp(page, "super-speed-plus", 16) == 0) 329a0249703SThinh Nguyen gi->composite.max_speed = USB_SPEED_SUPER_PLUS; 330a0249703SThinh Nguyen else if (strncmp(page, "super-speed", 11) == 0) 331a0249703SThinh Nguyen gi->composite.max_speed = USB_SPEED_SUPER; 332a0249703SThinh Nguyen else if (strncmp(page, "high-speed", 10) == 0) 333a0249703SThinh Nguyen gi->composite.max_speed = USB_SPEED_HIGH; 334a0249703SThinh Nguyen else if (strncmp(page, "full-speed", 10) == 0) 335a0249703SThinh Nguyen gi->composite.max_speed = USB_SPEED_FULL; 336a0249703SThinh Nguyen else if (strncmp(page, "low-speed", 9) == 0) 337a0249703SThinh Nguyen gi->composite.max_speed = USB_SPEED_LOW; 338a0249703SThinh Nguyen else 339a0249703SThinh Nguyen goto err; 340a0249703SThinh Nguyen 341a0249703SThinh Nguyen gi->composite.gadget_driver.max_speed = gi->composite.max_speed; 342a0249703SThinh Nguyen 343a0249703SThinh Nguyen mutex_unlock(&gi->lock); 344a0249703SThinh Nguyen return len; 345a0249703SThinh Nguyen err: 346a0249703SThinh Nguyen mutex_unlock(&gi->lock); 347a0249703SThinh Nguyen return -EINVAL; 348a0249703SThinh Nguyen } 349a0249703SThinh Nguyen 35045b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, bDeviceClass); 35145b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, bDeviceSubClass); 35245b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, bDeviceProtocol); 35345b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, bMaxPacketSize0); 35445b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, idVendor); 35545b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, idProduct); 35645b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, bcdDevice); 35745b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, bcdUSB); 35845b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, UDC); 359a0249703SThinh Nguyen CONFIGFS_ATTR(gadget_dev_desc_, max_speed); 36088af8bbeSSebastian Andrzej Siewior 36188af8bbeSSebastian Andrzej Siewior static struct configfs_attribute *gadget_root_attrs[] = { 36245b6a73fSChristoph Hellwig &gadget_dev_desc_attr_bDeviceClass, 36345b6a73fSChristoph Hellwig &gadget_dev_desc_attr_bDeviceSubClass, 36445b6a73fSChristoph Hellwig &gadget_dev_desc_attr_bDeviceProtocol, 36545b6a73fSChristoph Hellwig &gadget_dev_desc_attr_bMaxPacketSize0, 36645b6a73fSChristoph Hellwig &gadget_dev_desc_attr_idVendor, 36745b6a73fSChristoph Hellwig &gadget_dev_desc_attr_idProduct, 36845b6a73fSChristoph Hellwig &gadget_dev_desc_attr_bcdDevice, 36945b6a73fSChristoph Hellwig &gadget_dev_desc_attr_bcdUSB, 37045b6a73fSChristoph Hellwig &gadget_dev_desc_attr_UDC, 371a0249703SThinh Nguyen &gadget_dev_desc_attr_max_speed, 37288af8bbeSSebastian Andrzej Siewior NULL, 37388af8bbeSSebastian Andrzej Siewior }; 37488af8bbeSSebastian Andrzej Siewior 37588af8bbeSSebastian Andrzej Siewior static inline struct gadget_strings *to_gadget_strings(struct config_item *item) 37688af8bbeSSebastian Andrzej Siewior { 37788af8bbeSSebastian Andrzej Siewior return container_of(to_config_group(item), struct gadget_strings, 37888af8bbeSSebastian Andrzej Siewior group); 37988af8bbeSSebastian Andrzej Siewior } 38088af8bbeSSebastian Andrzej Siewior 38188af8bbeSSebastian Andrzej Siewior static inline struct gadget_config_name *to_gadget_config_name( 38288af8bbeSSebastian Andrzej Siewior struct config_item *item) 38388af8bbeSSebastian Andrzej Siewior { 38488af8bbeSSebastian Andrzej Siewior return container_of(to_config_group(item), struct gadget_config_name, 38588af8bbeSSebastian Andrzej Siewior group); 38688af8bbeSSebastian Andrzej Siewior } 38788af8bbeSSebastian Andrzej Siewior 38888af8bbeSSebastian Andrzej Siewior static inline struct usb_function_instance *to_usb_function_instance( 38988af8bbeSSebastian Andrzej Siewior struct config_item *item) 39088af8bbeSSebastian Andrzej Siewior { 39188af8bbeSSebastian Andrzej Siewior return container_of(to_config_group(item), 39288af8bbeSSebastian Andrzej Siewior struct usb_function_instance, group); 39388af8bbeSSebastian Andrzej Siewior } 39488af8bbeSSebastian Andrzej Siewior 39588af8bbeSSebastian Andrzej Siewior static void gadget_info_attr_release(struct config_item *item) 39688af8bbeSSebastian Andrzej Siewior { 39788af8bbeSSebastian Andrzej Siewior struct gadget_info *gi = to_gadget_info(item); 39888af8bbeSSebastian Andrzej Siewior 39988af8bbeSSebastian Andrzej Siewior WARN_ON(!list_empty(&gi->cdev.configs)); 40088af8bbeSSebastian Andrzej Siewior WARN_ON(!list_empty(&gi->string_list)); 40188af8bbeSSebastian Andrzej Siewior WARN_ON(!list_empty(&gi->available_func)); 40288af8bbeSSebastian Andrzej Siewior kfree(gi->composite.gadget_driver.function); 4037c075538SChanh Nguyen kfree(gi->composite.gadget_driver.driver.name); 40488af8bbeSSebastian Andrzej Siewior kfree(gi); 40588af8bbeSSebastian Andrzej Siewior } 40688af8bbeSSebastian Andrzej Siewior 40788af8bbeSSebastian Andrzej Siewior static struct configfs_item_operations gadget_root_item_ops = { 40888af8bbeSSebastian Andrzej Siewior .release = gadget_info_attr_release, 40988af8bbeSSebastian Andrzej Siewior }; 41088af8bbeSSebastian Andrzej Siewior 41188af8bbeSSebastian Andrzej Siewior static void gadget_config_attr_release(struct config_item *item) 41288af8bbeSSebastian Andrzej Siewior { 41388af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg = to_config_usb_cfg(item); 41488af8bbeSSebastian Andrzej Siewior 41588af8bbeSSebastian Andrzej Siewior WARN_ON(!list_empty(&cfg->c.functions)); 41688af8bbeSSebastian Andrzej Siewior list_del(&cfg->c.list); 41788af8bbeSSebastian Andrzej Siewior kfree(cfg->c.label); 41888af8bbeSSebastian Andrzej Siewior kfree(cfg); 41988af8bbeSSebastian Andrzej Siewior } 42088af8bbeSSebastian Andrzej Siewior 42188af8bbeSSebastian Andrzej Siewior static int config_usb_cfg_link( 42288af8bbeSSebastian Andrzej Siewior struct config_item *usb_cfg_ci, 42388af8bbeSSebastian Andrzej Siewior struct config_item *usb_func_ci) 42488af8bbeSSebastian Andrzej Siewior { 42588af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg = to_config_usb_cfg(usb_cfg_ci); 426260d88b7SLinyu Yuan struct gadget_info *gi = cfg_to_gadget_info(cfg); 42788af8bbeSSebastian Andrzej Siewior 4285284acccSLinyu Yuan struct usb_function_instance *fi = 4295284acccSLinyu Yuan to_usb_function_instance(usb_func_ci); 43036f4c25cSJakob Koschel struct usb_function_instance *a_fi = NULL, *iter; 43188af8bbeSSebastian Andrzej Siewior struct usb_function *f; 43288af8bbeSSebastian Andrzej Siewior int ret; 43388af8bbeSSebastian Andrzej Siewior 43488af8bbeSSebastian Andrzej Siewior mutex_lock(&gi->lock); 43588af8bbeSSebastian Andrzej Siewior /* 43688af8bbeSSebastian Andrzej Siewior * Make sure this function is from within our _this_ gadget and not 43788af8bbeSSebastian Andrzej Siewior * from another gadget or a random directory. 43888af8bbeSSebastian Andrzej Siewior * Also a function instance can only be linked once. 43988af8bbeSSebastian Andrzej Siewior */ 44036f4c25cSJakob Koschel list_for_each_entry(iter, &gi->available_func, cfs_list) { 44136f4c25cSJakob Koschel if (iter != fi) 44236f4c25cSJakob Koschel continue; 44336f4c25cSJakob Koschel a_fi = iter; 44488af8bbeSSebastian Andrzej Siewior break; 44588af8bbeSSebastian Andrzej Siewior } 44636f4c25cSJakob Koschel if (!a_fi) { 44788af8bbeSSebastian Andrzej Siewior ret = -EINVAL; 44888af8bbeSSebastian Andrzej Siewior goto out; 44988af8bbeSSebastian Andrzej Siewior } 45088af8bbeSSebastian Andrzej Siewior 45188af8bbeSSebastian Andrzej Siewior list_for_each_entry(f, &cfg->func_list, list) { 45288af8bbeSSebastian Andrzej Siewior if (f->fi == fi) { 45388af8bbeSSebastian Andrzej Siewior ret = -EEXIST; 45488af8bbeSSebastian Andrzej Siewior goto out; 45588af8bbeSSebastian Andrzej Siewior } 45688af8bbeSSebastian Andrzej Siewior } 45788af8bbeSSebastian Andrzej Siewior 45888af8bbeSSebastian Andrzej Siewior f = usb_get_function(fi); 45988af8bbeSSebastian Andrzej Siewior if (IS_ERR(f)) { 46088af8bbeSSebastian Andrzej Siewior ret = PTR_ERR(f); 46188af8bbeSSebastian Andrzej Siewior goto out; 46288af8bbeSSebastian Andrzej Siewior } 46388af8bbeSSebastian Andrzej Siewior 46488af8bbeSSebastian Andrzej Siewior /* stash the function until we bind it to the gadget */ 46588af8bbeSSebastian Andrzej Siewior list_add_tail(&f->list, &cfg->func_list); 46688af8bbeSSebastian Andrzej Siewior ret = 0; 46788af8bbeSSebastian Andrzej Siewior out: 46888af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 46988af8bbeSSebastian Andrzej Siewior return ret; 47088af8bbeSSebastian Andrzej Siewior } 47188af8bbeSSebastian Andrzej Siewior 472e16769d4SAndrzej Pietrasiewicz static void config_usb_cfg_unlink( 47388af8bbeSSebastian Andrzej Siewior struct config_item *usb_cfg_ci, 47488af8bbeSSebastian Andrzej Siewior struct config_item *usb_func_ci) 47588af8bbeSSebastian Andrzej Siewior { 47688af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg = to_config_usb_cfg(usb_cfg_ci); 477260d88b7SLinyu Yuan struct gadget_info *gi = cfg_to_gadget_info(cfg); 47888af8bbeSSebastian Andrzej Siewior 4795284acccSLinyu Yuan struct usb_function_instance *fi = 4805284acccSLinyu Yuan to_usb_function_instance(usb_func_ci); 48188af8bbeSSebastian Andrzej Siewior struct usb_function *f; 48288af8bbeSSebastian Andrzej Siewior 48388af8bbeSSebastian Andrzej Siewior /* 48488af8bbeSSebastian Andrzej Siewior * ideally I would like to forbid to unlink functions while a gadget is 48588af8bbeSSebastian Andrzej Siewior * bound to an UDC. Since this isn't possible at the moment, we simply 48688af8bbeSSebastian Andrzej Siewior * force an unbind, the function is available here and then we can 48788af8bbeSSebastian Andrzej Siewior * remove the function. 48888af8bbeSSebastian Andrzej Siewior */ 48988af8bbeSSebastian Andrzej Siewior mutex_lock(&gi->lock); 490afdaadc3SRuslan Bilovol if (gi->composite.gadget_driver.udc_name) 49188af8bbeSSebastian Andrzej Siewior unregister_gadget(gi); 492afdaadc3SRuslan Bilovol WARN_ON(gi->composite.gadget_driver.udc_name); 49388af8bbeSSebastian Andrzej Siewior 49488af8bbeSSebastian Andrzej Siewior list_for_each_entry(f, &cfg->func_list, list) { 49588af8bbeSSebastian Andrzej Siewior if (f->fi == fi) { 49688af8bbeSSebastian Andrzej Siewior list_del(&f->list); 49788af8bbeSSebastian Andrzej Siewior usb_put_function(f); 49888af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 499e16769d4SAndrzej Pietrasiewicz return; 50088af8bbeSSebastian Andrzej Siewior } 50188af8bbeSSebastian Andrzej Siewior } 50288af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 50375bfe23aSDavid Rientjes WARN(1, "Unable to locate function to unbind\n"); 50488af8bbeSSebastian Andrzej Siewior } 50588af8bbeSSebastian Andrzej Siewior 50688af8bbeSSebastian Andrzej Siewior static struct configfs_item_operations gadget_config_item_ops = { 50788af8bbeSSebastian Andrzej Siewior .release = gadget_config_attr_release, 50888af8bbeSSebastian Andrzej Siewior .allow_link = config_usb_cfg_link, 50988af8bbeSSebastian Andrzej Siewior .drop_link = config_usb_cfg_unlink, 51088af8bbeSSebastian Andrzej Siewior }; 51188af8bbeSSebastian Andrzej Siewior 51288af8bbeSSebastian Andrzej Siewior 51345b6a73fSChristoph Hellwig static ssize_t gadget_config_desc_MaxPower_show(struct config_item *item, 51488af8bbeSSebastian Andrzej Siewior char *page) 51588af8bbeSSebastian Andrzej Siewior { 516c26f1c10SLinyu Yuan struct config_usb_cfg *cfg = to_config_usb_cfg(item); 517c26f1c10SLinyu Yuan 518c26f1c10SLinyu Yuan return sprintf(page, "%u\n", cfg->c.MaxPower); 51988af8bbeSSebastian Andrzej Siewior } 52088af8bbeSSebastian Andrzej Siewior 52145b6a73fSChristoph Hellwig static ssize_t gadget_config_desc_MaxPower_store(struct config_item *item, 52288af8bbeSSebastian Andrzej Siewior const char *page, size_t len) 52388af8bbeSSebastian Andrzej Siewior { 524c26f1c10SLinyu Yuan struct config_usb_cfg *cfg = to_config_usb_cfg(item); 52588af8bbeSSebastian Andrzej Siewior u16 val; 52688af8bbeSSebastian Andrzej Siewior int ret; 52788af8bbeSSebastian Andrzej Siewior ret = kstrtou16(page, 0, &val); 52888af8bbeSSebastian Andrzej Siewior if (ret) 52988af8bbeSSebastian Andrzej Siewior return ret; 53088af8bbeSSebastian Andrzej Siewior if (DIV_ROUND_UP(val, 8) > 0xff) 53188af8bbeSSebastian Andrzej Siewior return -ERANGE; 532c26f1c10SLinyu Yuan cfg->c.MaxPower = val; 53388af8bbeSSebastian Andrzej Siewior return len; 53488af8bbeSSebastian Andrzej Siewior } 53588af8bbeSSebastian Andrzej Siewior 53645b6a73fSChristoph Hellwig static ssize_t gadget_config_desc_bmAttributes_show(struct config_item *item, 53788af8bbeSSebastian Andrzej Siewior char *page) 53888af8bbeSSebastian Andrzej Siewior { 539c26f1c10SLinyu Yuan struct config_usb_cfg *cfg = to_config_usb_cfg(item); 540c26f1c10SLinyu Yuan 541c26f1c10SLinyu Yuan return sprintf(page, "0x%02x\n", cfg->c.bmAttributes); 54288af8bbeSSebastian Andrzej Siewior } 54388af8bbeSSebastian Andrzej Siewior 54445b6a73fSChristoph Hellwig static ssize_t gadget_config_desc_bmAttributes_store(struct config_item *item, 54588af8bbeSSebastian Andrzej Siewior const char *page, size_t len) 54688af8bbeSSebastian Andrzej Siewior { 547c26f1c10SLinyu Yuan struct config_usb_cfg *cfg = to_config_usb_cfg(item); 54888af8bbeSSebastian Andrzej Siewior u8 val; 54988af8bbeSSebastian Andrzej Siewior int ret; 55088af8bbeSSebastian Andrzej Siewior ret = kstrtou8(page, 0, &val); 55188af8bbeSSebastian Andrzej Siewior if (ret) 55288af8bbeSSebastian Andrzej Siewior return ret; 55388af8bbeSSebastian Andrzej Siewior if (!(val & USB_CONFIG_ATT_ONE)) 55488af8bbeSSebastian Andrzej Siewior return -EINVAL; 55588af8bbeSSebastian Andrzej Siewior if (val & ~(USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER | 55688af8bbeSSebastian Andrzej Siewior USB_CONFIG_ATT_WAKEUP)) 55788af8bbeSSebastian Andrzej Siewior return -EINVAL; 558c26f1c10SLinyu Yuan cfg->c.bmAttributes = val; 55988af8bbeSSebastian Andrzej Siewior return len; 56088af8bbeSSebastian Andrzej Siewior } 56188af8bbeSSebastian Andrzej Siewior 56245b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_config_desc_, MaxPower); 56345b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_config_desc_, bmAttributes); 56488af8bbeSSebastian Andrzej Siewior 56588af8bbeSSebastian Andrzej Siewior static struct configfs_attribute *gadget_config_attrs[] = { 56645b6a73fSChristoph Hellwig &gadget_config_desc_attr_MaxPower, 56745b6a73fSChristoph Hellwig &gadget_config_desc_attr_bmAttributes, 56888af8bbeSSebastian Andrzej Siewior NULL, 56988af8bbeSSebastian Andrzej Siewior }; 57088af8bbeSSebastian Andrzej Siewior 5714ad01412SBhumika Goyal static const struct config_item_type gadget_config_type = { 57288af8bbeSSebastian Andrzej Siewior .ct_item_ops = &gadget_config_item_ops, 57388af8bbeSSebastian Andrzej Siewior .ct_attrs = gadget_config_attrs, 57488af8bbeSSebastian Andrzej Siewior .ct_owner = THIS_MODULE, 57588af8bbeSSebastian Andrzej Siewior }; 57688af8bbeSSebastian Andrzej Siewior 5774ad01412SBhumika Goyal static const struct config_item_type gadget_root_type = { 57888af8bbeSSebastian Andrzej Siewior .ct_item_ops = &gadget_root_item_ops, 57988af8bbeSSebastian Andrzej Siewior .ct_attrs = gadget_root_attrs, 58088af8bbeSSebastian Andrzej Siewior .ct_owner = THIS_MODULE, 58188af8bbeSSebastian Andrzej Siewior }; 58288af8bbeSSebastian Andrzej Siewior 58388af8bbeSSebastian Andrzej Siewior static void composite_init_dev(struct usb_composite_dev *cdev) 58488af8bbeSSebastian Andrzej Siewior { 58588af8bbeSSebastian Andrzej Siewior spin_lock_init(&cdev->lock); 58688af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&cdev->configs); 58788af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&cdev->gstrings); 58888af8bbeSSebastian Andrzej Siewior } 58988af8bbeSSebastian Andrzej Siewior 59088af8bbeSSebastian Andrzej Siewior static struct config_group *function_make( 59188af8bbeSSebastian Andrzej Siewior struct config_group *group, 59288af8bbeSSebastian Andrzej Siewior const char *name) 59388af8bbeSSebastian Andrzej Siewior { 59488af8bbeSSebastian Andrzej Siewior struct gadget_info *gi; 59588af8bbeSSebastian Andrzej Siewior struct usb_function_instance *fi; 59688af8bbeSSebastian Andrzej Siewior char buf[MAX_NAME_LEN]; 59788af8bbeSSebastian Andrzej Siewior char *func_name; 59888af8bbeSSebastian Andrzej Siewior char *instance_name; 59988af8bbeSSebastian Andrzej Siewior int ret; 60088af8bbeSSebastian Andrzej Siewior 60188af8bbeSSebastian Andrzej Siewior ret = snprintf(buf, MAX_NAME_LEN, "%s", name); 60288af8bbeSSebastian Andrzej Siewior if (ret >= MAX_NAME_LEN) 60388af8bbeSSebastian Andrzej Siewior return ERR_PTR(-ENAMETOOLONG); 60488af8bbeSSebastian Andrzej Siewior 60588af8bbeSSebastian Andrzej Siewior func_name = buf; 60688af8bbeSSebastian Andrzej Siewior instance_name = strchr(func_name, '.'); 60788af8bbeSSebastian Andrzej Siewior if (!instance_name) { 60888af8bbeSSebastian Andrzej Siewior pr_err("Unable to locate . in FUNC.INSTANCE\n"); 60988af8bbeSSebastian Andrzej Siewior return ERR_PTR(-EINVAL); 61088af8bbeSSebastian Andrzej Siewior } 61188af8bbeSSebastian Andrzej Siewior *instance_name = '\0'; 61288af8bbeSSebastian Andrzej Siewior instance_name++; 61388af8bbeSSebastian Andrzej Siewior 61488af8bbeSSebastian Andrzej Siewior fi = usb_get_function_instance(func_name); 61588af8bbeSSebastian Andrzej Siewior if (IS_ERR(fi)) 616a3469411SDuan Jiong return ERR_CAST(fi); 61788af8bbeSSebastian Andrzej Siewior 6183958b792SNicolas Iooss ret = config_item_set_name(&fi->group.cg_item, "%s", name); 61988af8bbeSSebastian Andrzej Siewior if (ret) { 62088af8bbeSSebastian Andrzej Siewior usb_put_function_instance(fi); 62188af8bbeSSebastian Andrzej Siewior return ERR_PTR(ret); 62288af8bbeSSebastian Andrzej Siewior } 6231933861dSAndrzej Pietrasiewicz if (fi->set_inst_name) { 6241933861dSAndrzej Pietrasiewicz ret = fi->set_inst_name(fi, instance_name); 6251933861dSAndrzej Pietrasiewicz if (ret) { 6261933861dSAndrzej Pietrasiewicz usb_put_function_instance(fi); 6271933861dSAndrzej Pietrasiewicz return ERR_PTR(ret); 6281933861dSAndrzej Pietrasiewicz } 6291933861dSAndrzej Pietrasiewicz } 63088af8bbeSSebastian Andrzej Siewior 63188af8bbeSSebastian Andrzej Siewior gi = container_of(group, struct gadget_info, functions_group); 63288af8bbeSSebastian Andrzej Siewior 63388af8bbeSSebastian Andrzej Siewior mutex_lock(&gi->lock); 63488af8bbeSSebastian Andrzej Siewior list_add_tail(&fi->cfs_list, &gi->available_func); 63588af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 63688af8bbeSSebastian Andrzej Siewior return &fi->group; 63788af8bbeSSebastian Andrzej Siewior } 63888af8bbeSSebastian Andrzej Siewior 63988af8bbeSSebastian Andrzej Siewior static void function_drop( 64088af8bbeSSebastian Andrzej Siewior struct config_group *group, 64188af8bbeSSebastian Andrzej Siewior struct config_item *item) 64288af8bbeSSebastian Andrzej Siewior { 64388af8bbeSSebastian Andrzej Siewior struct usb_function_instance *fi = to_usb_function_instance(item); 64488af8bbeSSebastian Andrzej Siewior struct gadget_info *gi; 64588af8bbeSSebastian Andrzej Siewior 64688af8bbeSSebastian Andrzej Siewior gi = container_of(group, struct gadget_info, functions_group); 64788af8bbeSSebastian Andrzej Siewior 64888af8bbeSSebastian Andrzej Siewior mutex_lock(&gi->lock); 64988af8bbeSSebastian Andrzej Siewior list_del(&fi->cfs_list); 65088af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 65188af8bbeSSebastian Andrzej Siewior config_item_put(item); 65288af8bbeSSebastian Andrzej Siewior } 65388af8bbeSSebastian Andrzej Siewior 65488af8bbeSSebastian Andrzej Siewior static struct configfs_group_operations functions_ops = { 65588af8bbeSSebastian Andrzej Siewior .make_group = &function_make, 65688af8bbeSSebastian Andrzej Siewior .drop_item = &function_drop, 65788af8bbeSSebastian Andrzej Siewior }; 65888af8bbeSSebastian Andrzej Siewior 6594ad01412SBhumika Goyal static const struct config_item_type functions_type = { 66088af8bbeSSebastian Andrzej Siewior .ct_group_ops = &functions_ops, 66188af8bbeSSebastian Andrzej Siewior .ct_owner = THIS_MODULE, 66288af8bbeSSebastian Andrzej Siewior }; 66388af8bbeSSebastian Andrzej Siewior 66488af8bbeSSebastian Andrzej Siewior GS_STRINGS_RW(gadget_config_name, configuration); 66588af8bbeSSebastian Andrzej Siewior 66688af8bbeSSebastian Andrzej Siewior static struct configfs_attribute *gadget_config_name_langid_attrs[] = { 66745b6a73fSChristoph Hellwig &gadget_config_name_attr_configuration, 66888af8bbeSSebastian Andrzej Siewior NULL, 66988af8bbeSSebastian Andrzej Siewior }; 67088af8bbeSSebastian Andrzej Siewior 67188af8bbeSSebastian Andrzej Siewior static void gadget_config_name_attr_release(struct config_item *item) 67288af8bbeSSebastian Andrzej Siewior { 67388af8bbeSSebastian Andrzej Siewior struct gadget_config_name *cn = to_gadget_config_name(item); 67488af8bbeSSebastian Andrzej Siewior 67588af8bbeSSebastian Andrzej Siewior kfree(cn->configuration); 67688af8bbeSSebastian Andrzej Siewior 67788af8bbeSSebastian Andrzej Siewior list_del(&cn->list); 67888af8bbeSSebastian Andrzej Siewior kfree(cn); 67988af8bbeSSebastian Andrzej Siewior } 68088af8bbeSSebastian Andrzej Siewior 68188af8bbeSSebastian Andrzej Siewior USB_CONFIG_STRING_RW_OPS(gadget_config_name); 68288af8bbeSSebastian Andrzej Siewior USB_CONFIG_STRINGS_LANG(gadget_config_name, config_usb_cfg); 68388af8bbeSSebastian Andrzej Siewior 68488af8bbeSSebastian Andrzej Siewior static struct config_group *config_desc_make( 68588af8bbeSSebastian Andrzej Siewior struct config_group *group, 68688af8bbeSSebastian Andrzej Siewior const char *name) 68788af8bbeSSebastian Andrzej Siewior { 68888af8bbeSSebastian Andrzej Siewior struct gadget_info *gi; 68988af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg; 69088af8bbeSSebastian Andrzej Siewior char buf[MAX_NAME_LEN]; 69188af8bbeSSebastian Andrzej Siewior char *num_str; 69288af8bbeSSebastian Andrzej Siewior u8 num; 69388af8bbeSSebastian Andrzej Siewior int ret; 69488af8bbeSSebastian Andrzej Siewior 69588af8bbeSSebastian Andrzej Siewior gi = container_of(group, struct gadget_info, configs_group); 69688af8bbeSSebastian Andrzej Siewior ret = snprintf(buf, MAX_NAME_LEN, "%s", name); 69788af8bbeSSebastian Andrzej Siewior if (ret >= MAX_NAME_LEN) 69888af8bbeSSebastian Andrzej Siewior return ERR_PTR(-ENAMETOOLONG); 69988af8bbeSSebastian Andrzej Siewior 70088af8bbeSSebastian Andrzej Siewior num_str = strchr(buf, '.'); 70188af8bbeSSebastian Andrzej Siewior if (!num_str) { 70288af8bbeSSebastian Andrzej Siewior pr_err("Unable to locate . in name.bConfigurationValue\n"); 70388af8bbeSSebastian Andrzej Siewior return ERR_PTR(-EINVAL); 70488af8bbeSSebastian Andrzej Siewior } 70588af8bbeSSebastian Andrzej Siewior 70688af8bbeSSebastian Andrzej Siewior *num_str = '\0'; 70788af8bbeSSebastian Andrzej Siewior num_str++; 70888af8bbeSSebastian Andrzej Siewior 70988af8bbeSSebastian Andrzej Siewior if (!strlen(buf)) 71088af8bbeSSebastian Andrzej Siewior return ERR_PTR(-EINVAL); 71188af8bbeSSebastian Andrzej Siewior 71288af8bbeSSebastian Andrzej Siewior ret = kstrtou8(num_str, 0, &num); 71388af8bbeSSebastian Andrzej Siewior if (ret) 71488af8bbeSSebastian Andrzej Siewior return ERR_PTR(ret); 71588af8bbeSSebastian Andrzej Siewior 71688af8bbeSSebastian Andrzej Siewior cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); 71788af8bbeSSebastian Andrzej Siewior if (!cfg) 71888af8bbeSSebastian Andrzej Siewior return ERR_PTR(-ENOMEM); 71988af8bbeSSebastian Andrzej Siewior cfg->c.label = kstrdup(buf, GFP_KERNEL); 72088af8bbeSSebastian Andrzej Siewior if (!cfg->c.label) { 72188af8bbeSSebastian Andrzej Siewior ret = -ENOMEM; 72288af8bbeSSebastian Andrzej Siewior goto err; 72388af8bbeSSebastian Andrzej Siewior } 72488af8bbeSSebastian Andrzej Siewior cfg->c.bConfigurationValue = num; 72588af8bbeSSebastian Andrzej Siewior cfg->c.MaxPower = CONFIG_USB_GADGET_VBUS_DRAW; 72688af8bbeSSebastian Andrzej Siewior cfg->c.bmAttributes = USB_CONFIG_ATT_ONE; 72788af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&cfg->string_list); 72888af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&cfg->func_list); 72988af8bbeSSebastian Andrzej Siewior 73088af8bbeSSebastian Andrzej Siewior config_group_init_type_name(&cfg->group, name, 73188af8bbeSSebastian Andrzej Siewior &gadget_config_type); 7321ae1602dSChristoph Hellwig 73388af8bbeSSebastian Andrzej Siewior config_group_init_type_name(&cfg->strings_group, "strings", 73488af8bbeSSebastian Andrzej Siewior &gadget_config_name_strings_type); 7351ae1602dSChristoph Hellwig configfs_add_default_group(&cfg->strings_group, &cfg->group); 73688af8bbeSSebastian Andrzej Siewior 73788af8bbeSSebastian Andrzej Siewior ret = usb_add_config_only(&gi->cdev, &cfg->c); 73888af8bbeSSebastian Andrzej Siewior if (ret) 73988af8bbeSSebastian Andrzej Siewior goto err; 74088af8bbeSSebastian Andrzej Siewior 74188af8bbeSSebastian Andrzej Siewior return &cfg->group; 74288af8bbeSSebastian Andrzej Siewior err: 74388af8bbeSSebastian Andrzej Siewior kfree(cfg->c.label); 74488af8bbeSSebastian Andrzej Siewior kfree(cfg); 74588af8bbeSSebastian Andrzej Siewior return ERR_PTR(ret); 74688af8bbeSSebastian Andrzej Siewior } 74788af8bbeSSebastian Andrzej Siewior 74888af8bbeSSebastian Andrzej Siewior static void config_desc_drop( 74988af8bbeSSebastian Andrzej Siewior struct config_group *group, 75088af8bbeSSebastian Andrzej Siewior struct config_item *item) 75188af8bbeSSebastian Andrzej Siewior { 75288af8bbeSSebastian Andrzej Siewior config_item_put(item); 75388af8bbeSSebastian Andrzej Siewior } 75488af8bbeSSebastian Andrzej Siewior 75588af8bbeSSebastian Andrzej Siewior static struct configfs_group_operations config_desc_ops = { 75688af8bbeSSebastian Andrzej Siewior .make_group = &config_desc_make, 75788af8bbeSSebastian Andrzej Siewior .drop_item = &config_desc_drop, 75888af8bbeSSebastian Andrzej Siewior }; 75988af8bbeSSebastian Andrzej Siewior 7604ad01412SBhumika Goyal static const struct config_item_type config_desc_type = { 76188af8bbeSSebastian Andrzej Siewior .ct_group_ops = &config_desc_ops, 76288af8bbeSSebastian Andrzej Siewior .ct_owner = THIS_MODULE, 76388af8bbeSSebastian Andrzej Siewior }; 76488af8bbeSSebastian Andrzej Siewior 76588af8bbeSSebastian Andrzej Siewior GS_STRINGS_RW(gadget_strings, manufacturer); 76688af8bbeSSebastian Andrzej Siewior GS_STRINGS_RW(gadget_strings, product); 76788af8bbeSSebastian Andrzej Siewior GS_STRINGS_RW(gadget_strings, serialnumber); 76888af8bbeSSebastian Andrzej Siewior 76988af8bbeSSebastian Andrzej Siewior static struct configfs_attribute *gadget_strings_langid_attrs[] = { 77045b6a73fSChristoph Hellwig &gadget_strings_attr_manufacturer, 77145b6a73fSChristoph Hellwig &gadget_strings_attr_product, 77245b6a73fSChristoph Hellwig &gadget_strings_attr_serialnumber, 77388af8bbeSSebastian Andrzej Siewior NULL, 77488af8bbeSSebastian Andrzej Siewior }; 77588af8bbeSSebastian Andrzej Siewior 77688af8bbeSSebastian Andrzej Siewior static void gadget_strings_attr_release(struct config_item *item) 77788af8bbeSSebastian Andrzej Siewior { 77888af8bbeSSebastian Andrzej Siewior struct gadget_strings *gs = to_gadget_strings(item); 77988af8bbeSSebastian Andrzej Siewior 78088af8bbeSSebastian Andrzej Siewior kfree(gs->manufacturer); 78188af8bbeSSebastian Andrzej Siewior kfree(gs->product); 78288af8bbeSSebastian Andrzej Siewior kfree(gs->serialnumber); 78388af8bbeSSebastian Andrzej Siewior 78488af8bbeSSebastian Andrzej Siewior list_del(&gs->list); 78588af8bbeSSebastian Andrzej Siewior kfree(gs); 78688af8bbeSSebastian Andrzej Siewior } 78788af8bbeSSebastian Andrzej Siewior 78888af8bbeSSebastian Andrzej Siewior USB_CONFIG_STRING_RW_OPS(gadget_strings); 78988af8bbeSSebastian Andrzej Siewior USB_CONFIG_STRINGS_LANG(gadget_strings, gadget_info); 79088af8bbeSSebastian Andrzej Siewior 79193c47394SJó Ágila Bitsch static inline struct gadget_info *webusb_item_to_gadget_info( 79293c47394SJó Ágila Bitsch struct config_item *item) 79393c47394SJó Ágila Bitsch { 79493c47394SJó Ágila Bitsch return container_of(to_config_group(item), 79593c47394SJó Ágila Bitsch struct gadget_info, webusb_group); 79693c47394SJó Ágila Bitsch } 79793c47394SJó Ágila Bitsch 79893c47394SJó Ágila Bitsch static ssize_t webusb_use_show(struct config_item *item, char *page) 79993c47394SJó Ágila Bitsch { 80093c47394SJó Ágila Bitsch return sysfs_emit(page, "%d\n", 80193c47394SJó Ágila Bitsch webusb_item_to_gadget_info(item)->use_webusb); 80293c47394SJó Ágila Bitsch } 80393c47394SJó Ágila Bitsch 80493c47394SJó Ágila Bitsch static ssize_t webusb_use_store(struct config_item *item, const char *page, 80593c47394SJó Ágila Bitsch size_t len) 80693c47394SJó Ágila Bitsch { 80793c47394SJó Ágila Bitsch struct gadget_info *gi = webusb_item_to_gadget_info(item); 80893c47394SJó Ágila Bitsch int ret; 80993c47394SJó Ágila Bitsch bool use; 81093c47394SJó Ágila Bitsch 81193c47394SJó Ágila Bitsch ret = kstrtobool(page, &use); 812*6f7fb48dSAndy Shevchenko if (ret) 813*6f7fb48dSAndy Shevchenko return ret; 814*6f7fb48dSAndy Shevchenko 815*6f7fb48dSAndy Shevchenko mutex_lock(&gi->lock); 81693c47394SJó Ágila Bitsch gi->use_webusb = use; 81793c47394SJó Ágila Bitsch mutex_unlock(&gi->lock); 81893c47394SJó Ágila Bitsch 819*6f7fb48dSAndy Shevchenko return len; 82093c47394SJó Ágila Bitsch } 82193c47394SJó Ágila Bitsch 82293c47394SJó Ágila Bitsch static ssize_t webusb_bcdVersion_show(struct config_item *item, char *page) 82393c47394SJó Ágila Bitsch { 82493c47394SJó Ágila Bitsch return sysfs_emit(page, "0x%04x\n", 82593c47394SJó Ágila Bitsch webusb_item_to_gadget_info(item)->bcd_webusb_version); 82693c47394SJó Ágila Bitsch } 82793c47394SJó Ágila Bitsch 82893c47394SJó Ágila Bitsch static ssize_t webusb_bcdVersion_store(struct config_item *item, 82993c47394SJó Ágila Bitsch const char *page, size_t len) 83093c47394SJó Ágila Bitsch { 83193c47394SJó Ágila Bitsch struct gadget_info *gi = webusb_item_to_gadget_info(item); 83293c47394SJó Ágila Bitsch u16 bcdVersion; 83393c47394SJó Ágila Bitsch int ret; 83493c47394SJó Ágila Bitsch 83593c47394SJó Ágila Bitsch ret = kstrtou16(page, 0, &bcdVersion); 83693c47394SJó Ágila Bitsch if (ret) 837*6f7fb48dSAndy Shevchenko return ret; 838*6f7fb48dSAndy Shevchenko 839*6f7fb48dSAndy Shevchenko mutex_lock(&gi->lock); 840*6f7fb48dSAndy Shevchenko 84193c47394SJó Ágila Bitsch ret = is_valid_bcd(bcdVersion); 84293c47394SJó Ágila Bitsch if (ret) 84393c47394SJó Ágila Bitsch goto out; 84493c47394SJó Ágila Bitsch 84593c47394SJó Ágila Bitsch gi->bcd_webusb_version = bcdVersion; 84693c47394SJó Ágila Bitsch ret = len; 84793c47394SJó Ágila Bitsch 84893c47394SJó Ágila Bitsch out: 84993c47394SJó Ágila Bitsch mutex_unlock(&gi->lock); 85093c47394SJó Ágila Bitsch 85193c47394SJó Ágila Bitsch return ret; 85293c47394SJó Ágila Bitsch } 85393c47394SJó Ágila Bitsch 85493c47394SJó Ágila Bitsch static ssize_t webusb_bVendorCode_show(struct config_item *item, char *page) 85593c47394SJó Ágila Bitsch { 85693c47394SJó Ágila Bitsch return sysfs_emit(page, "0x%02x\n", 85793c47394SJó Ágila Bitsch webusb_item_to_gadget_info(item)->b_webusb_vendor_code); 85893c47394SJó Ágila Bitsch } 85993c47394SJó Ágila Bitsch 86093c47394SJó Ágila Bitsch static ssize_t webusb_bVendorCode_store(struct config_item *item, 86193c47394SJó Ágila Bitsch const char *page, size_t len) 86293c47394SJó Ágila Bitsch { 86393c47394SJó Ágila Bitsch struct gadget_info *gi = webusb_item_to_gadget_info(item); 86493c47394SJó Ágila Bitsch int ret; 86593c47394SJó Ágila Bitsch u8 b_vendor_code; 86693c47394SJó Ágila Bitsch 86793c47394SJó Ágila Bitsch ret = kstrtou8(page, 0, &b_vendor_code); 868*6f7fb48dSAndy Shevchenko if (ret) 869*6f7fb48dSAndy Shevchenko return ret; 870*6f7fb48dSAndy Shevchenko 871*6f7fb48dSAndy Shevchenko mutex_lock(&gi->lock); 87293c47394SJó Ágila Bitsch gi->b_webusb_vendor_code = b_vendor_code; 87393c47394SJó Ágila Bitsch mutex_unlock(&gi->lock); 87493c47394SJó Ágila Bitsch 875*6f7fb48dSAndy Shevchenko return len; 87693c47394SJó Ágila Bitsch } 87793c47394SJó Ágila Bitsch 87893c47394SJó Ágila Bitsch static ssize_t webusb_landingPage_show(struct config_item *item, char *page) 87993c47394SJó Ágila Bitsch { 88093c47394SJó Ágila Bitsch return sysfs_emit(page, "%s\n", webusb_item_to_gadget_info(item)->landing_page); 88193c47394SJó Ágila Bitsch } 88293c47394SJó Ágila Bitsch 88393c47394SJó Ágila Bitsch static ssize_t webusb_landingPage_store(struct config_item *item, const char *page, 88493c47394SJó Ágila Bitsch size_t len) 88593c47394SJó Ágila Bitsch { 88693c47394SJó Ágila Bitsch struct gadget_info *gi = webusb_item_to_gadget_info(item); 88793c47394SJó Ágila Bitsch unsigned int bytes_to_strip = 0; 88893c47394SJó Ágila Bitsch int l = len; 88993c47394SJó Ágila Bitsch 89093c47394SJó Ágila Bitsch if (page[l - 1] == '\n') { 89193c47394SJó Ágila Bitsch --l; 89293c47394SJó Ágila Bitsch ++bytes_to_strip; 89393c47394SJó Ágila Bitsch } 89493c47394SJó Ágila Bitsch 89593c47394SJó Ágila Bitsch if (l > sizeof(gi->landing_page)) { 89693c47394SJó Ágila Bitsch pr_err("webusb: landingPage URL too long\n"); 89793c47394SJó Ágila Bitsch return -EINVAL; 89893c47394SJó Ágila Bitsch } 89993c47394SJó Ágila Bitsch 90093c47394SJó Ágila Bitsch // validation 90193c47394SJó Ágila Bitsch if (strncasecmp(page, "https://", 8) == 0) 90293c47394SJó Ágila Bitsch bytes_to_strip = 8; 90393c47394SJó Ágila Bitsch else if (strncasecmp(page, "http://", 7) == 0) 90493c47394SJó Ágila Bitsch bytes_to_strip = 7; 90593c47394SJó Ágila Bitsch else 90693c47394SJó Ágila Bitsch bytes_to_strip = 0; 90793c47394SJó Ágila Bitsch 90893c47394SJó Ágila Bitsch if (l > U8_MAX - WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + bytes_to_strip) { 90993c47394SJó Ágila Bitsch pr_err("webusb: landingPage URL %d bytes too long for given URL scheme\n", 91093c47394SJó Ágila Bitsch l - U8_MAX + WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH - bytes_to_strip); 91193c47394SJó Ágila Bitsch return -EINVAL; 91293c47394SJó Ágila Bitsch } 91393c47394SJó Ágila Bitsch 91493c47394SJó Ágila Bitsch mutex_lock(&gi->lock); 91593c47394SJó Ágila Bitsch // ensure 0 bytes are set, in case the new landing page is shorter then the old one. 91693c47394SJó Ágila Bitsch memset(gi->landing_page, 0, sizeof(gi->landing_page)); 91793c47394SJó Ágila Bitsch memcpy(gi->landing_page, page, l); 91893c47394SJó Ágila Bitsch mutex_unlock(&gi->lock); 91993c47394SJó Ágila Bitsch 92093c47394SJó Ágila Bitsch return len; 92193c47394SJó Ágila Bitsch } 92293c47394SJó Ágila Bitsch 92393c47394SJó Ágila Bitsch CONFIGFS_ATTR(webusb_, use); 92493c47394SJó Ágila Bitsch CONFIGFS_ATTR(webusb_, bVendorCode); 92593c47394SJó Ágila Bitsch CONFIGFS_ATTR(webusb_, bcdVersion); 92693c47394SJó Ágila Bitsch CONFIGFS_ATTR(webusb_, landingPage); 92793c47394SJó Ágila Bitsch 92893c47394SJó Ágila Bitsch static struct configfs_attribute *webusb_attrs[] = { 92993c47394SJó Ágila Bitsch &webusb_attr_use, 93093c47394SJó Ágila Bitsch &webusb_attr_bcdVersion, 93193c47394SJó Ágila Bitsch &webusb_attr_bVendorCode, 93293c47394SJó Ágila Bitsch &webusb_attr_landingPage, 93393c47394SJó Ágila Bitsch NULL, 93493c47394SJó Ágila Bitsch }; 93593c47394SJó Ágila Bitsch 93693c47394SJó Ágila Bitsch static struct config_item_type webusb_type = { 93793c47394SJó Ágila Bitsch .ct_attrs = webusb_attrs, 93893c47394SJó Ágila Bitsch .ct_owner = THIS_MODULE, 93993c47394SJó Ágila Bitsch }; 94093c47394SJó Ágila Bitsch 94145b6a73fSChristoph Hellwig static inline struct gadget_info *os_desc_item_to_gadget_info( 94245b6a73fSChristoph Hellwig struct config_item *item) 94387213d38SAndrzej Pietrasiewicz { 944167a799cSLinyu Yuan return container_of(to_config_group(item), 945167a799cSLinyu Yuan struct gadget_info, os_desc_group); 94687213d38SAndrzej Pietrasiewicz } 94787213d38SAndrzej Pietrasiewicz 94845b6a73fSChristoph Hellwig static ssize_t os_desc_use_show(struct config_item *item, char *page) 94945b6a73fSChristoph Hellwig { 950e800e8cbSStefan Agner return sprintf(page, "%d\n", 95145b6a73fSChristoph Hellwig os_desc_item_to_gadget_info(item)->use_os_desc); 95245b6a73fSChristoph Hellwig } 95345b6a73fSChristoph Hellwig 95445b6a73fSChristoph Hellwig static ssize_t os_desc_use_store(struct config_item *item, const char *page, 95587213d38SAndrzej Pietrasiewicz size_t len) 95687213d38SAndrzej Pietrasiewicz { 95745b6a73fSChristoph Hellwig struct gadget_info *gi = os_desc_item_to_gadget_info(item); 95887213d38SAndrzej Pietrasiewicz int ret; 95987213d38SAndrzej Pietrasiewicz bool use; 96087213d38SAndrzej Pietrasiewicz 961a8bc8cc1SChristophe JAILLET ret = kstrtobool(page, &use); 962*6f7fb48dSAndy Shevchenko if (ret) 963*6f7fb48dSAndy Shevchenko return ret; 964*6f7fb48dSAndy Shevchenko 965*6f7fb48dSAndy Shevchenko mutex_lock(&gi->lock); 96687213d38SAndrzej Pietrasiewicz gi->use_os_desc = use; 96787213d38SAndrzej Pietrasiewicz mutex_unlock(&gi->lock); 96887213d38SAndrzej Pietrasiewicz 969*6f7fb48dSAndy Shevchenko return len; 97087213d38SAndrzej Pietrasiewicz } 97187213d38SAndrzej Pietrasiewicz 97245b6a73fSChristoph Hellwig static ssize_t os_desc_b_vendor_code_show(struct config_item *item, char *page) 97387213d38SAndrzej Pietrasiewicz { 974e800e8cbSStefan Agner return sprintf(page, "0x%02x\n", 97545b6a73fSChristoph Hellwig os_desc_item_to_gadget_info(item)->b_vendor_code); 97687213d38SAndrzej Pietrasiewicz } 97787213d38SAndrzej Pietrasiewicz 97845b6a73fSChristoph Hellwig static ssize_t os_desc_b_vendor_code_store(struct config_item *item, 97987213d38SAndrzej Pietrasiewicz const char *page, size_t len) 98087213d38SAndrzej Pietrasiewicz { 98145b6a73fSChristoph Hellwig struct gadget_info *gi = os_desc_item_to_gadget_info(item); 98287213d38SAndrzej Pietrasiewicz int ret; 98387213d38SAndrzej Pietrasiewicz u8 b_vendor_code; 98487213d38SAndrzej Pietrasiewicz 98587213d38SAndrzej Pietrasiewicz ret = kstrtou8(page, 0, &b_vendor_code); 986*6f7fb48dSAndy Shevchenko if (ret) 987*6f7fb48dSAndy Shevchenko return ret; 988*6f7fb48dSAndy Shevchenko 989*6f7fb48dSAndy Shevchenko mutex_lock(&gi->lock); 99087213d38SAndrzej Pietrasiewicz gi->b_vendor_code = b_vendor_code; 99187213d38SAndrzej Pietrasiewicz mutex_unlock(&gi->lock); 99287213d38SAndrzej Pietrasiewicz 993*6f7fb48dSAndy Shevchenko return len; 99487213d38SAndrzej Pietrasiewicz } 99587213d38SAndrzej Pietrasiewicz 99645b6a73fSChristoph Hellwig static ssize_t os_desc_qw_sign_show(struct config_item *item, char *page) 99787213d38SAndrzej Pietrasiewicz { 99845b6a73fSChristoph Hellwig struct gadget_info *gi = os_desc_item_to_gadget_info(item); 99976180d71SStefan Agner int res; 100087213d38SAndrzej Pietrasiewicz 100176180d71SStefan Agner res = utf16s_to_utf8s((wchar_t *) gi->qw_sign, OS_STRING_QW_SIGN_LEN, 100276180d71SStefan Agner UTF16_LITTLE_ENDIAN, page, PAGE_SIZE - 1); 100376180d71SStefan Agner page[res++] = '\n'; 100476180d71SStefan Agner 100576180d71SStefan Agner return res; 100687213d38SAndrzej Pietrasiewicz } 100787213d38SAndrzej Pietrasiewicz 100845b6a73fSChristoph Hellwig static ssize_t os_desc_qw_sign_store(struct config_item *item, const char *page, 100987213d38SAndrzej Pietrasiewicz size_t len) 101087213d38SAndrzej Pietrasiewicz { 101145b6a73fSChristoph Hellwig struct gadget_info *gi = os_desc_item_to_gadget_info(item); 101287213d38SAndrzej Pietrasiewicz int res, l; 101387213d38SAndrzej Pietrasiewicz 101487213d38SAndrzej Pietrasiewicz l = min((int)len, OS_STRING_QW_SIGN_LEN >> 1); 101587213d38SAndrzej Pietrasiewicz if (page[l - 1] == '\n') 101687213d38SAndrzej Pietrasiewicz --l; 101787213d38SAndrzej Pietrasiewicz 101887213d38SAndrzej Pietrasiewicz mutex_lock(&gi->lock); 101987213d38SAndrzej Pietrasiewicz res = utf8s_to_utf16s(page, l, 102087213d38SAndrzej Pietrasiewicz UTF16_LITTLE_ENDIAN, (wchar_t *) gi->qw_sign, 102187213d38SAndrzej Pietrasiewicz OS_STRING_QW_SIGN_LEN); 102287213d38SAndrzej Pietrasiewicz if (res > 0) 102387213d38SAndrzej Pietrasiewicz res = len; 102487213d38SAndrzej Pietrasiewicz mutex_unlock(&gi->lock); 102587213d38SAndrzej Pietrasiewicz 102687213d38SAndrzej Pietrasiewicz return res; 102787213d38SAndrzej Pietrasiewicz } 102887213d38SAndrzej Pietrasiewicz 102945b6a73fSChristoph Hellwig CONFIGFS_ATTR(os_desc_, use); 103045b6a73fSChristoph Hellwig CONFIGFS_ATTR(os_desc_, b_vendor_code); 103145b6a73fSChristoph Hellwig CONFIGFS_ATTR(os_desc_, qw_sign); 103287213d38SAndrzej Pietrasiewicz 103387213d38SAndrzej Pietrasiewicz static struct configfs_attribute *os_desc_attrs[] = { 103445b6a73fSChristoph Hellwig &os_desc_attr_use, 103545b6a73fSChristoph Hellwig &os_desc_attr_b_vendor_code, 103645b6a73fSChristoph Hellwig &os_desc_attr_qw_sign, 103787213d38SAndrzej Pietrasiewicz NULL, 103887213d38SAndrzej Pietrasiewicz }; 103987213d38SAndrzej Pietrasiewicz 1040da424314SAndrzej Pietrasiewicz static int os_desc_link(struct config_item *os_desc_ci, 1041da424314SAndrzej Pietrasiewicz struct config_item *usb_cfg_ci) 1042da424314SAndrzej Pietrasiewicz { 1043167a799cSLinyu Yuan struct gadget_info *gi = os_desc_item_to_gadget_info(os_desc_ci); 1044da424314SAndrzej Pietrasiewicz struct usb_composite_dev *cdev = &gi->cdev; 10455d143ec4SLinyu Yuan struct config_usb_cfg *c_target = to_config_usb_cfg(usb_cfg_ci); 104636f4c25cSJakob Koschel struct usb_configuration *c = NULL, *iter; 1047da424314SAndrzej Pietrasiewicz int ret; 1048da424314SAndrzej Pietrasiewicz 1049da424314SAndrzej Pietrasiewicz mutex_lock(&gi->lock); 105036f4c25cSJakob Koschel list_for_each_entry(iter, &cdev->configs, list) { 105136f4c25cSJakob Koschel if (iter != &c_target->c) 105236f4c25cSJakob Koschel continue; 105336f4c25cSJakob Koschel c = iter; 1054da424314SAndrzej Pietrasiewicz break; 1055da424314SAndrzej Pietrasiewicz } 105636f4c25cSJakob Koschel if (!c) { 1057da424314SAndrzej Pietrasiewicz ret = -EINVAL; 1058da424314SAndrzej Pietrasiewicz goto out; 1059da424314SAndrzej Pietrasiewicz } 1060da424314SAndrzej Pietrasiewicz 1061da424314SAndrzej Pietrasiewicz if (cdev->os_desc_config) { 1062da424314SAndrzej Pietrasiewicz ret = -EBUSY; 1063da424314SAndrzej Pietrasiewicz goto out; 1064da424314SAndrzej Pietrasiewicz } 1065da424314SAndrzej Pietrasiewicz 1066da424314SAndrzej Pietrasiewicz cdev->os_desc_config = &c_target->c; 1067da424314SAndrzej Pietrasiewicz ret = 0; 1068da424314SAndrzej Pietrasiewicz 1069da424314SAndrzej Pietrasiewicz out: 1070da424314SAndrzej Pietrasiewicz mutex_unlock(&gi->lock); 1071da424314SAndrzej Pietrasiewicz return ret; 1072da424314SAndrzej Pietrasiewicz } 1073da424314SAndrzej Pietrasiewicz 1074e16769d4SAndrzej Pietrasiewicz static void os_desc_unlink(struct config_item *os_desc_ci, 1075da424314SAndrzej Pietrasiewicz struct config_item *usb_cfg_ci) 1076da424314SAndrzej Pietrasiewicz { 1077167a799cSLinyu Yuan struct gadget_info *gi = os_desc_item_to_gadget_info(os_desc_ci); 1078da424314SAndrzej Pietrasiewicz struct usb_composite_dev *cdev = &gi->cdev; 1079da424314SAndrzej Pietrasiewicz 1080da424314SAndrzej Pietrasiewicz mutex_lock(&gi->lock); 1081afdaadc3SRuslan Bilovol if (gi->composite.gadget_driver.udc_name) 1082da424314SAndrzej Pietrasiewicz unregister_gadget(gi); 1083da424314SAndrzej Pietrasiewicz cdev->os_desc_config = NULL; 1084afdaadc3SRuslan Bilovol WARN_ON(gi->composite.gadget_driver.udc_name); 1085da424314SAndrzej Pietrasiewicz mutex_unlock(&gi->lock); 1086da424314SAndrzej Pietrasiewicz } 1087da424314SAndrzej Pietrasiewicz 108887213d38SAndrzej Pietrasiewicz static struct configfs_item_operations os_desc_ops = { 1089da424314SAndrzej Pietrasiewicz .allow_link = os_desc_link, 1090da424314SAndrzej Pietrasiewicz .drop_link = os_desc_unlink, 109187213d38SAndrzej Pietrasiewicz }; 109287213d38SAndrzej Pietrasiewicz 109387213d38SAndrzej Pietrasiewicz static struct config_item_type os_desc_type = { 109487213d38SAndrzej Pietrasiewicz .ct_item_ops = &os_desc_ops, 109587213d38SAndrzej Pietrasiewicz .ct_attrs = os_desc_attrs, 109687213d38SAndrzej Pietrasiewicz .ct_owner = THIS_MODULE, 109787213d38SAndrzej Pietrasiewicz }; 109887213d38SAndrzej Pietrasiewicz 10997419485fSAndrzej Pietrasiewicz static inline struct usb_os_desc_ext_prop 11007419485fSAndrzej Pietrasiewicz *to_usb_os_desc_ext_prop(struct config_item *item) 11017419485fSAndrzej Pietrasiewicz { 11027419485fSAndrzej Pietrasiewicz return container_of(item, struct usb_os_desc_ext_prop, item); 11037419485fSAndrzej Pietrasiewicz } 11047419485fSAndrzej Pietrasiewicz 110545b6a73fSChristoph Hellwig static ssize_t ext_prop_type_show(struct config_item *item, char *page) 11067419485fSAndrzej Pietrasiewicz { 1107e800e8cbSStefan Agner return sprintf(page, "%d\n", to_usb_os_desc_ext_prop(item)->type); 11087419485fSAndrzej Pietrasiewicz } 11097419485fSAndrzej Pietrasiewicz 111045b6a73fSChristoph Hellwig static ssize_t ext_prop_type_store(struct config_item *item, 11117419485fSAndrzej Pietrasiewicz const char *page, size_t len) 11127419485fSAndrzej Pietrasiewicz { 111345b6a73fSChristoph Hellwig struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); 11147419485fSAndrzej Pietrasiewicz struct usb_os_desc *desc = to_usb_os_desc(ext_prop->item.ci_parent); 11157419485fSAndrzej Pietrasiewicz u8 type; 11167419485fSAndrzej Pietrasiewicz int ret; 11177419485fSAndrzej Pietrasiewicz 11187419485fSAndrzej Pietrasiewicz ret = kstrtou8(page, 0, &type); 11197419485fSAndrzej Pietrasiewicz if (ret) 1120*6f7fb48dSAndy Shevchenko return ret; 1121*6f7fb48dSAndy Shevchenko 1122*6f7fb48dSAndy Shevchenko if (type < USB_EXT_PROP_UNICODE || type > USB_EXT_PROP_UNICODE_MULTI) 1123*6f7fb48dSAndy Shevchenko return -EINVAL; 1124*6f7fb48dSAndy Shevchenko 1125*6f7fb48dSAndy Shevchenko if (desc->opts_mutex) 1126*6f7fb48dSAndy Shevchenko mutex_lock(desc->opts_mutex); 11277419485fSAndrzej Pietrasiewicz 11287419485fSAndrzej Pietrasiewicz if ((ext_prop->type == USB_EXT_PROP_BINARY || 11297419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_LE32 || 11307419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_BE32) && 11317419485fSAndrzej Pietrasiewicz (type == USB_EXT_PROP_UNICODE || 11327419485fSAndrzej Pietrasiewicz type == USB_EXT_PROP_UNICODE_ENV || 11337419485fSAndrzej Pietrasiewicz type == USB_EXT_PROP_UNICODE_LINK)) 11347419485fSAndrzej Pietrasiewicz ext_prop->data_len <<= 1; 11357419485fSAndrzej Pietrasiewicz else if ((ext_prop->type == USB_EXT_PROP_UNICODE || 11367419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_UNICODE_ENV || 11377419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_UNICODE_LINK) && 11387419485fSAndrzej Pietrasiewicz (type == USB_EXT_PROP_BINARY || 11397419485fSAndrzej Pietrasiewicz type == USB_EXT_PROP_LE32 || 11407419485fSAndrzej Pietrasiewicz type == USB_EXT_PROP_BE32)) 11417419485fSAndrzej Pietrasiewicz ext_prop->data_len >>= 1; 11427419485fSAndrzej Pietrasiewicz ext_prop->type = type; 11437419485fSAndrzej Pietrasiewicz 11447419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 11457419485fSAndrzej Pietrasiewicz mutex_unlock(desc->opts_mutex); 1146*6f7fb48dSAndy Shevchenko return len; 11477419485fSAndrzej Pietrasiewicz } 11487419485fSAndrzej Pietrasiewicz 114945b6a73fSChristoph Hellwig static ssize_t ext_prop_data_show(struct config_item *item, char *page) 11507419485fSAndrzej Pietrasiewicz { 115145b6a73fSChristoph Hellwig struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); 11527419485fSAndrzej Pietrasiewicz int len = ext_prop->data_len; 11537419485fSAndrzej Pietrasiewicz 11547419485fSAndrzej Pietrasiewicz if (ext_prop->type == USB_EXT_PROP_UNICODE || 11557419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_UNICODE_ENV || 11567419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_UNICODE_LINK) 11577419485fSAndrzej Pietrasiewicz len >>= 1; 11587419485fSAndrzej Pietrasiewicz memcpy(page, ext_prop->data, len); 11597419485fSAndrzej Pietrasiewicz 11607419485fSAndrzej Pietrasiewicz return len; 11617419485fSAndrzej Pietrasiewicz } 11627419485fSAndrzej Pietrasiewicz 116345b6a73fSChristoph Hellwig static ssize_t ext_prop_data_store(struct config_item *item, 11647419485fSAndrzej Pietrasiewicz const char *page, size_t len) 11657419485fSAndrzej Pietrasiewicz { 116645b6a73fSChristoph Hellwig struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); 11677419485fSAndrzej Pietrasiewicz struct usb_os_desc *desc = to_usb_os_desc(ext_prop->item.ci_parent); 11687419485fSAndrzej Pietrasiewicz char *new_data; 11697419485fSAndrzej Pietrasiewicz size_t ret_len = len; 11707419485fSAndrzej Pietrasiewicz 11717419485fSAndrzej Pietrasiewicz if (page[len - 1] == '\n' || page[len - 1] == '\0') 11727419485fSAndrzej Pietrasiewicz --len; 117358b949e0SBenoit Taine new_data = kmemdup(page, len, GFP_KERNEL); 11747419485fSAndrzej Pietrasiewicz if (!new_data) 11757419485fSAndrzej Pietrasiewicz return -ENOMEM; 11767419485fSAndrzej Pietrasiewicz 11777419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 11787419485fSAndrzej Pietrasiewicz mutex_lock(desc->opts_mutex); 11797419485fSAndrzej Pietrasiewicz kfree(ext_prop->data); 11807419485fSAndrzej Pietrasiewicz ext_prop->data = new_data; 11817419485fSAndrzej Pietrasiewicz desc->ext_prop_len -= ext_prop->data_len; 11827419485fSAndrzej Pietrasiewicz ext_prop->data_len = len; 11837419485fSAndrzej Pietrasiewicz desc->ext_prop_len += ext_prop->data_len; 11847419485fSAndrzej Pietrasiewicz if (ext_prop->type == USB_EXT_PROP_UNICODE || 11857419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_UNICODE_ENV || 11867419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_UNICODE_LINK) { 11877419485fSAndrzej Pietrasiewicz desc->ext_prop_len -= ext_prop->data_len; 11887419485fSAndrzej Pietrasiewicz ext_prop->data_len <<= 1; 11897419485fSAndrzej Pietrasiewicz ext_prop->data_len += 2; 11907419485fSAndrzej Pietrasiewicz desc->ext_prop_len += ext_prop->data_len; 11917419485fSAndrzej Pietrasiewicz } 11927419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 11937419485fSAndrzej Pietrasiewicz mutex_unlock(desc->opts_mutex); 11947419485fSAndrzej Pietrasiewicz return ret_len; 11957419485fSAndrzej Pietrasiewicz } 11967419485fSAndrzej Pietrasiewicz 119745b6a73fSChristoph Hellwig CONFIGFS_ATTR(ext_prop_, type); 119845b6a73fSChristoph Hellwig CONFIGFS_ATTR(ext_prop_, data); 11997419485fSAndrzej Pietrasiewicz 12007419485fSAndrzej Pietrasiewicz static struct configfs_attribute *ext_prop_attrs[] = { 120145b6a73fSChristoph Hellwig &ext_prop_attr_type, 120245b6a73fSChristoph Hellwig &ext_prop_attr_data, 12037419485fSAndrzej Pietrasiewicz NULL, 12047419485fSAndrzej Pietrasiewicz }; 12057419485fSAndrzej Pietrasiewicz 12067419485fSAndrzej Pietrasiewicz static void usb_os_desc_ext_prop_release(struct config_item *item) 12077419485fSAndrzej Pietrasiewicz { 12087419485fSAndrzej Pietrasiewicz struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); 12097419485fSAndrzej Pietrasiewicz 12107419485fSAndrzej Pietrasiewicz kfree(ext_prop); /* frees a whole chunk */ 12117419485fSAndrzej Pietrasiewicz } 12127419485fSAndrzej Pietrasiewicz 12137419485fSAndrzej Pietrasiewicz static struct configfs_item_operations ext_prop_ops = { 12147419485fSAndrzej Pietrasiewicz .release = usb_os_desc_ext_prop_release, 12157419485fSAndrzej Pietrasiewicz }; 12167419485fSAndrzej Pietrasiewicz 12177419485fSAndrzej Pietrasiewicz static struct config_item *ext_prop_make( 12187419485fSAndrzej Pietrasiewicz struct config_group *group, 12197419485fSAndrzej Pietrasiewicz const char *name) 12207419485fSAndrzej Pietrasiewicz { 12217419485fSAndrzej Pietrasiewicz struct usb_os_desc_ext_prop *ext_prop; 12227419485fSAndrzej Pietrasiewicz struct config_item_type *ext_prop_type; 12237419485fSAndrzej Pietrasiewicz struct usb_os_desc *desc; 12247419485fSAndrzej Pietrasiewicz char *vlabuf; 12257419485fSAndrzej Pietrasiewicz 12267419485fSAndrzej Pietrasiewicz vla_group(data_chunk); 12277419485fSAndrzej Pietrasiewicz vla_item(data_chunk, struct usb_os_desc_ext_prop, ext_prop, 1); 12287419485fSAndrzej Pietrasiewicz vla_item(data_chunk, struct config_item_type, ext_prop_type, 1); 12297419485fSAndrzej Pietrasiewicz 12307419485fSAndrzej Pietrasiewicz vlabuf = kzalloc(vla_group_size(data_chunk), GFP_KERNEL); 12317419485fSAndrzej Pietrasiewicz if (!vlabuf) 12327419485fSAndrzej Pietrasiewicz return ERR_PTR(-ENOMEM); 12337419485fSAndrzej Pietrasiewicz 12347419485fSAndrzej Pietrasiewicz ext_prop = vla_ptr(vlabuf, data_chunk, ext_prop); 12357419485fSAndrzej Pietrasiewicz ext_prop_type = vla_ptr(vlabuf, data_chunk, ext_prop_type); 12367419485fSAndrzej Pietrasiewicz 12377419485fSAndrzej Pietrasiewicz desc = container_of(group, struct usb_os_desc, group); 12387419485fSAndrzej Pietrasiewicz ext_prop_type->ct_item_ops = &ext_prop_ops; 12397419485fSAndrzej Pietrasiewicz ext_prop_type->ct_attrs = ext_prop_attrs; 12407419485fSAndrzej Pietrasiewicz ext_prop_type->ct_owner = desc->owner; 12417419485fSAndrzej Pietrasiewicz 12427419485fSAndrzej Pietrasiewicz config_item_init_type_name(&ext_prop->item, name, ext_prop_type); 12437419485fSAndrzej Pietrasiewicz 12447419485fSAndrzej Pietrasiewicz ext_prop->name = kstrdup(name, GFP_KERNEL); 12457419485fSAndrzej Pietrasiewicz if (!ext_prop->name) { 12467419485fSAndrzej Pietrasiewicz kfree(vlabuf); 12477419485fSAndrzej Pietrasiewicz return ERR_PTR(-ENOMEM); 12487419485fSAndrzej Pietrasiewicz } 12497419485fSAndrzej Pietrasiewicz desc->ext_prop_len += 14; 12507419485fSAndrzej Pietrasiewicz ext_prop->name_len = 2 * strlen(ext_prop->name) + 2; 12517419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 12527419485fSAndrzej Pietrasiewicz mutex_lock(desc->opts_mutex); 12537419485fSAndrzej Pietrasiewicz desc->ext_prop_len += ext_prop->name_len; 12547419485fSAndrzej Pietrasiewicz list_add_tail(&ext_prop->entry, &desc->ext_prop); 12557419485fSAndrzej Pietrasiewicz ++desc->ext_prop_count; 12567419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 12577419485fSAndrzej Pietrasiewicz mutex_unlock(desc->opts_mutex); 12587419485fSAndrzej Pietrasiewicz 12597419485fSAndrzej Pietrasiewicz return &ext_prop->item; 12607419485fSAndrzej Pietrasiewicz } 12617419485fSAndrzej Pietrasiewicz 12627419485fSAndrzej Pietrasiewicz static void ext_prop_drop(struct config_group *group, struct config_item *item) 12637419485fSAndrzej Pietrasiewicz { 12647419485fSAndrzej Pietrasiewicz struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); 12657419485fSAndrzej Pietrasiewicz struct usb_os_desc *desc = to_usb_os_desc(&group->cg_item); 12667419485fSAndrzej Pietrasiewicz 12677419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 12687419485fSAndrzej Pietrasiewicz mutex_lock(desc->opts_mutex); 12697419485fSAndrzej Pietrasiewicz list_del(&ext_prop->entry); 12707419485fSAndrzej Pietrasiewicz --desc->ext_prop_count; 12717419485fSAndrzej Pietrasiewicz kfree(ext_prop->name); 12727419485fSAndrzej Pietrasiewicz desc->ext_prop_len -= (ext_prop->name_len + ext_prop->data_len + 14); 12737419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 12747419485fSAndrzej Pietrasiewicz mutex_unlock(desc->opts_mutex); 12757419485fSAndrzej Pietrasiewicz config_item_put(item); 12767419485fSAndrzej Pietrasiewicz } 12777419485fSAndrzej Pietrasiewicz 12787419485fSAndrzej Pietrasiewicz static struct configfs_group_operations interf_grp_ops = { 12797419485fSAndrzej Pietrasiewicz .make_item = &ext_prop_make, 12807419485fSAndrzej Pietrasiewicz .drop_item = &ext_prop_drop, 12817419485fSAndrzej Pietrasiewicz }; 12827419485fSAndrzej Pietrasiewicz 128345b6a73fSChristoph Hellwig static ssize_t interf_grp_compatible_id_show(struct config_item *item, 1284da424314SAndrzej Pietrasiewicz char *page) 1285da424314SAndrzej Pietrasiewicz { 128645b6a73fSChristoph Hellwig memcpy(page, to_usb_os_desc(item)->ext_compat_id, 8); 1287da424314SAndrzej Pietrasiewicz return 8; 1288da424314SAndrzej Pietrasiewicz } 1289da424314SAndrzej Pietrasiewicz 129045b6a73fSChristoph Hellwig static ssize_t interf_grp_compatible_id_store(struct config_item *item, 1291da424314SAndrzej Pietrasiewicz const char *page, size_t len) 1292da424314SAndrzej Pietrasiewicz { 129345b6a73fSChristoph Hellwig struct usb_os_desc *desc = to_usb_os_desc(item); 1294da424314SAndrzej Pietrasiewicz int l; 1295da424314SAndrzej Pietrasiewicz 1296da424314SAndrzej Pietrasiewicz l = min_t(int, 8, len); 1297da424314SAndrzej Pietrasiewicz if (page[l - 1] == '\n') 1298da424314SAndrzej Pietrasiewicz --l; 1299da424314SAndrzej Pietrasiewicz if (desc->opts_mutex) 1300da424314SAndrzej Pietrasiewicz mutex_lock(desc->opts_mutex); 1301da424314SAndrzej Pietrasiewicz memcpy(desc->ext_compat_id, page, l); 1302da424314SAndrzej Pietrasiewicz 1303da424314SAndrzej Pietrasiewicz if (desc->opts_mutex) 1304da424314SAndrzej Pietrasiewicz mutex_unlock(desc->opts_mutex); 1305da424314SAndrzej Pietrasiewicz 1306da424314SAndrzej Pietrasiewicz return len; 1307da424314SAndrzej Pietrasiewicz } 1308da424314SAndrzej Pietrasiewicz 130945b6a73fSChristoph Hellwig static ssize_t interf_grp_sub_compatible_id_show(struct config_item *item, 1310da424314SAndrzej Pietrasiewicz char *page) 1311da424314SAndrzej Pietrasiewicz { 131245b6a73fSChristoph Hellwig memcpy(page, to_usb_os_desc(item)->ext_compat_id + 8, 8); 1313da424314SAndrzej Pietrasiewicz return 8; 1314da424314SAndrzej Pietrasiewicz } 1315da424314SAndrzej Pietrasiewicz 131645b6a73fSChristoph Hellwig static ssize_t interf_grp_sub_compatible_id_store(struct config_item *item, 1317da424314SAndrzej Pietrasiewicz const char *page, size_t len) 1318da424314SAndrzej Pietrasiewicz { 131945b6a73fSChristoph Hellwig struct usb_os_desc *desc = to_usb_os_desc(item); 1320da424314SAndrzej Pietrasiewicz int l; 1321da424314SAndrzej Pietrasiewicz 1322da424314SAndrzej Pietrasiewicz l = min_t(int, 8, len); 1323da424314SAndrzej Pietrasiewicz if (page[l - 1] == '\n') 1324da424314SAndrzej Pietrasiewicz --l; 1325da424314SAndrzej Pietrasiewicz if (desc->opts_mutex) 1326da424314SAndrzej Pietrasiewicz mutex_lock(desc->opts_mutex); 1327da424314SAndrzej Pietrasiewicz memcpy(desc->ext_compat_id + 8, page, l); 1328da424314SAndrzej Pietrasiewicz 1329da424314SAndrzej Pietrasiewicz if (desc->opts_mutex) 1330da424314SAndrzej Pietrasiewicz mutex_unlock(desc->opts_mutex); 1331da424314SAndrzej Pietrasiewicz 1332da424314SAndrzej Pietrasiewicz return len; 1333da424314SAndrzej Pietrasiewicz } 1334da424314SAndrzej Pietrasiewicz 133545b6a73fSChristoph Hellwig CONFIGFS_ATTR(interf_grp_, compatible_id); 133645b6a73fSChristoph Hellwig CONFIGFS_ATTR(interf_grp_, sub_compatible_id); 1337da424314SAndrzej Pietrasiewicz 1338da424314SAndrzej Pietrasiewicz static struct configfs_attribute *interf_grp_attrs[] = { 133945b6a73fSChristoph Hellwig &interf_grp_attr_compatible_id, 134045b6a73fSChristoph Hellwig &interf_grp_attr_sub_compatible_id, 1341da424314SAndrzej Pietrasiewicz NULL 1342da424314SAndrzej Pietrasiewicz }; 1343da424314SAndrzej Pietrasiewicz 1344ff74745eSAndrew Gabbasov struct config_group *usb_os_desc_prepare_interf_dir( 1345ff74745eSAndrew Gabbasov struct config_group *parent, 1346da424314SAndrzej Pietrasiewicz int n_interf, 1347da424314SAndrzej Pietrasiewicz struct usb_os_desc **desc, 134814574b54SAndrzej Pietrasiewicz char **names, 1349da424314SAndrzej Pietrasiewicz struct module *owner) 1350da424314SAndrzej Pietrasiewicz { 13511ae1602dSChristoph Hellwig struct config_group *os_desc_group; 1352da424314SAndrzej Pietrasiewicz struct config_item_type *os_desc_type, *interface_type; 1353da424314SAndrzej Pietrasiewicz 1354da424314SAndrzej Pietrasiewicz vla_group(data_chunk); 1355da424314SAndrzej Pietrasiewicz vla_item(data_chunk, struct config_group, os_desc_group, 1); 1356da424314SAndrzej Pietrasiewicz vla_item(data_chunk, struct config_item_type, os_desc_type, 1); 1357da424314SAndrzej Pietrasiewicz vla_item(data_chunk, struct config_item_type, interface_type, 1); 1358da424314SAndrzej Pietrasiewicz 1359da424314SAndrzej Pietrasiewicz char *vlabuf = kzalloc(vla_group_size(data_chunk), GFP_KERNEL); 1360da424314SAndrzej Pietrasiewicz if (!vlabuf) 1361ff74745eSAndrew Gabbasov return ERR_PTR(-ENOMEM); 1362da424314SAndrzej Pietrasiewicz 1363da424314SAndrzej Pietrasiewicz os_desc_group = vla_ptr(vlabuf, data_chunk, os_desc_group); 1364da424314SAndrzej Pietrasiewicz os_desc_type = vla_ptr(vlabuf, data_chunk, os_desc_type); 1365da424314SAndrzej Pietrasiewicz interface_type = vla_ptr(vlabuf, data_chunk, interface_type); 1366da424314SAndrzej Pietrasiewicz 1367da424314SAndrzej Pietrasiewicz os_desc_type->ct_owner = owner; 1368da424314SAndrzej Pietrasiewicz config_group_init_type_name(os_desc_group, "os_desc", os_desc_type); 13691ae1602dSChristoph Hellwig configfs_add_default_group(os_desc_group, parent); 1370da424314SAndrzej Pietrasiewicz 13717419485fSAndrzej Pietrasiewicz interface_type->ct_group_ops = &interf_grp_ops; 1372da424314SAndrzej Pietrasiewicz interface_type->ct_attrs = interf_grp_attrs; 1373da424314SAndrzej Pietrasiewicz interface_type->ct_owner = owner; 1374da424314SAndrzej Pietrasiewicz 1375da424314SAndrzej Pietrasiewicz while (n_interf--) { 1376da424314SAndrzej Pietrasiewicz struct usb_os_desc *d; 1377da424314SAndrzej Pietrasiewicz 1378da424314SAndrzej Pietrasiewicz d = desc[n_interf]; 13797419485fSAndrzej Pietrasiewicz d->owner = owner; 1380da424314SAndrzej Pietrasiewicz config_group_init_type_name(&d->group, "", interface_type); 138114574b54SAndrzej Pietrasiewicz config_item_set_name(&d->group.cg_item, "interface.%s", 138214574b54SAndrzej Pietrasiewicz names[n_interf]); 13831ae1602dSChristoph Hellwig configfs_add_default_group(&d->group, os_desc_group); 1384da424314SAndrzej Pietrasiewicz } 1385da424314SAndrzej Pietrasiewicz 1386ff74745eSAndrew Gabbasov return os_desc_group; 1387da424314SAndrzej Pietrasiewicz } 1388da424314SAndrzej Pietrasiewicz EXPORT_SYMBOL(usb_os_desc_prepare_interf_dir); 1389da424314SAndrzej Pietrasiewicz 139088af8bbeSSebastian Andrzej Siewior static int configfs_do_nothing(struct usb_composite_dev *cdev) 139188af8bbeSSebastian Andrzej Siewior { 139275bfe23aSDavid Rientjes WARN_ON(1); 139388af8bbeSSebastian Andrzej Siewior return -EINVAL; 139488af8bbeSSebastian Andrzej Siewior } 139588af8bbeSSebastian Andrzej Siewior 139688af8bbeSSebastian Andrzej Siewior int composite_dev_prepare(struct usb_composite_driver *composite, 139788af8bbeSSebastian Andrzej Siewior struct usb_composite_dev *dev); 139888af8bbeSSebastian Andrzej Siewior 1399da424314SAndrzej Pietrasiewicz int composite_os_desc_req_prepare(struct usb_composite_dev *cdev, 1400da424314SAndrzej Pietrasiewicz struct usb_ep *ep0); 1401da424314SAndrzej Pietrasiewicz 140288af8bbeSSebastian Andrzej Siewior static void purge_configs_funcs(struct gadget_info *gi) 140388af8bbeSSebastian Andrzej Siewior { 140488af8bbeSSebastian Andrzej Siewior struct usb_configuration *c; 140588af8bbeSSebastian Andrzej Siewior 140688af8bbeSSebastian Andrzej Siewior list_for_each_entry(c, &gi->cdev.configs, list) { 140788af8bbeSSebastian Andrzej Siewior struct usb_function *f, *tmp; 140888af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg; 140988af8bbeSSebastian Andrzej Siewior 141088af8bbeSSebastian Andrzej Siewior cfg = container_of(c, struct config_usb_cfg, c); 141188af8bbeSSebastian Andrzej Siewior 14126cd0fe91SChandana Kishori Chiluveru list_for_each_entry_safe_reverse(f, tmp, &c->functions, list) { 141388af8bbeSSebastian Andrzej Siewior 14146cd0fe91SChandana Kishori Chiluveru list_move(&f->list, &cfg->func_list); 141588af8bbeSSebastian Andrzej Siewior if (f->unbind) { 1416da7b895dSRomain Izard dev_dbg(&gi->cdev.gadget->dev, 1417a08f5dbfSRomain Izard "unbind function '%s'/%p\n", 1418a08f5dbfSRomain Izard f->name, f); 141988af8bbeSSebastian Andrzej Siewior f->unbind(c, f); 142088af8bbeSSebastian Andrzej Siewior } 142188af8bbeSSebastian Andrzej Siewior } 142288af8bbeSSebastian Andrzej Siewior c->next_interface_id = 0; 1423903124feSKrzysztof Opasiak memset(c->interface, 0, sizeof(c->interface)); 1424554eead5SJohn Youn c->superspeed_plus = 0; 142588af8bbeSSebastian Andrzej Siewior c->superspeed = 0; 142688af8bbeSSebastian Andrzej Siewior c->highspeed = 0; 142788af8bbeSSebastian Andrzej Siewior c->fullspeed = 0; 142888af8bbeSSebastian Andrzej Siewior } 142988af8bbeSSebastian Andrzej Siewior } 143088af8bbeSSebastian Andrzej Siewior 143188af8bbeSSebastian Andrzej Siewior static int configfs_composite_bind(struct usb_gadget *gadget, 143288af8bbeSSebastian Andrzej Siewior struct usb_gadget_driver *gdriver) 143388af8bbeSSebastian Andrzej Siewior { 143488af8bbeSSebastian Andrzej Siewior struct usb_composite_driver *composite = to_cdriver(gdriver); 143588af8bbeSSebastian Andrzej Siewior struct gadget_info *gi = container_of(composite, 143688af8bbeSSebastian Andrzej Siewior struct gadget_info, composite); 143788af8bbeSSebastian Andrzej Siewior struct usb_composite_dev *cdev = &gi->cdev; 143888af8bbeSSebastian Andrzej Siewior struct usb_configuration *c; 143988af8bbeSSebastian Andrzej Siewior struct usb_string *s; 144088af8bbeSSebastian Andrzej Siewior unsigned i; 144188af8bbeSSebastian Andrzej Siewior int ret; 144288af8bbeSSebastian Andrzej Siewior 144388af8bbeSSebastian Andrzej Siewior /* the gi->lock is hold by the caller */ 14441a1c851bSPeter Chen gi->unbind = 0; 144588af8bbeSSebastian Andrzej Siewior cdev->gadget = gadget; 144688af8bbeSSebastian Andrzej Siewior set_gadget_data(gadget, cdev); 144788af8bbeSSebastian Andrzej Siewior ret = composite_dev_prepare(composite, cdev); 144888af8bbeSSebastian Andrzej Siewior if (ret) 144988af8bbeSSebastian Andrzej Siewior return ret; 145088af8bbeSSebastian Andrzej Siewior /* and now the gadget bind */ 145188af8bbeSSebastian Andrzej Siewior ret = -EINVAL; 145288af8bbeSSebastian Andrzej Siewior 145388af8bbeSSebastian Andrzej Siewior if (list_empty(&gi->cdev.configs)) { 145488af8bbeSSebastian Andrzej Siewior pr_err("Need at least one configuration in %s.\n", 145588af8bbeSSebastian Andrzej Siewior gi->composite.name); 145688af8bbeSSebastian Andrzej Siewior goto err_comp_cleanup; 145788af8bbeSSebastian Andrzej Siewior } 145888af8bbeSSebastian Andrzej Siewior 145988af8bbeSSebastian Andrzej Siewior 146088af8bbeSSebastian Andrzej Siewior list_for_each_entry(c, &gi->cdev.configs, list) { 146188af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg; 146288af8bbeSSebastian Andrzej Siewior 146388af8bbeSSebastian Andrzej Siewior cfg = container_of(c, struct config_usb_cfg, c); 146488af8bbeSSebastian Andrzej Siewior if (list_empty(&cfg->func_list)) { 146588af8bbeSSebastian Andrzej Siewior pr_err("Config %s/%d of %s needs at least one function.\n", 146688af8bbeSSebastian Andrzej Siewior c->label, c->bConfigurationValue, 146788af8bbeSSebastian Andrzej Siewior gi->composite.name); 146888af8bbeSSebastian Andrzej Siewior goto err_comp_cleanup; 146988af8bbeSSebastian Andrzej Siewior } 147088af8bbeSSebastian Andrzej Siewior } 147188af8bbeSSebastian Andrzej Siewior 147288af8bbeSSebastian Andrzej Siewior /* init all strings */ 147388af8bbeSSebastian Andrzej Siewior if (!list_empty(&gi->string_list)) { 147488af8bbeSSebastian Andrzej Siewior struct gadget_strings *gs; 147588af8bbeSSebastian Andrzej Siewior 147688af8bbeSSebastian Andrzej Siewior i = 0; 147788af8bbeSSebastian Andrzej Siewior list_for_each_entry(gs, &gi->string_list, list) { 147888af8bbeSSebastian Andrzej Siewior 147988af8bbeSSebastian Andrzej Siewior gi->gstrings[i] = &gs->stringtab_dev; 148088af8bbeSSebastian Andrzej Siewior gs->stringtab_dev.strings = gs->strings; 148188af8bbeSSebastian Andrzej Siewior gs->strings[USB_GADGET_MANUFACTURER_IDX].s = 148288af8bbeSSebastian Andrzej Siewior gs->manufacturer; 148388af8bbeSSebastian Andrzej Siewior gs->strings[USB_GADGET_PRODUCT_IDX].s = gs->product; 148488af8bbeSSebastian Andrzej Siewior gs->strings[USB_GADGET_SERIAL_IDX].s = gs->serialnumber; 148588af8bbeSSebastian Andrzej Siewior i++; 148688af8bbeSSebastian Andrzej Siewior } 148788af8bbeSSebastian Andrzej Siewior gi->gstrings[i] = NULL; 148888af8bbeSSebastian Andrzej Siewior s = usb_gstrings_attach(&gi->cdev, gi->gstrings, 148988af8bbeSSebastian Andrzej Siewior USB_GADGET_FIRST_AVAIL_IDX); 1490fea77077SWei Yongjun if (IS_ERR(s)) { 1491fea77077SWei Yongjun ret = PTR_ERR(s); 149288af8bbeSSebastian Andrzej Siewior goto err_comp_cleanup; 1493fea77077SWei Yongjun } 149488af8bbeSSebastian Andrzej Siewior 149588af8bbeSSebastian Andrzej Siewior gi->cdev.desc.iManufacturer = s[USB_GADGET_MANUFACTURER_IDX].id; 149688af8bbeSSebastian Andrzej Siewior gi->cdev.desc.iProduct = s[USB_GADGET_PRODUCT_IDX].id; 149788af8bbeSSebastian Andrzej Siewior gi->cdev.desc.iSerialNumber = s[USB_GADGET_SERIAL_IDX].id; 149888af8bbeSSebastian Andrzej Siewior } 149988af8bbeSSebastian Andrzej Siewior 150093c47394SJó Ágila Bitsch if (gi->use_webusb) { 150193c47394SJó Ágila Bitsch cdev->use_webusb = true; 150293c47394SJó Ágila Bitsch cdev->bcd_webusb_version = gi->bcd_webusb_version; 150393c47394SJó Ágila Bitsch cdev->b_webusb_vendor_code = gi->b_webusb_vendor_code; 150493c47394SJó Ágila Bitsch memcpy(cdev->landing_page, gi->landing_page, WEBUSB_URL_RAW_MAX_LENGTH); 150593c47394SJó Ágila Bitsch } 150693c47394SJó Ágila Bitsch 150787213d38SAndrzej Pietrasiewicz if (gi->use_os_desc) { 150887213d38SAndrzej Pietrasiewicz cdev->use_os_string = true; 150987213d38SAndrzej Pietrasiewicz cdev->b_vendor_code = gi->b_vendor_code; 151087213d38SAndrzej Pietrasiewicz memcpy(cdev->qw_sign, gi->qw_sign, OS_STRING_QW_SIGN_LEN); 151187213d38SAndrzej Pietrasiewicz } 151287213d38SAndrzej Pietrasiewicz 151341ce84c8SLi Jun if (gadget_is_otg(gadget) && !otg_desc[0]) { 151441ce84c8SLi Jun struct usb_descriptor_header *usb_desc; 151541ce84c8SLi Jun 151641ce84c8SLi Jun usb_desc = usb_otg_descriptor_alloc(gadget); 151741ce84c8SLi Jun if (!usb_desc) { 151841ce84c8SLi Jun ret = -ENOMEM; 151941ce84c8SLi Jun goto err_comp_cleanup; 152041ce84c8SLi Jun } 152141ce84c8SLi Jun usb_otg_descriptor_init(gadget, usb_desc); 152241ce84c8SLi Jun otg_desc[0] = usb_desc; 152341ce84c8SLi Jun otg_desc[1] = NULL; 152441ce84c8SLi Jun } 152541ce84c8SLi Jun 152688af8bbeSSebastian Andrzej Siewior /* Go through all configs, attach all functions */ 152788af8bbeSSebastian Andrzej Siewior list_for_each_entry(c, &gi->cdev.configs, list) { 152888af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg; 152988af8bbeSSebastian Andrzej Siewior struct usb_function *f; 153088af8bbeSSebastian Andrzej Siewior struct usb_function *tmp; 153188af8bbeSSebastian Andrzej Siewior struct gadget_config_name *cn; 153288af8bbeSSebastian Andrzej Siewior 153341ce84c8SLi Jun if (gadget_is_otg(gadget)) 153441ce84c8SLi Jun c->descriptors = otg_desc; 153541ce84c8SLi Jun 153688af8bbeSSebastian Andrzej Siewior cfg = container_of(c, struct config_usb_cfg, c); 153788af8bbeSSebastian Andrzej Siewior if (!list_empty(&cfg->string_list)) { 153888af8bbeSSebastian Andrzej Siewior i = 0; 153988af8bbeSSebastian Andrzej Siewior list_for_each_entry(cn, &cfg->string_list, list) { 154088af8bbeSSebastian Andrzej Siewior cfg->gstrings[i] = &cn->stringtab_dev; 154188af8bbeSSebastian Andrzej Siewior cn->stringtab_dev.strings = &cn->strings; 154288af8bbeSSebastian Andrzej Siewior cn->strings.s = cn->configuration; 154388af8bbeSSebastian Andrzej Siewior i++; 154488af8bbeSSebastian Andrzej Siewior } 154588af8bbeSSebastian Andrzej Siewior cfg->gstrings[i] = NULL; 154688af8bbeSSebastian Andrzej Siewior s = usb_gstrings_attach(&gi->cdev, cfg->gstrings, 1); 1547fea77077SWei Yongjun if (IS_ERR(s)) { 1548fea77077SWei Yongjun ret = PTR_ERR(s); 154988af8bbeSSebastian Andrzej Siewior goto err_comp_cleanup; 1550fea77077SWei Yongjun } 155188af8bbeSSebastian Andrzej Siewior c->iConfiguration = s[0].id; 155288af8bbeSSebastian Andrzej Siewior } 155388af8bbeSSebastian Andrzej Siewior 155488af8bbeSSebastian Andrzej Siewior list_for_each_entry_safe(f, tmp, &cfg->func_list, list) { 155588af8bbeSSebastian Andrzej Siewior list_del(&f->list); 155688af8bbeSSebastian Andrzej Siewior ret = usb_add_function(c, f); 15575a68e9b5SAndrzej Pietrasiewicz if (ret) { 15585a68e9b5SAndrzej Pietrasiewicz list_add(&f->list, &cfg->func_list); 155988af8bbeSSebastian Andrzej Siewior goto err_purge_funcs; 156088af8bbeSSebastian Andrzej Siewior } 15615a68e9b5SAndrzej Pietrasiewicz } 15627adf9e3aSWesley Cheng ret = usb_gadget_check_config(cdev->gadget); 15637adf9e3aSWesley Cheng if (ret) 15647adf9e3aSWesley Cheng goto err_purge_funcs; 15657adf9e3aSWesley Cheng 156688af8bbeSSebastian Andrzej Siewior usb_ep_autoconfig_reset(cdev->gadget); 156788af8bbeSSebastian Andrzej Siewior } 1568da424314SAndrzej Pietrasiewicz if (cdev->use_os_string) { 1569da424314SAndrzej Pietrasiewicz ret = composite_os_desc_req_prepare(cdev, gadget->ep0); 1570da424314SAndrzej Pietrasiewicz if (ret) 1571da424314SAndrzej Pietrasiewicz goto err_purge_funcs; 1572da424314SAndrzej Pietrasiewicz } 1573da424314SAndrzej Pietrasiewicz 157488af8bbeSSebastian Andrzej Siewior usb_ep_autoconfig_reset(cdev->gadget); 157588af8bbeSSebastian Andrzej Siewior return 0; 157688af8bbeSSebastian Andrzej Siewior 157788af8bbeSSebastian Andrzej Siewior err_purge_funcs: 157888af8bbeSSebastian Andrzej Siewior purge_configs_funcs(gi); 157988af8bbeSSebastian Andrzej Siewior err_comp_cleanup: 158088af8bbeSSebastian Andrzej Siewior composite_dev_cleanup(cdev); 158188af8bbeSSebastian Andrzej Siewior return ret; 158288af8bbeSSebastian Andrzej Siewior } 158388af8bbeSSebastian Andrzej Siewior 158488af8bbeSSebastian Andrzej Siewior static void configfs_composite_unbind(struct usb_gadget *gadget) 158588af8bbeSSebastian Andrzej Siewior { 158688af8bbeSSebastian Andrzej Siewior struct usb_composite_dev *cdev; 158788af8bbeSSebastian Andrzej Siewior struct gadget_info *gi; 15881a1c851bSPeter Chen unsigned long flags; 158988af8bbeSSebastian Andrzej Siewior 159088af8bbeSSebastian Andrzej Siewior /* the gi->lock is hold by the caller */ 159188af8bbeSSebastian Andrzej Siewior 159288af8bbeSSebastian Andrzej Siewior cdev = get_gadget_data(gadget); 159388af8bbeSSebastian Andrzej Siewior gi = container_of(cdev, struct gadget_info, cdev); 15941a1c851bSPeter Chen spin_lock_irqsave(&gi->spinlock, flags); 15951a1c851bSPeter Chen gi->unbind = 1; 15961a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 159788af8bbeSSebastian Andrzej Siewior 159841ce84c8SLi Jun kfree(otg_desc[0]); 159941ce84c8SLi Jun otg_desc[0] = NULL; 160088af8bbeSSebastian Andrzej Siewior purge_configs_funcs(gi); 160188af8bbeSSebastian Andrzej Siewior composite_dev_cleanup(cdev); 160288af8bbeSSebastian Andrzej Siewior usb_ep_autoconfig_reset(cdev->gadget); 16031a1c851bSPeter Chen spin_lock_irqsave(&gi->spinlock, flags); 160488af8bbeSSebastian Andrzej Siewior cdev->gadget = NULL; 1605bf95c4d4SVijayavardhan Vennapusa cdev->deactivations = 0; 1606bf95c4d4SVijayavardhan Vennapusa gadget->deactivated = false; 160788af8bbeSSebastian Andrzej Siewior set_gadget_data(gadget, NULL); 16081a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 16091a1c851bSPeter Chen } 16101a1c851bSPeter Chen 16111a1c851bSPeter Chen static int configfs_composite_setup(struct usb_gadget *gadget, 16121a1c851bSPeter Chen const struct usb_ctrlrequest *ctrl) 16131a1c851bSPeter Chen { 16141a1c851bSPeter Chen struct usb_composite_dev *cdev; 16151a1c851bSPeter Chen struct gadget_info *gi; 16161a1c851bSPeter Chen unsigned long flags; 16171a1c851bSPeter Chen int ret; 16181a1c851bSPeter Chen 16191a1c851bSPeter Chen cdev = get_gadget_data(gadget); 16201a1c851bSPeter Chen if (!cdev) 16211a1c851bSPeter Chen return 0; 16221a1c851bSPeter Chen 16231a1c851bSPeter Chen gi = container_of(cdev, struct gadget_info, cdev); 16241a1c851bSPeter Chen spin_lock_irqsave(&gi->spinlock, flags); 16251a1c851bSPeter Chen cdev = get_gadget_data(gadget); 16261a1c851bSPeter Chen if (!cdev || gi->unbind) { 16271a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 16281a1c851bSPeter Chen return 0; 16291a1c851bSPeter Chen } 16301a1c851bSPeter Chen 16311a1c851bSPeter Chen ret = composite_setup(gadget, ctrl); 16321a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 16331a1c851bSPeter Chen return ret; 16341a1c851bSPeter Chen } 16351a1c851bSPeter Chen 16361a1c851bSPeter Chen static void configfs_composite_disconnect(struct usb_gadget *gadget) 16371a1c851bSPeter Chen { 16381a1c851bSPeter Chen struct usb_composite_dev *cdev; 16391a1c851bSPeter Chen struct gadget_info *gi; 16401a1c851bSPeter Chen unsigned long flags; 16411a1c851bSPeter Chen 16421a1c851bSPeter Chen cdev = get_gadget_data(gadget); 16431a1c851bSPeter Chen if (!cdev) 16441a1c851bSPeter Chen return; 16451a1c851bSPeter Chen 16461a1c851bSPeter Chen gi = container_of(cdev, struct gadget_info, cdev); 16471a1c851bSPeter Chen spin_lock_irqsave(&gi->spinlock, flags); 16481a1c851bSPeter Chen cdev = get_gadget_data(gadget); 16491a1c851bSPeter Chen if (!cdev || gi->unbind) { 16501a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 16511a1c851bSPeter Chen return; 16521a1c851bSPeter Chen } 16531a1c851bSPeter Chen 16541a1c851bSPeter Chen composite_disconnect(gadget); 16551a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 16561a1c851bSPeter Chen } 16571a1c851bSPeter Chen 16584d7aae9fSWesley Cheng static void configfs_composite_reset(struct usb_gadget *gadget) 16594d7aae9fSWesley Cheng { 16604d7aae9fSWesley Cheng struct usb_composite_dev *cdev; 16614d7aae9fSWesley Cheng struct gadget_info *gi; 16624d7aae9fSWesley Cheng unsigned long flags; 16634d7aae9fSWesley Cheng 16644d7aae9fSWesley Cheng cdev = get_gadget_data(gadget); 16654d7aae9fSWesley Cheng if (!cdev) 16664d7aae9fSWesley Cheng return; 16674d7aae9fSWesley Cheng 16684d7aae9fSWesley Cheng gi = container_of(cdev, struct gadget_info, cdev); 16694d7aae9fSWesley Cheng spin_lock_irqsave(&gi->spinlock, flags); 16704d7aae9fSWesley Cheng cdev = get_gadget_data(gadget); 16714d7aae9fSWesley Cheng if (!cdev || gi->unbind) { 16724d7aae9fSWesley Cheng spin_unlock_irqrestore(&gi->spinlock, flags); 16734d7aae9fSWesley Cheng return; 16744d7aae9fSWesley Cheng } 16754d7aae9fSWesley Cheng 16764d7aae9fSWesley Cheng composite_reset(gadget); 16774d7aae9fSWesley Cheng spin_unlock_irqrestore(&gi->spinlock, flags); 16784d7aae9fSWesley Cheng } 16794d7aae9fSWesley Cheng 16801a1c851bSPeter Chen static void configfs_composite_suspend(struct usb_gadget *gadget) 16811a1c851bSPeter Chen { 16821a1c851bSPeter Chen struct usb_composite_dev *cdev; 16831a1c851bSPeter Chen struct gadget_info *gi; 16841a1c851bSPeter Chen unsigned long flags; 16851a1c851bSPeter Chen 16861a1c851bSPeter Chen cdev = get_gadget_data(gadget); 16871a1c851bSPeter Chen if (!cdev) 16881a1c851bSPeter Chen return; 16891a1c851bSPeter Chen 16901a1c851bSPeter Chen gi = container_of(cdev, struct gadget_info, cdev); 16911a1c851bSPeter Chen spin_lock_irqsave(&gi->spinlock, flags); 16921a1c851bSPeter Chen cdev = get_gadget_data(gadget); 16931a1c851bSPeter Chen if (!cdev || gi->unbind) { 16941a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 16951a1c851bSPeter Chen return; 16961a1c851bSPeter Chen } 16971a1c851bSPeter Chen 16981a1c851bSPeter Chen composite_suspend(gadget); 16991a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 17001a1c851bSPeter Chen } 17011a1c851bSPeter Chen 17021a1c851bSPeter Chen static void configfs_composite_resume(struct usb_gadget *gadget) 17031a1c851bSPeter Chen { 17041a1c851bSPeter Chen struct usb_composite_dev *cdev; 17051a1c851bSPeter Chen struct gadget_info *gi; 17061a1c851bSPeter Chen unsigned long flags; 17071a1c851bSPeter Chen 17081a1c851bSPeter Chen cdev = get_gadget_data(gadget); 17091a1c851bSPeter Chen if (!cdev) 17101a1c851bSPeter Chen return; 17111a1c851bSPeter Chen 17121a1c851bSPeter Chen gi = container_of(cdev, struct gadget_info, cdev); 17131a1c851bSPeter Chen spin_lock_irqsave(&gi->spinlock, flags); 17141a1c851bSPeter Chen cdev = get_gadget_data(gadget); 17151a1c851bSPeter Chen if (!cdev || gi->unbind) { 17161a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 17171a1c851bSPeter Chen return; 17181a1c851bSPeter Chen } 17191a1c851bSPeter Chen 17201a1c851bSPeter Chen composite_resume(gadget); 17211a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 172288af8bbeSSebastian Andrzej Siewior } 172388af8bbeSSebastian Andrzej Siewior 172488af8bbeSSebastian Andrzej Siewior static const struct usb_gadget_driver configfs_driver_template = { 172588af8bbeSSebastian Andrzej Siewior .bind = configfs_composite_bind, 172688af8bbeSSebastian Andrzej Siewior .unbind = configfs_composite_unbind, 172788af8bbeSSebastian Andrzej Siewior 17281a1c851bSPeter Chen .setup = configfs_composite_setup, 17294d7aae9fSWesley Cheng .reset = configfs_composite_reset, 17301a1c851bSPeter Chen .disconnect = configfs_composite_disconnect, 173188af8bbeSSebastian Andrzej Siewior 17321a1c851bSPeter Chen .suspend = configfs_composite_suspend, 17331a1c851bSPeter Chen .resume = configfs_composite_resume, 17343a571870SAndrzej Pietrasiewicz 1735e2459108Staehyun.cho .max_speed = USB_SPEED_SUPER_PLUS, 173688af8bbeSSebastian Andrzej Siewior .driver = { 173788af8bbeSSebastian Andrzej Siewior .owner = THIS_MODULE, 173888af8bbeSSebastian Andrzej Siewior }, 1739f1bddbb3SKrzysztof Opasiak .match_existing_only = 1, 174088af8bbeSSebastian Andrzej Siewior }; 174188af8bbeSSebastian Andrzej Siewior 174288af8bbeSSebastian Andrzej Siewior static struct config_group *gadgets_make( 174388af8bbeSSebastian Andrzej Siewior struct config_group *group, 174488af8bbeSSebastian Andrzej Siewior const char *name) 174588af8bbeSSebastian Andrzej Siewior { 174688af8bbeSSebastian Andrzej Siewior struct gadget_info *gi; 174788af8bbeSSebastian Andrzej Siewior 174888af8bbeSSebastian Andrzej Siewior gi = kzalloc(sizeof(*gi), GFP_KERNEL); 174988af8bbeSSebastian Andrzej Siewior if (!gi) 175088af8bbeSSebastian Andrzej Siewior return ERR_PTR(-ENOMEM); 175188af8bbeSSebastian Andrzej Siewior 17521ae1602dSChristoph Hellwig config_group_init_type_name(&gi->group, name, &gadget_root_type); 175388af8bbeSSebastian Andrzej Siewior 175488af8bbeSSebastian Andrzej Siewior config_group_init_type_name(&gi->functions_group, "functions", 175588af8bbeSSebastian Andrzej Siewior &functions_type); 17561ae1602dSChristoph Hellwig configfs_add_default_group(&gi->functions_group, &gi->group); 17571ae1602dSChristoph Hellwig 175888af8bbeSSebastian Andrzej Siewior config_group_init_type_name(&gi->configs_group, "configs", 175988af8bbeSSebastian Andrzej Siewior &config_desc_type); 17601ae1602dSChristoph Hellwig configfs_add_default_group(&gi->configs_group, &gi->group); 17611ae1602dSChristoph Hellwig 176288af8bbeSSebastian Andrzej Siewior config_group_init_type_name(&gi->strings_group, "strings", 176388af8bbeSSebastian Andrzej Siewior &gadget_strings_strings_type); 17641ae1602dSChristoph Hellwig configfs_add_default_group(&gi->strings_group, &gi->group); 17651ae1602dSChristoph Hellwig 176687213d38SAndrzej Pietrasiewicz config_group_init_type_name(&gi->os_desc_group, "os_desc", 176787213d38SAndrzej Pietrasiewicz &os_desc_type); 17681ae1602dSChristoph Hellwig configfs_add_default_group(&gi->os_desc_group, &gi->group); 176988af8bbeSSebastian Andrzej Siewior 177093c47394SJó Ágila Bitsch config_group_init_type_name(&gi->webusb_group, "webusb", 177193c47394SJó Ágila Bitsch &webusb_type); 177293c47394SJó Ágila Bitsch configfs_add_default_group(&gi->webusb_group, &gi->group); 177393c47394SJó Ágila Bitsch 177488af8bbeSSebastian Andrzej Siewior gi->composite.bind = configfs_do_nothing; 177588af8bbeSSebastian Andrzej Siewior gi->composite.unbind = configfs_do_nothing; 177688af8bbeSSebastian Andrzej Siewior gi->composite.suspend = NULL; 177788af8bbeSSebastian Andrzej Siewior gi->composite.resume = NULL; 1778e2459108Staehyun.cho gi->composite.max_speed = USB_SPEED_SUPER_PLUS; 177988af8bbeSSebastian Andrzej Siewior 1780093edc2bSWei Yongjun spin_lock_init(&gi->spinlock); 178188af8bbeSSebastian Andrzej Siewior mutex_init(&gi->lock); 178288af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&gi->string_list); 178388af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&gi->available_func); 178488af8bbeSSebastian Andrzej Siewior 178588af8bbeSSebastian Andrzej Siewior composite_init_dev(&gi->cdev); 178688af8bbeSSebastian Andrzej Siewior gi->cdev.desc.bLength = USB_DT_DEVICE_SIZE; 178788af8bbeSSebastian Andrzej Siewior gi->cdev.desc.bDescriptorType = USB_DT_DEVICE; 178888af8bbeSSebastian Andrzej Siewior gi->cdev.desc.bcdDevice = cpu_to_le16(get_default_bcdDevice()); 178988af8bbeSSebastian Andrzej Siewior 179088af8bbeSSebastian Andrzej Siewior gi->composite.gadget_driver = configfs_driver_template; 179188af8bbeSSebastian Andrzej Siewior 17927c075538SChanh Nguyen gi->composite.gadget_driver.driver.name = kasprintf(GFP_KERNEL, 17937c075538SChanh Nguyen "configfs-gadget.%s", name); 17947c075538SChanh Nguyen if (!gi->composite.gadget_driver.driver.name) 17957c075538SChanh Nguyen goto err; 17967c075538SChanh Nguyen 179788af8bbeSSebastian Andrzej Siewior gi->composite.gadget_driver.function = kstrdup(name, GFP_KERNEL); 179888af8bbeSSebastian Andrzej Siewior gi->composite.name = gi->composite.gadget_driver.function; 179988af8bbeSSebastian Andrzej Siewior 180088af8bbeSSebastian Andrzej Siewior if (!gi->composite.gadget_driver.function) 18017c075538SChanh Nguyen goto out_free_driver_name; 180288af8bbeSSebastian Andrzej Siewior 180388af8bbeSSebastian Andrzej Siewior return &gi->group; 18047c075538SChanh Nguyen 18057c075538SChanh Nguyen out_free_driver_name: 18067c075538SChanh Nguyen kfree(gi->composite.gadget_driver.driver.name); 180788af8bbeSSebastian Andrzej Siewior err: 180888af8bbeSSebastian Andrzej Siewior kfree(gi); 180988af8bbeSSebastian Andrzej Siewior return ERR_PTR(-ENOMEM); 181088af8bbeSSebastian Andrzej Siewior } 181188af8bbeSSebastian Andrzej Siewior 181288af8bbeSSebastian Andrzej Siewior static void gadgets_drop(struct config_group *group, struct config_item *item) 181388af8bbeSSebastian Andrzej Siewior { 181488af8bbeSSebastian Andrzej Siewior config_item_put(item); 181588af8bbeSSebastian Andrzej Siewior } 181688af8bbeSSebastian Andrzej Siewior 181788af8bbeSSebastian Andrzej Siewior static struct configfs_group_operations gadgets_ops = { 181888af8bbeSSebastian Andrzej Siewior .make_group = &gadgets_make, 181988af8bbeSSebastian Andrzej Siewior .drop_item = &gadgets_drop, 182088af8bbeSSebastian Andrzej Siewior }; 182188af8bbeSSebastian Andrzej Siewior 18224ad01412SBhumika Goyal static const struct config_item_type gadgets_type = { 182388af8bbeSSebastian Andrzej Siewior .ct_group_ops = &gadgets_ops, 182488af8bbeSSebastian Andrzej Siewior .ct_owner = THIS_MODULE, 182588af8bbeSSebastian Andrzej Siewior }; 182688af8bbeSSebastian Andrzej Siewior 182788af8bbeSSebastian Andrzej Siewior static struct configfs_subsystem gadget_subsys = { 182888af8bbeSSebastian Andrzej Siewior .su_group = { 182988af8bbeSSebastian Andrzej Siewior .cg_item = { 183088af8bbeSSebastian Andrzej Siewior .ci_namebuf = "usb_gadget", 183188af8bbeSSebastian Andrzej Siewior .ci_type = &gadgets_type, 183288af8bbeSSebastian Andrzej Siewior }, 183388af8bbeSSebastian Andrzej Siewior }, 183488af8bbeSSebastian Andrzej Siewior .su_mutex = __MUTEX_INITIALIZER(gadget_subsys.su_mutex), 183588af8bbeSSebastian Andrzej Siewior }; 183688af8bbeSSebastian Andrzej Siewior 1837092a4bd0SAndrzej Pietrasiewicz void unregister_gadget_item(struct config_item *item) 1838092a4bd0SAndrzej Pietrasiewicz { 1839092a4bd0SAndrzej Pietrasiewicz struct gadget_info *gi = to_gadget_info(item); 1840092a4bd0SAndrzej Pietrasiewicz 1841cee51c33SWinter Wang mutex_lock(&gi->lock); 1842092a4bd0SAndrzej Pietrasiewicz unregister_gadget(gi); 1843cee51c33SWinter Wang mutex_unlock(&gi->lock); 1844092a4bd0SAndrzej Pietrasiewicz } 18450700faafSFelipe Balbi EXPORT_SYMBOL_GPL(unregister_gadget_item); 1846092a4bd0SAndrzej Pietrasiewicz 184788af8bbeSSebastian Andrzej Siewior static int __init gadget_cfs_init(void) 184888af8bbeSSebastian Andrzej Siewior { 184988af8bbeSSebastian Andrzej Siewior int ret; 185088af8bbeSSebastian Andrzej Siewior 185188af8bbeSSebastian Andrzej Siewior config_group_init(&gadget_subsys.su_group); 185288af8bbeSSebastian Andrzej Siewior 185388af8bbeSSebastian Andrzej Siewior ret = configfs_register_subsystem(&gadget_subsys); 185488af8bbeSSebastian Andrzej Siewior return ret; 185588af8bbeSSebastian Andrzej Siewior } 185688af8bbeSSebastian Andrzej Siewior module_init(gadget_cfs_init); 185788af8bbeSSebastian Andrzej Siewior 185888af8bbeSSebastian Andrzej Siewior static void __exit gadget_cfs_exit(void) 185988af8bbeSSebastian Andrzej Siewior { 186088af8bbeSSebastian Andrzej Siewior configfs_unregister_subsystem(&gadget_subsys); 186188af8bbeSSebastian Andrzej Siewior } 186288af8bbeSSebastian Andrzej Siewior module_exit(gadget_cfs_exit); 1863