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> 687213d38SAndrzej Pietrasiewicz #include <linux/nls.h> 788af8bbeSSebastian Andrzej Siewior #include <linux/usb/composite.h> 888af8bbeSSebastian Andrzej Siewior #include <linux/usb/gadget_configfs.h> 90009e99aSRashika Kheria #include "configfs.h" 10da424314SAndrzej Pietrasiewicz #include "u_f.h" 117419485fSAndrzej Pietrasiewicz #include "u_os_desc.h" 1288af8bbeSSebastian Andrzej Siewior 1388af8bbeSSebastian Andrzej Siewior int check_user_usb_string(const char *name, 1488af8bbeSSebastian Andrzej Siewior struct usb_gadget_strings *stringtab_dev) 1588af8bbeSSebastian Andrzej Siewior { 1688af8bbeSSebastian Andrzej Siewior u16 num; 1788af8bbeSSebastian Andrzej Siewior int ret; 1888af8bbeSSebastian Andrzej Siewior 1988af8bbeSSebastian Andrzej Siewior ret = kstrtou16(name, 0, &num); 2088af8bbeSSebastian Andrzej Siewior if (ret) 2188af8bbeSSebastian Andrzej Siewior return ret; 2288af8bbeSSebastian Andrzej Siewior 2317309a6aSTao Ren if (!usb_validate_langid(num)) 2488af8bbeSSebastian Andrzej Siewior return -EINVAL; 2588af8bbeSSebastian Andrzej Siewior 2688af8bbeSSebastian Andrzej Siewior stringtab_dev->language = num; 2788af8bbeSSebastian Andrzej Siewior return 0; 2888af8bbeSSebastian Andrzej Siewior } 2988af8bbeSSebastian Andrzej Siewior 3088af8bbeSSebastian Andrzej Siewior #define MAX_NAME_LEN 40 3188af8bbeSSebastian Andrzej Siewior #define MAX_USB_STRING_LANGS 2 3288af8bbeSSebastian Andrzej Siewior 3341ce84c8SLi Jun static const struct usb_descriptor_header *otg_desc[2]; 3441ce84c8SLi Jun 3588af8bbeSSebastian Andrzej Siewior struct gadget_info { 3688af8bbeSSebastian Andrzej Siewior struct config_group group; 3788af8bbeSSebastian Andrzej Siewior struct config_group functions_group; 3888af8bbeSSebastian Andrzej Siewior struct config_group configs_group; 3988af8bbeSSebastian Andrzej Siewior struct config_group strings_group; 4087213d38SAndrzej Pietrasiewicz struct config_group os_desc_group; 4188af8bbeSSebastian Andrzej Siewior 4288af8bbeSSebastian Andrzej Siewior struct mutex lock; 4388af8bbeSSebastian Andrzej Siewior struct usb_gadget_strings *gstrings[MAX_USB_STRING_LANGS + 1]; 4488af8bbeSSebastian Andrzej Siewior struct list_head string_list; 4588af8bbeSSebastian Andrzej Siewior struct list_head available_func; 4688af8bbeSSebastian Andrzej Siewior 4788af8bbeSSebastian Andrzej Siewior struct usb_composite_driver composite; 4888af8bbeSSebastian Andrzej Siewior struct usb_composite_dev cdev; 4987213d38SAndrzej Pietrasiewicz bool use_os_desc; 5087213d38SAndrzej Pietrasiewicz char b_vendor_code; 5187213d38SAndrzej Pietrasiewicz char qw_sign[OS_STRING_QW_SIGN_LEN]; 521a1c851bSPeter Chen spinlock_t spinlock; 531a1c851bSPeter Chen bool unbind; 5488af8bbeSSebastian Andrzej Siewior }; 5588af8bbeSSebastian Andrzej Siewior 5645b6a73fSChristoph Hellwig static inline struct gadget_info *to_gadget_info(struct config_item *item) 5745b6a73fSChristoph Hellwig { 5845b6a73fSChristoph Hellwig return container_of(to_config_group(item), struct gadget_info, group); 5945b6a73fSChristoph Hellwig } 6045b6a73fSChristoph Hellwig 6188af8bbeSSebastian Andrzej Siewior struct config_usb_cfg { 6288af8bbeSSebastian Andrzej Siewior struct config_group group; 6388af8bbeSSebastian Andrzej Siewior struct config_group strings_group; 6488af8bbeSSebastian Andrzej Siewior struct list_head string_list; 6588af8bbeSSebastian Andrzej Siewior struct usb_configuration c; 6688af8bbeSSebastian Andrzej Siewior struct list_head func_list; 6788af8bbeSSebastian Andrzej Siewior struct usb_gadget_strings *gstrings[MAX_USB_STRING_LANGS + 1]; 6888af8bbeSSebastian Andrzej Siewior }; 6988af8bbeSSebastian Andrzej Siewior 7045b6a73fSChristoph Hellwig static inline struct config_usb_cfg *to_config_usb_cfg(struct config_item *item) 7145b6a73fSChristoph Hellwig { 7245b6a73fSChristoph Hellwig return container_of(to_config_group(item), struct config_usb_cfg, 7345b6a73fSChristoph Hellwig group); 7445b6a73fSChristoph Hellwig } 7545b6a73fSChristoph Hellwig 76*260d88b7SLinyu Yuan static inline struct gadget_info *cfg_to_gadget_info(struct config_usb_cfg *cfg) 77*260d88b7SLinyu Yuan { 78*260d88b7SLinyu Yuan return container_of(cfg->c.cdev, struct gadget_info, cdev); 79*260d88b7SLinyu Yuan } 80*260d88b7SLinyu Yuan 8188af8bbeSSebastian Andrzej Siewior struct gadget_strings { 8288af8bbeSSebastian Andrzej Siewior struct usb_gadget_strings stringtab_dev; 8388af8bbeSSebastian Andrzej Siewior struct usb_string strings[USB_GADGET_FIRST_AVAIL_IDX]; 8488af8bbeSSebastian Andrzej Siewior char *manufacturer; 8588af8bbeSSebastian Andrzej Siewior char *product; 8688af8bbeSSebastian Andrzej Siewior char *serialnumber; 8788af8bbeSSebastian Andrzej Siewior 8888af8bbeSSebastian Andrzej Siewior struct config_group group; 8988af8bbeSSebastian Andrzej Siewior struct list_head list; 9088af8bbeSSebastian Andrzej Siewior }; 9188af8bbeSSebastian Andrzej Siewior 9287213d38SAndrzej Pietrasiewicz struct os_desc { 9387213d38SAndrzej Pietrasiewicz struct config_group group; 9487213d38SAndrzej Pietrasiewicz }; 9587213d38SAndrzej Pietrasiewicz 9688af8bbeSSebastian Andrzej Siewior struct gadget_config_name { 9788af8bbeSSebastian Andrzej Siewior struct usb_gadget_strings stringtab_dev; 9888af8bbeSSebastian Andrzej Siewior struct usb_string strings; 9988af8bbeSSebastian Andrzej Siewior char *configuration; 10088af8bbeSSebastian Andrzej Siewior 10188af8bbeSSebastian Andrzej Siewior struct config_group group; 10288af8bbeSSebastian Andrzej Siewior struct list_head list; 10388af8bbeSSebastian Andrzej Siewior }; 10488af8bbeSSebastian Andrzej Siewior 10598f153a1SJim Lin #define USB_MAX_STRING_WITH_NULL_LEN (USB_MAX_STRING_LEN+1) 10698f153a1SJim Lin 10788af8bbeSSebastian Andrzej Siewior static int usb_string_copy(const char *s, char **s_copy) 10888af8bbeSSebastian Andrzej Siewior { 10988af8bbeSSebastian Andrzej Siewior int ret; 11088af8bbeSSebastian Andrzej Siewior char *str; 11188af8bbeSSebastian Andrzej Siewior char *copy = *s_copy; 11288af8bbeSSebastian Andrzej Siewior ret = strlen(s); 11381c74628SMacpaul Lin if (ret > USB_MAX_STRING_LEN) 11488af8bbeSSebastian Andrzej Siewior return -EOVERFLOW; 11588af8bbeSSebastian Andrzej Siewior 11698f153a1SJim Lin if (copy) { 11798f153a1SJim Lin str = copy; 11898f153a1SJim Lin } else { 11998f153a1SJim Lin str = kmalloc(USB_MAX_STRING_WITH_NULL_LEN, GFP_KERNEL); 12088af8bbeSSebastian Andrzej Siewior if (!str) 12188af8bbeSSebastian Andrzej Siewior return -ENOMEM; 12298f153a1SJim Lin } 12398f153a1SJim Lin strcpy(str, s); 12488af8bbeSSebastian Andrzej Siewior if (str[ret - 1] == '\n') 12588af8bbeSSebastian Andrzej Siewior str[ret - 1] = '\0'; 12688af8bbeSSebastian Andrzej Siewior *s_copy = str; 12788af8bbeSSebastian Andrzej Siewior return 0; 12888af8bbeSSebastian Andrzej Siewior } 12988af8bbeSSebastian Andrzej Siewior 13088af8bbeSSebastian Andrzej Siewior #define GI_DEVICE_DESC_SIMPLE_R_u8(__name) \ 13145b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_##__name##_show(struct config_item *item, \ 13288af8bbeSSebastian Andrzej Siewior char *page) \ 13388af8bbeSSebastian Andrzej Siewior { \ 13445b6a73fSChristoph Hellwig return sprintf(page, "0x%02x\n", \ 13545b6a73fSChristoph Hellwig to_gadget_info(item)->cdev.desc.__name); \ 13688af8bbeSSebastian Andrzej Siewior } 13788af8bbeSSebastian Andrzej Siewior 13888af8bbeSSebastian Andrzej Siewior #define GI_DEVICE_DESC_SIMPLE_R_u16(__name) \ 13945b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_##__name##_show(struct config_item *item, \ 14088af8bbeSSebastian Andrzej Siewior char *page) \ 14188af8bbeSSebastian Andrzej Siewior { \ 14245b6a73fSChristoph Hellwig return sprintf(page, "0x%04x\n", \ 14345b6a73fSChristoph Hellwig le16_to_cpup(&to_gadget_info(item)->cdev.desc.__name)); \ 14488af8bbeSSebastian Andrzej Siewior } 14588af8bbeSSebastian Andrzej Siewior 14688af8bbeSSebastian Andrzej Siewior 14788af8bbeSSebastian Andrzej Siewior #define GI_DEVICE_DESC_SIMPLE_W_u8(_name) \ 14845b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_##_name##_store(struct config_item *item, \ 14988af8bbeSSebastian Andrzej Siewior const char *page, size_t len) \ 15088af8bbeSSebastian Andrzej Siewior { \ 15188af8bbeSSebastian Andrzej Siewior u8 val; \ 15288af8bbeSSebastian Andrzej Siewior int ret; \ 15388af8bbeSSebastian Andrzej Siewior ret = kstrtou8(page, 0, &val); \ 15488af8bbeSSebastian Andrzej Siewior if (ret) \ 15588af8bbeSSebastian Andrzej Siewior return ret; \ 15645b6a73fSChristoph Hellwig to_gadget_info(item)->cdev.desc._name = val; \ 15788af8bbeSSebastian Andrzej Siewior return len; \ 15888af8bbeSSebastian Andrzej Siewior } 15988af8bbeSSebastian Andrzej Siewior 16088af8bbeSSebastian Andrzej Siewior #define GI_DEVICE_DESC_SIMPLE_W_u16(_name) \ 16145b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_##_name##_store(struct config_item *item, \ 16288af8bbeSSebastian Andrzej Siewior const char *page, size_t len) \ 16388af8bbeSSebastian Andrzej Siewior { \ 16488af8bbeSSebastian Andrzej Siewior u16 val; \ 16588af8bbeSSebastian Andrzej Siewior int ret; \ 16688af8bbeSSebastian Andrzej Siewior ret = kstrtou16(page, 0, &val); \ 16788af8bbeSSebastian Andrzej Siewior if (ret) \ 16888af8bbeSSebastian Andrzej Siewior return ret; \ 16945b6a73fSChristoph Hellwig to_gadget_info(item)->cdev.desc._name = cpu_to_le16p(&val); \ 17088af8bbeSSebastian Andrzej Siewior return len; \ 17188af8bbeSSebastian Andrzej Siewior } 17288af8bbeSSebastian Andrzej Siewior 17388af8bbeSSebastian Andrzej Siewior #define GI_DEVICE_DESC_SIMPLE_RW(_name, _type) \ 17488af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_R_##_type(_name) \ 17588af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_W_##_type(_name) 17688af8bbeSSebastian Andrzej Siewior 17788af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_R_u16(bcdUSB); 17888af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(bDeviceClass, u8); 17988af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(bDeviceSubClass, u8); 18088af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(bDeviceProtocol, u8); 18188af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(bMaxPacketSize0, u8); 18288af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(idVendor, u16); 18388af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_RW(idProduct, u16); 18488af8bbeSSebastian Andrzej Siewior GI_DEVICE_DESC_SIMPLE_R_u16(bcdDevice); 18588af8bbeSSebastian Andrzej Siewior 18688af8bbeSSebastian Andrzej Siewior static ssize_t is_valid_bcd(u16 bcd_val) 18788af8bbeSSebastian Andrzej Siewior { 18888af8bbeSSebastian Andrzej Siewior if ((bcd_val & 0xf) > 9) 18988af8bbeSSebastian Andrzej Siewior return -EINVAL; 19088af8bbeSSebastian Andrzej Siewior if (((bcd_val >> 4) & 0xf) > 9) 19188af8bbeSSebastian Andrzej Siewior return -EINVAL; 19288af8bbeSSebastian Andrzej Siewior if (((bcd_val >> 8) & 0xf) > 9) 19388af8bbeSSebastian Andrzej Siewior return -EINVAL; 19488af8bbeSSebastian Andrzej Siewior if (((bcd_val >> 12) & 0xf) > 9) 19588af8bbeSSebastian Andrzej Siewior return -EINVAL; 19688af8bbeSSebastian Andrzej Siewior return 0; 19788af8bbeSSebastian Andrzej Siewior } 19888af8bbeSSebastian Andrzej Siewior 19945b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_bcdDevice_store(struct config_item *item, 20088af8bbeSSebastian Andrzej Siewior const char *page, size_t len) 20188af8bbeSSebastian Andrzej Siewior { 20288af8bbeSSebastian Andrzej Siewior u16 bcdDevice; 20388af8bbeSSebastian Andrzej Siewior int ret; 20488af8bbeSSebastian Andrzej Siewior 20588af8bbeSSebastian Andrzej Siewior ret = kstrtou16(page, 0, &bcdDevice); 20688af8bbeSSebastian Andrzej Siewior if (ret) 20788af8bbeSSebastian Andrzej Siewior return ret; 20888af8bbeSSebastian Andrzej Siewior ret = is_valid_bcd(bcdDevice); 20988af8bbeSSebastian Andrzej Siewior if (ret) 21088af8bbeSSebastian Andrzej Siewior return ret; 21188af8bbeSSebastian Andrzej Siewior 21245b6a73fSChristoph Hellwig to_gadget_info(item)->cdev.desc.bcdDevice = cpu_to_le16(bcdDevice); 21388af8bbeSSebastian Andrzej Siewior return len; 21488af8bbeSSebastian Andrzej Siewior } 21588af8bbeSSebastian Andrzej Siewior 21645b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_bcdUSB_store(struct config_item *item, 21788af8bbeSSebastian Andrzej Siewior const char *page, size_t len) 21888af8bbeSSebastian Andrzej Siewior { 21988af8bbeSSebastian Andrzej Siewior u16 bcdUSB; 22088af8bbeSSebastian Andrzej Siewior int ret; 22188af8bbeSSebastian Andrzej Siewior 22288af8bbeSSebastian Andrzej Siewior ret = kstrtou16(page, 0, &bcdUSB); 22388af8bbeSSebastian Andrzej Siewior if (ret) 22488af8bbeSSebastian Andrzej Siewior return ret; 22588af8bbeSSebastian Andrzej Siewior ret = is_valid_bcd(bcdUSB); 22688af8bbeSSebastian Andrzej Siewior if (ret) 22788af8bbeSSebastian Andrzej Siewior return ret; 22888af8bbeSSebastian Andrzej Siewior 22945b6a73fSChristoph Hellwig to_gadget_info(item)->cdev.desc.bcdUSB = cpu_to_le16(bcdUSB); 23088af8bbeSSebastian Andrzej Siewior return len; 23188af8bbeSSebastian Andrzej Siewior } 23288af8bbeSSebastian Andrzej Siewior 23345b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_UDC_show(struct config_item *item, char *page) 23488af8bbeSSebastian Andrzej Siewior { 23564e6bbffSEddie Hung struct gadget_info *gi = to_gadget_info(item); 23664e6bbffSEddie Hung char *udc_name; 23764e6bbffSEddie Hung int ret; 238afdaadc3SRuslan Bilovol 23964e6bbffSEddie Hung mutex_lock(&gi->lock); 24064e6bbffSEddie Hung udc_name = gi->composite.gadget_driver.udc_name; 24164e6bbffSEddie Hung ret = sprintf(page, "%s\n", udc_name ?: ""); 24264e6bbffSEddie Hung mutex_unlock(&gi->lock); 24364e6bbffSEddie Hung 24464e6bbffSEddie Hung return ret; 24588af8bbeSSebastian Andrzej Siewior } 24688af8bbeSSebastian Andrzej Siewior 24788af8bbeSSebastian Andrzej Siewior static int unregister_gadget(struct gadget_info *gi) 24888af8bbeSSebastian Andrzej Siewior { 24988af8bbeSSebastian Andrzej Siewior int ret; 25088af8bbeSSebastian Andrzej Siewior 251afdaadc3SRuslan Bilovol if (!gi->composite.gadget_driver.udc_name) 25288af8bbeSSebastian Andrzej Siewior return -ENODEV; 25388af8bbeSSebastian Andrzej Siewior 25488af8bbeSSebastian Andrzej Siewior ret = usb_gadget_unregister_driver(&gi->composite.gadget_driver); 25588af8bbeSSebastian Andrzej Siewior if (ret) 25688af8bbeSSebastian Andrzej Siewior return ret; 257afdaadc3SRuslan Bilovol kfree(gi->composite.gadget_driver.udc_name); 258afdaadc3SRuslan Bilovol gi->composite.gadget_driver.udc_name = NULL; 25988af8bbeSSebastian Andrzej Siewior return 0; 26088af8bbeSSebastian Andrzej Siewior } 26188af8bbeSSebastian Andrzej Siewior 26245b6a73fSChristoph Hellwig static ssize_t gadget_dev_desc_UDC_store(struct config_item *item, 26388af8bbeSSebastian Andrzej Siewior const char *page, size_t len) 26488af8bbeSSebastian Andrzej Siewior { 26545b6a73fSChristoph Hellwig struct gadget_info *gi = to_gadget_info(item); 26688af8bbeSSebastian Andrzej Siewior char *name; 26788af8bbeSSebastian Andrzej Siewior int ret; 26888af8bbeSSebastian Andrzej Siewior 26915753588SKyungtae Kim if (strlen(page) < len) 27015753588SKyungtae Kim return -EOVERFLOW; 27115753588SKyungtae Kim 27288af8bbeSSebastian Andrzej Siewior name = kstrdup(page, GFP_KERNEL); 27388af8bbeSSebastian Andrzej Siewior if (!name) 27488af8bbeSSebastian Andrzej Siewior return -ENOMEM; 27588af8bbeSSebastian Andrzej Siewior if (name[len - 1] == '\n') 27688af8bbeSSebastian Andrzej Siewior name[len - 1] = '\0'; 27788af8bbeSSebastian Andrzej Siewior 27888af8bbeSSebastian Andrzej Siewior mutex_lock(&gi->lock); 27988af8bbeSSebastian Andrzej Siewior 28088af8bbeSSebastian Andrzej Siewior if (!strlen(name)) { 28188af8bbeSSebastian Andrzej Siewior ret = unregister_gadget(gi); 28288af8bbeSSebastian Andrzej Siewior if (ret) 28388af8bbeSSebastian Andrzej Siewior goto err; 28438355b2aSJohn Keeping kfree(name); 28588af8bbeSSebastian Andrzej Siewior } else { 286afdaadc3SRuslan Bilovol if (gi->composite.gadget_driver.udc_name) { 28788af8bbeSSebastian Andrzej Siewior ret = -EBUSY; 28888af8bbeSSebastian Andrzej Siewior goto err; 28988af8bbeSSebastian Andrzej Siewior } 290afdaadc3SRuslan Bilovol gi->composite.gadget_driver.udc_name = name; 291afdaadc3SRuslan Bilovol ret = usb_gadget_probe_driver(&gi->composite.gadget_driver); 292afdaadc3SRuslan Bilovol if (ret) { 293afdaadc3SRuslan Bilovol gi->composite.gadget_driver.udc_name = NULL; 29488af8bbeSSebastian Andrzej Siewior goto err; 295afdaadc3SRuslan Bilovol } 29688af8bbeSSebastian Andrzej Siewior } 29788af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 29888af8bbeSSebastian Andrzej Siewior return len; 29988af8bbeSSebastian Andrzej Siewior err: 30088af8bbeSSebastian Andrzej Siewior kfree(name); 30188af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 30288af8bbeSSebastian Andrzej Siewior return ret; 30388af8bbeSSebastian Andrzej Siewior } 30488af8bbeSSebastian Andrzej Siewior 305a0249703SThinh Nguyen static ssize_t gadget_dev_desc_max_speed_show(struct config_item *item, 306a0249703SThinh Nguyen char *page) 307a0249703SThinh Nguyen { 308a0249703SThinh Nguyen enum usb_device_speed speed = to_gadget_info(item)->composite.max_speed; 309a0249703SThinh Nguyen 310a0249703SThinh Nguyen return sprintf(page, "%s\n", usb_speed_string(speed)); 311a0249703SThinh Nguyen } 312a0249703SThinh Nguyen 313a0249703SThinh Nguyen static ssize_t gadget_dev_desc_max_speed_store(struct config_item *item, 314a0249703SThinh Nguyen const char *page, size_t len) 315a0249703SThinh Nguyen { 316a0249703SThinh Nguyen struct gadget_info *gi = to_gadget_info(item); 317a0249703SThinh Nguyen 318a0249703SThinh Nguyen mutex_lock(&gi->lock); 319a0249703SThinh Nguyen 320a0249703SThinh Nguyen /* Prevent changing of max_speed after the driver is binded */ 321a0249703SThinh Nguyen if (gi->composite.gadget_driver.udc_name) 322a0249703SThinh Nguyen goto err; 323a0249703SThinh Nguyen 324a0249703SThinh Nguyen if (strncmp(page, "super-speed-plus", 16) == 0) 325a0249703SThinh Nguyen gi->composite.max_speed = USB_SPEED_SUPER_PLUS; 326a0249703SThinh Nguyen else if (strncmp(page, "super-speed", 11) == 0) 327a0249703SThinh Nguyen gi->composite.max_speed = USB_SPEED_SUPER; 328a0249703SThinh Nguyen else if (strncmp(page, "high-speed", 10) == 0) 329a0249703SThinh Nguyen gi->composite.max_speed = USB_SPEED_HIGH; 330a0249703SThinh Nguyen else if (strncmp(page, "full-speed", 10) == 0) 331a0249703SThinh Nguyen gi->composite.max_speed = USB_SPEED_FULL; 332a0249703SThinh Nguyen else if (strncmp(page, "low-speed", 9) == 0) 333a0249703SThinh Nguyen gi->composite.max_speed = USB_SPEED_LOW; 334a0249703SThinh Nguyen else 335a0249703SThinh Nguyen goto err; 336a0249703SThinh Nguyen 337a0249703SThinh Nguyen gi->composite.gadget_driver.max_speed = gi->composite.max_speed; 338a0249703SThinh Nguyen 339a0249703SThinh Nguyen mutex_unlock(&gi->lock); 340a0249703SThinh Nguyen return len; 341a0249703SThinh Nguyen err: 342a0249703SThinh Nguyen mutex_unlock(&gi->lock); 343a0249703SThinh Nguyen return -EINVAL; 344a0249703SThinh Nguyen } 345a0249703SThinh Nguyen 34645b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, bDeviceClass); 34745b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, bDeviceSubClass); 34845b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, bDeviceProtocol); 34945b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, bMaxPacketSize0); 35045b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, idVendor); 35145b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, idProduct); 35245b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, bcdDevice); 35345b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, bcdUSB); 35445b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_dev_desc_, UDC); 355a0249703SThinh Nguyen CONFIGFS_ATTR(gadget_dev_desc_, max_speed); 35688af8bbeSSebastian Andrzej Siewior 35788af8bbeSSebastian Andrzej Siewior static struct configfs_attribute *gadget_root_attrs[] = { 35845b6a73fSChristoph Hellwig &gadget_dev_desc_attr_bDeviceClass, 35945b6a73fSChristoph Hellwig &gadget_dev_desc_attr_bDeviceSubClass, 36045b6a73fSChristoph Hellwig &gadget_dev_desc_attr_bDeviceProtocol, 36145b6a73fSChristoph Hellwig &gadget_dev_desc_attr_bMaxPacketSize0, 36245b6a73fSChristoph Hellwig &gadget_dev_desc_attr_idVendor, 36345b6a73fSChristoph Hellwig &gadget_dev_desc_attr_idProduct, 36445b6a73fSChristoph Hellwig &gadget_dev_desc_attr_bcdDevice, 36545b6a73fSChristoph Hellwig &gadget_dev_desc_attr_bcdUSB, 36645b6a73fSChristoph Hellwig &gadget_dev_desc_attr_UDC, 367a0249703SThinh Nguyen &gadget_dev_desc_attr_max_speed, 36888af8bbeSSebastian Andrzej Siewior NULL, 36988af8bbeSSebastian Andrzej Siewior }; 37088af8bbeSSebastian Andrzej Siewior 37188af8bbeSSebastian Andrzej Siewior static inline struct gadget_strings *to_gadget_strings(struct config_item *item) 37288af8bbeSSebastian Andrzej Siewior { 37388af8bbeSSebastian Andrzej Siewior return container_of(to_config_group(item), struct gadget_strings, 37488af8bbeSSebastian Andrzej Siewior group); 37588af8bbeSSebastian Andrzej Siewior } 37688af8bbeSSebastian Andrzej Siewior 37788af8bbeSSebastian Andrzej Siewior static inline struct gadget_config_name *to_gadget_config_name( 37888af8bbeSSebastian Andrzej Siewior struct config_item *item) 37988af8bbeSSebastian Andrzej Siewior { 38088af8bbeSSebastian Andrzej Siewior return container_of(to_config_group(item), struct gadget_config_name, 38188af8bbeSSebastian Andrzej Siewior group); 38288af8bbeSSebastian Andrzej Siewior } 38388af8bbeSSebastian Andrzej Siewior 38488af8bbeSSebastian Andrzej Siewior static inline struct usb_function_instance *to_usb_function_instance( 38588af8bbeSSebastian Andrzej Siewior struct config_item *item) 38688af8bbeSSebastian Andrzej Siewior { 38788af8bbeSSebastian Andrzej Siewior return container_of(to_config_group(item), 38888af8bbeSSebastian Andrzej Siewior struct usb_function_instance, group); 38988af8bbeSSebastian Andrzej Siewior } 39088af8bbeSSebastian Andrzej Siewior 39188af8bbeSSebastian Andrzej Siewior static void gadget_info_attr_release(struct config_item *item) 39288af8bbeSSebastian Andrzej Siewior { 39388af8bbeSSebastian Andrzej Siewior struct gadget_info *gi = to_gadget_info(item); 39488af8bbeSSebastian Andrzej Siewior 39588af8bbeSSebastian Andrzej Siewior WARN_ON(!list_empty(&gi->cdev.configs)); 39688af8bbeSSebastian Andrzej Siewior WARN_ON(!list_empty(&gi->string_list)); 39788af8bbeSSebastian Andrzej Siewior WARN_ON(!list_empty(&gi->available_func)); 39888af8bbeSSebastian Andrzej Siewior kfree(gi->composite.gadget_driver.function); 39988af8bbeSSebastian Andrzej Siewior kfree(gi); 40088af8bbeSSebastian Andrzej Siewior } 40188af8bbeSSebastian Andrzej Siewior 40288af8bbeSSebastian Andrzej Siewior static struct configfs_item_operations gadget_root_item_ops = { 40388af8bbeSSebastian Andrzej Siewior .release = gadget_info_attr_release, 40488af8bbeSSebastian Andrzej Siewior }; 40588af8bbeSSebastian Andrzej Siewior 40688af8bbeSSebastian Andrzej Siewior static void gadget_config_attr_release(struct config_item *item) 40788af8bbeSSebastian Andrzej Siewior { 40888af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg = to_config_usb_cfg(item); 40988af8bbeSSebastian Andrzej Siewior 41088af8bbeSSebastian Andrzej Siewior WARN_ON(!list_empty(&cfg->c.functions)); 41188af8bbeSSebastian Andrzej Siewior list_del(&cfg->c.list); 41288af8bbeSSebastian Andrzej Siewior kfree(cfg->c.label); 41388af8bbeSSebastian Andrzej Siewior kfree(cfg); 41488af8bbeSSebastian Andrzej Siewior } 41588af8bbeSSebastian Andrzej Siewior 41688af8bbeSSebastian Andrzej Siewior static int config_usb_cfg_link( 41788af8bbeSSebastian Andrzej Siewior struct config_item *usb_cfg_ci, 41888af8bbeSSebastian Andrzej Siewior struct config_item *usb_func_ci) 41988af8bbeSSebastian Andrzej Siewior { 42088af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg = to_config_usb_cfg(usb_cfg_ci); 421*260d88b7SLinyu Yuan struct gadget_info *gi = cfg_to_gadget_info(cfg); 42288af8bbeSSebastian Andrzej Siewior 42388af8bbeSSebastian Andrzej Siewior struct config_group *group = to_config_group(usb_func_ci); 42488af8bbeSSebastian Andrzej Siewior struct usb_function_instance *fi = container_of(group, 42588af8bbeSSebastian Andrzej Siewior struct usb_function_instance, group); 42688af8bbeSSebastian Andrzej Siewior struct usb_function_instance *a_fi; 42788af8bbeSSebastian Andrzej Siewior struct usb_function *f; 42888af8bbeSSebastian Andrzej Siewior int ret; 42988af8bbeSSebastian Andrzej Siewior 43088af8bbeSSebastian Andrzej Siewior mutex_lock(&gi->lock); 43188af8bbeSSebastian Andrzej Siewior /* 43288af8bbeSSebastian Andrzej Siewior * Make sure this function is from within our _this_ gadget and not 43388af8bbeSSebastian Andrzej Siewior * from another gadget or a random directory. 43488af8bbeSSebastian Andrzej Siewior * Also a function instance can only be linked once. 43588af8bbeSSebastian Andrzej Siewior */ 43688af8bbeSSebastian Andrzej Siewior list_for_each_entry(a_fi, &gi->available_func, cfs_list) { 43788af8bbeSSebastian Andrzej Siewior if (a_fi == fi) 43888af8bbeSSebastian Andrzej Siewior break; 43988af8bbeSSebastian Andrzej Siewior } 44088af8bbeSSebastian Andrzej Siewior if (a_fi != fi) { 44188af8bbeSSebastian Andrzej Siewior ret = -EINVAL; 44288af8bbeSSebastian Andrzej Siewior goto out; 44388af8bbeSSebastian Andrzej Siewior } 44488af8bbeSSebastian Andrzej Siewior 44588af8bbeSSebastian Andrzej Siewior list_for_each_entry(f, &cfg->func_list, list) { 44688af8bbeSSebastian Andrzej Siewior if (f->fi == fi) { 44788af8bbeSSebastian Andrzej Siewior ret = -EEXIST; 44888af8bbeSSebastian Andrzej Siewior goto out; 44988af8bbeSSebastian Andrzej Siewior } 45088af8bbeSSebastian Andrzej Siewior } 45188af8bbeSSebastian Andrzej Siewior 45288af8bbeSSebastian Andrzej Siewior f = usb_get_function(fi); 45388af8bbeSSebastian Andrzej Siewior if (IS_ERR(f)) { 45488af8bbeSSebastian Andrzej Siewior ret = PTR_ERR(f); 45588af8bbeSSebastian Andrzej Siewior goto out; 45688af8bbeSSebastian Andrzej Siewior } 45788af8bbeSSebastian Andrzej Siewior 45888af8bbeSSebastian Andrzej Siewior /* stash the function until we bind it to the gadget */ 45988af8bbeSSebastian Andrzej Siewior list_add_tail(&f->list, &cfg->func_list); 46088af8bbeSSebastian Andrzej Siewior ret = 0; 46188af8bbeSSebastian Andrzej Siewior out: 46288af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 46388af8bbeSSebastian Andrzej Siewior return ret; 46488af8bbeSSebastian Andrzej Siewior } 46588af8bbeSSebastian Andrzej Siewior 466e16769d4SAndrzej Pietrasiewicz static void config_usb_cfg_unlink( 46788af8bbeSSebastian Andrzej Siewior struct config_item *usb_cfg_ci, 46888af8bbeSSebastian Andrzej Siewior struct config_item *usb_func_ci) 46988af8bbeSSebastian Andrzej Siewior { 47088af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg = to_config_usb_cfg(usb_cfg_ci); 471*260d88b7SLinyu Yuan struct gadget_info *gi = cfg_to_gadget_info(cfg); 47288af8bbeSSebastian Andrzej Siewior 47388af8bbeSSebastian Andrzej Siewior struct config_group *group = to_config_group(usb_func_ci); 47488af8bbeSSebastian Andrzej Siewior struct usb_function_instance *fi = container_of(group, 47588af8bbeSSebastian Andrzej Siewior struct usb_function_instance, group); 47688af8bbeSSebastian Andrzej Siewior struct usb_function *f; 47788af8bbeSSebastian Andrzej Siewior 47888af8bbeSSebastian Andrzej Siewior /* 47988af8bbeSSebastian Andrzej Siewior * ideally I would like to forbid to unlink functions while a gadget is 48088af8bbeSSebastian Andrzej Siewior * bound to an UDC. Since this isn't possible at the moment, we simply 48188af8bbeSSebastian Andrzej Siewior * force an unbind, the function is available here and then we can 48288af8bbeSSebastian Andrzej Siewior * remove the function. 48388af8bbeSSebastian Andrzej Siewior */ 48488af8bbeSSebastian Andrzej Siewior mutex_lock(&gi->lock); 485afdaadc3SRuslan Bilovol if (gi->composite.gadget_driver.udc_name) 48688af8bbeSSebastian Andrzej Siewior unregister_gadget(gi); 487afdaadc3SRuslan Bilovol WARN_ON(gi->composite.gadget_driver.udc_name); 48888af8bbeSSebastian Andrzej Siewior 48988af8bbeSSebastian Andrzej Siewior list_for_each_entry(f, &cfg->func_list, list) { 49088af8bbeSSebastian Andrzej Siewior if (f->fi == fi) { 49188af8bbeSSebastian Andrzej Siewior list_del(&f->list); 49288af8bbeSSebastian Andrzej Siewior usb_put_function(f); 49388af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 494e16769d4SAndrzej Pietrasiewicz return; 49588af8bbeSSebastian Andrzej Siewior } 49688af8bbeSSebastian Andrzej Siewior } 49788af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 49875bfe23aSDavid Rientjes WARN(1, "Unable to locate function to unbind\n"); 49988af8bbeSSebastian Andrzej Siewior } 50088af8bbeSSebastian Andrzej Siewior 50188af8bbeSSebastian Andrzej Siewior static struct configfs_item_operations gadget_config_item_ops = { 50288af8bbeSSebastian Andrzej Siewior .release = gadget_config_attr_release, 50388af8bbeSSebastian Andrzej Siewior .allow_link = config_usb_cfg_link, 50488af8bbeSSebastian Andrzej Siewior .drop_link = config_usb_cfg_unlink, 50588af8bbeSSebastian Andrzej Siewior }; 50688af8bbeSSebastian Andrzej Siewior 50788af8bbeSSebastian Andrzej Siewior 50845b6a73fSChristoph Hellwig static ssize_t gadget_config_desc_MaxPower_show(struct config_item *item, 50988af8bbeSSebastian Andrzej Siewior char *page) 51088af8bbeSSebastian Andrzej Siewior { 51145b6a73fSChristoph Hellwig return sprintf(page, "%u\n", to_config_usb_cfg(item)->c.MaxPower); 51288af8bbeSSebastian Andrzej Siewior } 51388af8bbeSSebastian Andrzej Siewior 51445b6a73fSChristoph Hellwig static ssize_t gadget_config_desc_MaxPower_store(struct config_item *item, 51588af8bbeSSebastian Andrzej Siewior const char *page, size_t len) 51688af8bbeSSebastian Andrzej Siewior { 51788af8bbeSSebastian Andrzej Siewior u16 val; 51888af8bbeSSebastian Andrzej Siewior int ret; 51988af8bbeSSebastian Andrzej Siewior ret = kstrtou16(page, 0, &val); 52088af8bbeSSebastian Andrzej Siewior if (ret) 52188af8bbeSSebastian Andrzej Siewior return ret; 52288af8bbeSSebastian Andrzej Siewior if (DIV_ROUND_UP(val, 8) > 0xff) 52388af8bbeSSebastian Andrzej Siewior return -ERANGE; 52445b6a73fSChristoph Hellwig to_config_usb_cfg(item)->c.MaxPower = val; 52588af8bbeSSebastian Andrzej Siewior return len; 52688af8bbeSSebastian Andrzej Siewior } 52788af8bbeSSebastian Andrzej Siewior 52845b6a73fSChristoph Hellwig static ssize_t gadget_config_desc_bmAttributes_show(struct config_item *item, 52988af8bbeSSebastian Andrzej Siewior char *page) 53088af8bbeSSebastian Andrzej Siewior { 53145b6a73fSChristoph Hellwig return sprintf(page, "0x%02x\n", 53245b6a73fSChristoph Hellwig to_config_usb_cfg(item)->c.bmAttributes); 53388af8bbeSSebastian Andrzej Siewior } 53488af8bbeSSebastian Andrzej Siewior 53545b6a73fSChristoph Hellwig static ssize_t gadget_config_desc_bmAttributes_store(struct config_item *item, 53688af8bbeSSebastian Andrzej Siewior const char *page, size_t len) 53788af8bbeSSebastian Andrzej Siewior { 53888af8bbeSSebastian Andrzej Siewior u8 val; 53988af8bbeSSebastian Andrzej Siewior int ret; 54088af8bbeSSebastian Andrzej Siewior ret = kstrtou8(page, 0, &val); 54188af8bbeSSebastian Andrzej Siewior if (ret) 54288af8bbeSSebastian Andrzej Siewior return ret; 54388af8bbeSSebastian Andrzej Siewior if (!(val & USB_CONFIG_ATT_ONE)) 54488af8bbeSSebastian Andrzej Siewior return -EINVAL; 54588af8bbeSSebastian Andrzej Siewior if (val & ~(USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER | 54688af8bbeSSebastian Andrzej Siewior USB_CONFIG_ATT_WAKEUP)) 54788af8bbeSSebastian Andrzej Siewior return -EINVAL; 54845b6a73fSChristoph Hellwig to_config_usb_cfg(item)->c.bmAttributes = val; 54988af8bbeSSebastian Andrzej Siewior return len; 55088af8bbeSSebastian Andrzej Siewior } 55188af8bbeSSebastian Andrzej Siewior 55245b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_config_desc_, MaxPower); 55345b6a73fSChristoph Hellwig CONFIGFS_ATTR(gadget_config_desc_, bmAttributes); 55488af8bbeSSebastian Andrzej Siewior 55588af8bbeSSebastian Andrzej Siewior static struct configfs_attribute *gadget_config_attrs[] = { 55645b6a73fSChristoph Hellwig &gadget_config_desc_attr_MaxPower, 55745b6a73fSChristoph Hellwig &gadget_config_desc_attr_bmAttributes, 55888af8bbeSSebastian Andrzej Siewior NULL, 55988af8bbeSSebastian Andrzej Siewior }; 56088af8bbeSSebastian Andrzej Siewior 5614ad01412SBhumika Goyal static const struct config_item_type gadget_config_type = { 56288af8bbeSSebastian Andrzej Siewior .ct_item_ops = &gadget_config_item_ops, 56388af8bbeSSebastian Andrzej Siewior .ct_attrs = gadget_config_attrs, 56488af8bbeSSebastian Andrzej Siewior .ct_owner = THIS_MODULE, 56588af8bbeSSebastian Andrzej Siewior }; 56688af8bbeSSebastian Andrzej Siewior 5674ad01412SBhumika Goyal static const struct config_item_type gadget_root_type = { 56888af8bbeSSebastian Andrzej Siewior .ct_item_ops = &gadget_root_item_ops, 56988af8bbeSSebastian Andrzej Siewior .ct_attrs = gadget_root_attrs, 57088af8bbeSSebastian Andrzej Siewior .ct_owner = THIS_MODULE, 57188af8bbeSSebastian Andrzej Siewior }; 57288af8bbeSSebastian Andrzej Siewior 57388af8bbeSSebastian Andrzej Siewior static void composite_init_dev(struct usb_composite_dev *cdev) 57488af8bbeSSebastian Andrzej Siewior { 57588af8bbeSSebastian Andrzej Siewior spin_lock_init(&cdev->lock); 57688af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&cdev->configs); 57788af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&cdev->gstrings); 57888af8bbeSSebastian Andrzej Siewior } 57988af8bbeSSebastian Andrzej Siewior 58088af8bbeSSebastian Andrzej Siewior static struct config_group *function_make( 58188af8bbeSSebastian Andrzej Siewior struct config_group *group, 58288af8bbeSSebastian Andrzej Siewior const char *name) 58388af8bbeSSebastian Andrzej Siewior { 58488af8bbeSSebastian Andrzej Siewior struct gadget_info *gi; 58588af8bbeSSebastian Andrzej Siewior struct usb_function_instance *fi; 58688af8bbeSSebastian Andrzej Siewior char buf[MAX_NAME_LEN]; 58788af8bbeSSebastian Andrzej Siewior char *func_name; 58888af8bbeSSebastian Andrzej Siewior char *instance_name; 58988af8bbeSSebastian Andrzej Siewior int ret; 59088af8bbeSSebastian Andrzej Siewior 59188af8bbeSSebastian Andrzej Siewior ret = snprintf(buf, MAX_NAME_LEN, "%s", name); 59288af8bbeSSebastian Andrzej Siewior if (ret >= MAX_NAME_LEN) 59388af8bbeSSebastian Andrzej Siewior return ERR_PTR(-ENAMETOOLONG); 59488af8bbeSSebastian Andrzej Siewior 59588af8bbeSSebastian Andrzej Siewior func_name = buf; 59688af8bbeSSebastian Andrzej Siewior instance_name = strchr(func_name, '.'); 59788af8bbeSSebastian Andrzej Siewior if (!instance_name) { 59888af8bbeSSebastian Andrzej Siewior pr_err("Unable to locate . in FUNC.INSTANCE\n"); 59988af8bbeSSebastian Andrzej Siewior return ERR_PTR(-EINVAL); 60088af8bbeSSebastian Andrzej Siewior } 60188af8bbeSSebastian Andrzej Siewior *instance_name = '\0'; 60288af8bbeSSebastian Andrzej Siewior instance_name++; 60388af8bbeSSebastian Andrzej Siewior 60488af8bbeSSebastian Andrzej Siewior fi = usb_get_function_instance(func_name); 60588af8bbeSSebastian Andrzej Siewior if (IS_ERR(fi)) 606a3469411SDuan Jiong return ERR_CAST(fi); 60788af8bbeSSebastian Andrzej Siewior 6083958b792SNicolas Iooss ret = config_item_set_name(&fi->group.cg_item, "%s", name); 60988af8bbeSSebastian Andrzej Siewior if (ret) { 61088af8bbeSSebastian Andrzej Siewior usb_put_function_instance(fi); 61188af8bbeSSebastian Andrzej Siewior return ERR_PTR(ret); 61288af8bbeSSebastian Andrzej Siewior } 6131933861dSAndrzej Pietrasiewicz if (fi->set_inst_name) { 6141933861dSAndrzej Pietrasiewicz ret = fi->set_inst_name(fi, instance_name); 6151933861dSAndrzej Pietrasiewicz if (ret) { 6161933861dSAndrzej Pietrasiewicz usb_put_function_instance(fi); 6171933861dSAndrzej Pietrasiewicz return ERR_PTR(ret); 6181933861dSAndrzej Pietrasiewicz } 6191933861dSAndrzej Pietrasiewicz } 62088af8bbeSSebastian Andrzej Siewior 62188af8bbeSSebastian Andrzej Siewior gi = container_of(group, struct gadget_info, functions_group); 62288af8bbeSSebastian Andrzej Siewior 62388af8bbeSSebastian Andrzej Siewior mutex_lock(&gi->lock); 62488af8bbeSSebastian Andrzej Siewior list_add_tail(&fi->cfs_list, &gi->available_func); 62588af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 62688af8bbeSSebastian Andrzej Siewior return &fi->group; 62788af8bbeSSebastian Andrzej Siewior } 62888af8bbeSSebastian Andrzej Siewior 62988af8bbeSSebastian Andrzej Siewior static void function_drop( 63088af8bbeSSebastian Andrzej Siewior struct config_group *group, 63188af8bbeSSebastian Andrzej Siewior struct config_item *item) 63288af8bbeSSebastian Andrzej Siewior { 63388af8bbeSSebastian Andrzej Siewior struct usb_function_instance *fi = to_usb_function_instance(item); 63488af8bbeSSebastian Andrzej Siewior struct gadget_info *gi; 63588af8bbeSSebastian Andrzej Siewior 63688af8bbeSSebastian Andrzej Siewior gi = container_of(group, struct gadget_info, functions_group); 63788af8bbeSSebastian Andrzej Siewior 63888af8bbeSSebastian Andrzej Siewior mutex_lock(&gi->lock); 63988af8bbeSSebastian Andrzej Siewior list_del(&fi->cfs_list); 64088af8bbeSSebastian Andrzej Siewior mutex_unlock(&gi->lock); 64188af8bbeSSebastian Andrzej Siewior config_item_put(item); 64288af8bbeSSebastian Andrzej Siewior } 64388af8bbeSSebastian Andrzej Siewior 64488af8bbeSSebastian Andrzej Siewior static struct configfs_group_operations functions_ops = { 64588af8bbeSSebastian Andrzej Siewior .make_group = &function_make, 64688af8bbeSSebastian Andrzej Siewior .drop_item = &function_drop, 64788af8bbeSSebastian Andrzej Siewior }; 64888af8bbeSSebastian Andrzej Siewior 6494ad01412SBhumika Goyal static const struct config_item_type functions_type = { 65088af8bbeSSebastian Andrzej Siewior .ct_group_ops = &functions_ops, 65188af8bbeSSebastian Andrzej Siewior .ct_owner = THIS_MODULE, 65288af8bbeSSebastian Andrzej Siewior }; 65388af8bbeSSebastian Andrzej Siewior 65488af8bbeSSebastian Andrzej Siewior GS_STRINGS_RW(gadget_config_name, configuration); 65588af8bbeSSebastian Andrzej Siewior 65688af8bbeSSebastian Andrzej Siewior static struct configfs_attribute *gadget_config_name_langid_attrs[] = { 65745b6a73fSChristoph Hellwig &gadget_config_name_attr_configuration, 65888af8bbeSSebastian Andrzej Siewior NULL, 65988af8bbeSSebastian Andrzej Siewior }; 66088af8bbeSSebastian Andrzej Siewior 66188af8bbeSSebastian Andrzej Siewior static void gadget_config_name_attr_release(struct config_item *item) 66288af8bbeSSebastian Andrzej Siewior { 66388af8bbeSSebastian Andrzej Siewior struct gadget_config_name *cn = to_gadget_config_name(item); 66488af8bbeSSebastian Andrzej Siewior 66588af8bbeSSebastian Andrzej Siewior kfree(cn->configuration); 66688af8bbeSSebastian Andrzej Siewior 66788af8bbeSSebastian Andrzej Siewior list_del(&cn->list); 66888af8bbeSSebastian Andrzej Siewior kfree(cn); 66988af8bbeSSebastian Andrzej Siewior } 67088af8bbeSSebastian Andrzej Siewior 67188af8bbeSSebastian Andrzej Siewior USB_CONFIG_STRING_RW_OPS(gadget_config_name); 67288af8bbeSSebastian Andrzej Siewior USB_CONFIG_STRINGS_LANG(gadget_config_name, config_usb_cfg); 67388af8bbeSSebastian Andrzej Siewior 67488af8bbeSSebastian Andrzej Siewior static struct config_group *config_desc_make( 67588af8bbeSSebastian Andrzej Siewior struct config_group *group, 67688af8bbeSSebastian Andrzej Siewior const char *name) 67788af8bbeSSebastian Andrzej Siewior { 67888af8bbeSSebastian Andrzej Siewior struct gadget_info *gi; 67988af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg; 68088af8bbeSSebastian Andrzej Siewior char buf[MAX_NAME_LEN]; 68188af8bbeSSebastian Andrzej Siewior char *num_str; 68288af8bbeSSebastian Andrzej Siewior u8 num; 68388af8bbeSSebastian Andrzej Siewior int ret; 68488af8bbeSSebastian Andrzej Siewior 68588af8bbeSSebastian Andrzej Siewior gi = container_of(group, struct gadget_info, configs_group); 68688af8bbeSSebastian Andrzej Siewior ret = snprintf(buf, MAX_NAME_LEN, "%s", name); 68788af8bbeSSebastian Andrzej Siewior if (ret >= MAX_NAME_LEN) 68888af8bbeSSebastian Andrzej Siewior return ERR_PTR(-ENAMETOOLONG); 68988af8bbeSSebastian Andrzej Siewior 69088af8bbeSSebastian Andrzej Siewior num_str = strchr(buf, '.'); 69188af8bbeSSebastian Andrzej Siewior if (!num_str) { 69288af8bbeSSebastian Andrzej Siewior pr_err("Unable to locate . in name.bConfigurationValue\n"); 69388af8bbeSSebastian Andrzej Siewior return ERR_PTR(-EINVAL); 69488af8bbeSSebastian Andrzej Siewior } 69588af8bbeSSebastian Andrzej Siewior 69688af8bbeSSebastian Andrzej Siewior *num_str = '\0'; 69788af8bbeSSebastian Andrzej Siewior num_str++; 69888af8bbeSSebastian Andrzej Siewior 69988af8bbeSSebastian Andrzej Siewior if (!strlen(buf)) 70088af8bbeSSebastian Andrzej Siewior return ERR_PTR(-EINVAL); 70188af8bbeSSebastian Andrzej Siewior 70288af8bbeSSebastian Andrzej Siewior ret = kstrtou8(num_str, 0, &num); 70388af8bbeSSebastian Andrzej Siewior if (ret) 70488af8bbeSSebastian Andrzej Siewior return ERR_PTR(ret); 70588af8bbeSSebastian Andrzej Siewior 70688af8bbeSSebastian Andrzej Siewior cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); 70788af8bbeSSebastian Andrzej Siewior if (!cfg) 70888af8bbeSSebastian Andrzej Siewior return ERR_PTR(-ENOMEM); 70988af8bbeSSebastian Andrzej Siewior cfg->c.label = kstrdup(buf, GFP_KERNEL); 71088af8bbeSSebastian Andrzej Siewior if (!cfg->c.label) { 71188af8bbeSSebastian Andrzej Siewior ret = -ENOMEM; 71288af8bbeSSebastian Andrzej Siewior goto err; 71388af8bbeSSebastian Andrzej Siewior } 71488af8bbeSSebastian Andrzej Siewior cfg->c.bConfigurationValue = num; 71588af8bbeSSebastian Andrzej Siewior cfg->c.MaxPower = CONFIG_USB_GADGET_VBUS_DRAW; 71688af8bbeSSebastian Andrzej Siewior cfg->c.bmAttributes = USB_CONFIG_ATT_ONE; 71788af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&cfg->string_list); 71888af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&cfg->func_list); 71988af8bbeSSebastian Andrzej Siewior 72088af8bbeSSebastian Andrzej Siewior config_group_init_type_name(&cfg->group, name, 72188af8bbeSSebastian Andrzej Siewior &gadget_config_type); 7221ae1602dSChristoph Hellwig 72388af8bbeSSebastian Andrzej Siewior config_group_init_type_name(&cfg->strings_group, "strings", 72488af8bbeSSebastian Andrzej Siewior &gadget_config_name_strings_type); 7251ae1602dSChristoph Hellwig configfs_add_default_group(&cfg->strings_group, &cfg->group); 72688af8bbeSSebastian Andrzej Siewior 72788af8bbeSSebastian Andrzej Siewior ret = usb_add_config_only(&gi->cdev, &cfg->c); 72888af8bbeSSebastian Andrzej Siewior if (ret) 72988af8bbeSSebastian Andrzej Siewior goto err; 73088af8bbeSSebastian Andrzej Siewior 73188af8bbeSSebastian Andrzej Siewior return &cfg->group; 73288af8bbeSSebastian Andrzej Siewior err: 73388af8bbeSSebastian Andrzej Siewior kfree(cfg->c.label); 73488af8bbeSSebastian Andrzej Siewior kfree(cfg); 73588af8bbeSSebastian Andrzej Siewior return ERR_PTR(ret); 73688af8bbeSSebastian Andrzej Siewior } 73788af8bbeSSebastian Andrzej Siewior 73888af8bbeSSebastian Andrzej Siewior static void config_desc_drop( 73988af8bbeSSebastian Andrzej Siewior struct config_group *group, 74088af8bbeSSebastian Andrzej Siewior struct config_item *item) 74188af8bbeSSebastian Andrzej Siewior { 74288af8bbeSSebastian Andrzej Siewior config_item_put(item); 74388af8bbeSSebastian Andrzej Siewior } 74488af8bbeSSebastian Andrzej Siewior 74588af8bbeSSebastian Andrzej Siewior static struct configfs_group_operations config_desc_ops = { 74688af8bbeSSebastian Andrzej Siewior .make_group = &config_desc_make, 74788af8bbeSSebastian Andrzej Siewior .drop_item = &config_desc_drop, 74888af8bbeSSebastian Andrzej Siewior }; 74988af8bbeSSebastian Andrzej Siewior 7504ad01412SBhumika Goyal static const struct config_item_type config_desc_type = { 75188af8bbeSSebastian Andrzej Siewior .ct_group_ops = &config_desc_ops, 75288af8bbeSSebastian Andrzej Siewior .ct_owner = THIS_MODULE, 75388af8bbeSSebastian Andrzej Siewior }; 75488af8bbeSSebastian Andrzej Siewior 75588af8bbeSSebastian Andrzej Siewior GS_STRINGS_RW(gadget_strings, manufacturer); 75688af8bbeSSebastian Andrzej Siewior GS_STRINGS_RW(gadget_strings, product); 75788af8bbeSSebastian Andrzej Siewior GS_STRINGS_RW(gadget_strings, serialnumber); 75888af8bbeSSebastian Andrzej Siewior 75988af8bbeSSebastian Andrzej Siewior static struct configfs_attribute *gadget_strings_langid_attrs[] = { 76045b6a73fSChristoph Hellwig &gadget_strings_attr_manufacturer, 76145b6a73fSChristoph Hellwig &gadget_strings_attr_product, 76245b6a73fSChristoph Hellwig &gadget_strings_attr_serialnumber, 76388af8bbeSSebastian Andrzej Siewior NULL, 76488af8bbeSSebastian Andrzej Siewior }; 76588af8bbeSSebastian Andrzej Siewior 76688af8bbeSSebastian Andrzej Siewior static void gadget_strings_attr_release(struct config_item *item) 76788af8bbeSSebastian Andrzej Siewior { 76888af8bbeSSebastian Andrzej Siewior struct gadget_strings *gs = to_gadget_strings(item); 76988af8bbeSSebastian Andrzej Siewior 77088af8bbeSSebastian Andrzej Siewior kfree(gs->manufacturer); 77188af8bbeSSebastian Andrzej Siewior kfree(gs->product); 77288af8bbeSSebastian Andrzej Siewior kfree(gs->serialnumber); 77388af8bbeSSebastian Andrzej Siewior 77488af8bbeSSebastian Andrzej Siewior list_del(&gs->list); 77588af8bbeSSebastian Andrzej Siewior kfree(gs); 77688af8bbeSSebastian Andrzej Siewior } 77788af8bbeSSebastian Andrzej Siewior 77888af8bbeSSebastian Andrzej Siewior USB_CONFIG_STRING_RW_OPS(gadget_strings); 77988af8bbeSSebastian Andrzej Siewior USB_CONFIG_STRINGS_LANG(gadget_strings, gadget_info); 78088af8bbeSSebastian Andrzej Siewior 78187213d38SAndrzej Pietrasiewicz static inline struct os_desc *to_os_desc(struct config_item *item) 78287213d38SAndrzej Pietrasiewicz { 78387213d38SAndrzej Pietrasiewicz return container_of(to_config_group(item), struct os_desc, group); 78487213d38SAndrzej Pietrasiewicz } 78587213d38SAndrzej Pietrasiewicz 78645b6a73fSChristoph Hellwig static inline struct gadget_info *os_desc_item_to_gadget_info( 78745b6a73fSChristoph Hellwig struct config_item *item) 78887213d38SAndrzej Pietrasiewicz { 78945b6a73fSChristoph Hellwig return to_gadget_info(to_os_desc(item)->group.cg_item.ci_parent); 79087213d38SAndrzej Pietrasiewicz } 79187213d38SAndrzej Pietrasiewicz 79245b6a73fSChristoph Hellwig static ssize_t os_desc_use_show(struct config_item *item, char *page) 79345b6a73fSChristoph Hellwig { 794e800e8cbSStefan Agner return sprintf(page, "%d\n", 79545b6a73fSChristoph Hellwig os_desc_item_to_gadget_info(item)->use_os_desc); 79645b6a73fSChristoph Hellwig } 79745b6a73fSChristoph Hellwig 79845b6a73fSChristoph Hellwig static ssize_t os_desc_use_store(struct config_item *item, const char *page, 79987213d38SAndrzej Pietrasiewicz size_t len) 80087213d38SAndrzej Pietrasiewicz { 80145b6a73fSChristoph Hellwig struct gadget_info *gi = os_desc_item_to_gadget_info(item); 80287213d38SAndrzej Pietrasiewicz int ret; 80387213d38SAndrzej Pietrasiewicz bool use; 80487213d38SAndrzej Pietrasiewicz 80587213d38SAndrzej Pietrasiewicz mutex_lock(&gi->lock); 80687213d38SAndrzej Pietrasiewicz ret = strtobool(page, &use); 80787213d38SAndrzej Pietrasiewicz if (!ret) { 80887213d38SAndrzej Pietrasiewicz gi->use_os_desc = use; 80987213d38SAndrzej Pietrasiewicz ret = len; 81087213d38SAndrzej Pietrasiewicz } 81187213d38SAndrzej Pietrasiewicz mutex_unlock(&gi->lock); 81287213d38SAndrzej Pietrasiewicz 81387213d38SAndrzej Pietrasiewicz return ret; 81487213d38SAndrzej Pietrasiewicz } 81587213d38SAndrzej Pietrasiewicz 81645b6a73fSChristoph Hellwig static ssize_t os_desc_b_vendor_code_show(struct config_item *item, char *page) 81787213d38SAndrzej Pietrasiewicz { 818e800e8cbSStefan Agner return sprintf(page, "0x%02x\n", 81945b6a73fSChristoph Hellwig os_desc_item_to_gadget_info(item)->b_vendor_code); 82087213d38SAndrzej Pietrasiewicz } 82187213d38SAndrzej Pietrasiewicz 82245b6a73fSChristoph Hellwig static ssize_t os_desc_b_vendor_code_store(struct config_item *item, 82387213d38SAndrzej Pietrasiewicz const char *page, size_t len) 82487213d38SAndrzej Pietrasiewicz { 82545b6a73fSChristoph Hellwig struct gadget_info *gi = os_desc_item_to_gadget_info(item); 82687213d38SAndrzej Pietrasiewicz int ret; 82787213d38SAndrzej Pietrasiewicz u8 b_vendor_code; 82887213d38SAndrzej Pietrasiewicz 82987213d38SAndrzej Pietrasiewicz mutex_lock(&gi->lock); 83087213d38SAndrzej Pietrasiewicz ret = kstrtou8(page, 0, &b_vendor_code); 83187213d38SAndrzej Pietrasiewicz if (!ret) { 83287213d38SAndrzej Pietrasiewicz gi->b_vendor_code = b_vendor_code; 83387213d38SAndrzej Pietrasiewicz ret = len; 83487213d38SAndrzej Pietrasiewicz } 83587213d38SAndrzej Pietrasiewicz mutex_unlock(&gi->lock); 83687213d38SAndrzej Pietrasiewicz 83787213d38SAndrzej Pietrasiewicz return ret; 83887213d38SAndrzej Pietrasiewicz } 83987213d38SAndrzej Pietrasiewicz 84045b6a73fSChristoph Hellwig static ssize_t os_desc_qw_sign_show(struct config_item *item, char *page) 84187213d38SAndrzej Pietrasiewicz { 84245b6a73fSChristoph Hellwig struct gadget_info *gi = os_desc_item_to_gadget_info(item); 84376180d71SStefan Agner int res; 84487213d38SAndrzej Pietrasiewicz 84576180d71SStefan Agner res = utf16s_to_utf8s((wchar_t *) gi->qw_sign, OS_STRING_QW_SIGN_LEN, 84676180d71SStefan Agner UTF16_LITTLE_ENDIAN, page, PAGE_SIZE - 1); 84776180d71SStefan Agner page[res++] = '\n'; 84876180d71SStefan Agner 84976180d71SStefan Agner return res; 85087213d38SAndrzej Pietrasiewicz } 85187213d38SAndrzej Pietrasiewicz 85245b6a73fSChristoph Hellwig static ssize_t os_desc_qw_sign_store(struct config_item *item, const char *page, 85387213d38SAndrzej Pietrasiewicz size_t len) 85487213d38SAndrzej Pietrasiewicz { 85545b6a73fSChristoph Hellwig struct gadget_info *gi = os_desc_item_to_gadget_info(item); 85687213d38SAndrzej Pietrasiewicz int res, l; 85787213d38SAndrzej Pietrasiewicz 85887213d38SAndrzej Pietrasiewicz l = min((int)len, OS_STRING_QW_SIGN_LEN >> 1); 85987213d38SAndrzej Pietrasiewicz if (page[l - 1] == '\n') 86087213d38SAndrzej Pietrasiewicz --l; 86187213d38SAndrzej Pietrasiewicz 86287213d38SAndrzej Pietrasiewicz mutex_lock(&gi->lock); 86387213d38SAndrzej Pietrasiewicz res = utf8s_to_utf16s(page, l, 86487213d38SAndrzej Pietrasiewicz UTF16_LITTLE_ENDIAN, (wchar_t *) gi->qw_sign, 86587213d38SAndrzej Pietrasiewicz OS_STRING_QW_SIGN_LEN); 86687213d38SAndrzej Pietrasiewicz if (res > 0) 86787213d38SAndrzej Pietrasiewicz res = len; 86887213d38SAndrzej Pietrasiewicz mutex_unlock(&gi->lock); 86987213d38SAndrzej Pietrasiewicz 87087213d38SAndrzej Pietrasiewicz return res; 87187213d38SAndrzej Pietrasiewicz } 87287213d38SAndrzej Pietrasiewicz 87345b6a73fSChristoph Hellwig CONFIGFS_ATTR(os_desc_, use); 87445b6a73fSChristoph Hellwig CONFIGFS_ATTR(os_desc_, b_vendor_code); 87545b6a73fSChristoph Hellwig CONFIGFS_ATTR(os_desc_, qw_sign); 87687213d38SAndrzej Pietrasiewicz 87787213d38SAndrzej Pietrasiewicz static struct configfs_attribute *os_desc_attrs[] = { 87845b6a73fSChristoph Hellwig &os_desc_attr_use, 87945b6a73fSChristoph Hellwig &os_desc_attr_b_vendor_code, 88045b6a73fSChristoph Hellwig &os_desc_attr_qw_sign, 88187213d38SAndrzej Pietrasiewicz NULL, 88287213d38SAndrzej Pietrasiewicz }; 88387213d38SAndrzej Pietrasiewicz 88487213d38SAndrzej Pietrasiewicz static void os_desc_attr_release(struct config_item *item) 88587213d38SAndrzej Pietrasiewicz { 88687213d38SAndrzej Pietrasiewicz struct os_desc *os_desc = to_os_desc(item); 88787213d38SAndrzej Pietrasiewicz kfree(os_desc); 88887213d38SAndrzej Pietrasiewicz } 88987213d38SAndrzej Pietrasiewicz 890da424314SAndrzej Pietrasiewicz static int os_desc_link(struct config_item *os_desc_ci, 891da424314SAndrzej Pietrasiewicz struct config_item *usb_cfg_ci) 892da424314SAndrzej Pietrasiewicz { 893da424314SAndrzej Pietrasiewicz struct gadget_info *gi = container_of(to_config_group(os_desc_ci), 894da424314SAndrzej Pietrasiewicz struct gadget_info, os_desc_group); 895da424314SAndrzej Pietrasiewicz struct usb_composite_dev *cdev = &gi->cdev; 896da424314SAndrzej Pietrasiewicz struct config_usb_cfg *c_target = 897da424314SAndrzej Pietrasiewicz container_of(to_config_group(usb_cfg_ci), 898da424314SAndrzej Pietrasiewicz struct config_usb_cfg, group); 899da424314SAndrzej Pietrasiewicz struct usb_configuration *c; 900da424314SAndrzej Pietrasiewicz int ret; 901da424314SAndrzej Pietrasiewicz 902da424314SAndrzej Pietrasiewicz mutex_lock(&gi->lock); 903da424314SAndrzej Pietrasiewicz list_for_each_entry(c, &cdev->configs, list) { 904da424314SAndrzej Pietrasiewicz if (c == &c_target->c) 905da424314SAndrzej Pietrasiewicz break; 906da424314SAndrzej Pietrasiewicz } 907da424314SAndrzej Pietrasiewicz if (c != &c_target->c) { 908da424314SAndrzej Pietrasiewicz ret = -EINVAL; 909da424314SAndrzej Pietrasiewicz goto out; 910da424314SAndrzej Pietrasiewicz } 911da424314SAndrzej Pietrasiewicz 912da424314SAndrzej Pietrasiewicz if (cdev->os_desc_config) { 913da424314SAndrzej Pietrasiewicz ret = -EBUSY; 914da424314SAndrzej Pietrasiewicz goto out; 915da424314SAndrzej Pietrasiewicz } 916da424314SAndrzej Pietrasiewicz 917da424314SAndrzej Pietrasiewicz cdev->os_desc_config = &c_target->c; 918da424314SAndrzej Pietrasiewicz ret = 0; 919da424314SAndrzej Pietrasiewicz 920da424314SAndrzej Pietrasiewicz out: 921da424314SAndrzej Pietrasiewicz mutex_unlock(&gi->lock); 922da424314SAndrzej Pietrasiewicz return ret; 923da424314SAndrzej Pietrasiewicz } 924da424314SAndrzej Pietrasiewicz 925e16769d4SAndrzej Pietrasiewicz static void os_desc_unlink(struct config_item *os_desc_ci, 926da424314SAndrzej Pietrasiewicz struct config_item *usb_cfg_ci) 927da424314SAndrzej Pietrasiewicz { 928da424314SAndrzej Pietrasiewicz struct gadget_info *gi = container_of(to_config_group(os_desc_ci), 929da424314SAndrzej Pietrasiewicz struct gadget_info, os_desc_group); 930da424314SAndrzej Pietrasiewicz struct usb_composite_dev *cdev = &gi->cdev; 931da424314SAndrzej Pietrasiewicz 932da424314SAndrzej Pietrasiewicz mutex_lock(&gi->lock); 933afdaadc3SRuslan Bilovol if (gi->composite.gadget_driver.udc_name) 934da424314SAndrzej Pietrasiewicz unregister_gadget(gi); 935da424314SAndrzej Pietrasiewicz cdev->os_desc_config = NULL; 936afdaadc3SRuslan Bilovol WARN_ON(gi->composite.gadget_driver.udc_name); 937da424314SAndrzej Pietrasiewicz mutex_unlock(&gi->lock); 938da424314SAndrzej Pietrasiewicz } 939da424314SAndrzej Pietrasiewicz 94087213d38SAndrzej Pietrasiewicz static struct configfs_item_operations os_desc_ops = { 94187213d38SAndrzej Pietrasiewicz .release = os_desc_attr_release, 942da424314SAndrzej Pietrasiewicz .allow_link = os_desc_link, 943da424314SAndrzej Pietrasiewicz .drop_link = os_desc_unlink, 94487213d38SAndrzej Pietrasiewicz }; 94587213d38SAndrzej Pietrasiewicz 94687213d38SAndrzej Pietrasiewicz static struct config_item_type os_desc_type = { 94787213d38SAndrzej Pietrasiewicz .ct_item_ops = &os_desc_ops, 94887213d38SAndrzej Pietrasiewicz .ct_attrs = os_desc_attrs, 94987213d38SAndrzej Pietrasiewicz .ct_owner = THIS_MODULE, 95087213d38SAndrzej Pietrasiewicz }; 95187213d38SAndrzej Pietrasiewicz 9527419485fSAndrzej Pietrasiewicz static inline struct usb_os_desc_ext_prop 9537419485fSAndrzej Pietrasiewicz *to_usb_os_desc_ext_prop(struct config_item *item) 9547419485fSAndrzej Pietrasiewicz { 9557419485fSAndrzej Pietrasiewicz return container_of(item, struct usb_os_desc_ext_prop, item); 9567419485fSAndrzej Pietrasiewicz } 9577419485fSAndrzej Pietrasiewicz 95845b6a73fSChristoph Hellwig static ssize_t ext_prop_type_show(struct config_item *item, char *page) 9597419485fSAndrzej Pietrasiewicz { 960e800e8cbSStefan Agner return sprintf(page, "%d\n", to_usb_os_desc_ext_prop(item)->type); 9617419485fSAndrzej Pietrasiewicz } 9627419485fSAndrzej Pietrasiewicz 96345b6a73fSChristoph Hellwig static ssize_t ext_prop_type_store(struct config_item *item, 9647419485fSAndrzej Pietrasiewicz const char *page, size_t len) 9657419485fSAndrzej Pietrasiewicz { 96645b6a73fSChristoph Hellwig struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); 9677419485fSAndrzej Pietrasiewicz struct usb_os_desc *desc = to_usb_os_desc(ext_prop->item.ci_parent); 9687419485fSAndrzej Pietrasiewicz u8 type; 9697419485fSAndrzej Pietrasiewicz int ret; 9707419485fSAndrzej Pietrasiewicz 9717419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 9727419485fSAndrzej Pietrasiewicz mutex_lock(desc->opts_mutex); 9737419485fSAndrzej Pietrasiewicz ret = kstrtou8(page, 0, &type); 9747419485fSAndrzej Pietrasiewicz if (ret) 9757419485fSAndrzej Pietrasiewicz goto end; 9767419485fSAndrzej Pietrasiewicz if (type < USB_EXT_PROP_UNICODE || type > USB_EXT_PROP_UNICODE_MULTI) { 9777419485fSAndrzej Pietrasiewicz ret = -EINVAL; 9787419485fSAndrzej Pietrasiewicz goto end; 9797419485fSAndrzej Pietrasiewicz } 9807419485fSAndrzej Pietrasiewicz 9817419485fSAndrzej Pietrasiewicz if ((ext_prop->type == USB_EXT_PROP_BINARY || 9827419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_LE32 || 9837419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_BE32) && 9847419485fSAndrzej Pietrasiewicz (type == USB_EXT_PROP_UNICODE || 9857419485fSAndrzej Pietrasiewicz type == USB_EXT_PROP_UNICODE_ENV || 9867419485fSAndrzej Pietrasiewicz type == USB_EXT_PROP_UNICODE_LINK)) 9877419485fSAndrzej Pietrasiewicz ext_prop->data_len <<= 1; 9887419485fSAndrzej Pietrasiewicz else if ((ext_prop->type == USB_EXT_PROP_UNICODE || 9897419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_UNICODE_ENV || 9907419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_UNICODE_LINK) && 9917419485fSAndrzej Pietrasiewicz (type == USB_EXT_PROP_BINARY || 9927419485fSAndrzej Pietrasiewicz type == USB_EXT_PROP_LE32 || 9937419485fSAndrzej Pietrasiewicz type == USB_EXT_PROP_BE32)) 9947419485fSAndrzej Pietrasiewicz ext_prop->data_len >>= 1; 9957419485fSAndrzej Pietrasiewicz ext_prop->type = type; 9967419485fSAndrzej Pietrasiewicz ret = len; 9977419485fSAndrzej Pietrasiewicz 9987419485fSAndrzej Pietrasiewicz end: 9997419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 10007419485fSAndrzej Pietrasiewicz mutex_unlock(desc->opts_mutex); 10017419485fSAndrzej Pietrasiewicz return ret; 10027419485fSAndrzej Pietrasiewicz } 10037419485fSAndrzej Pietrasiewicz 100445b6a73fSChristoph Hellwig static ssize_t ext_prop_data_show(struct config_item *item, char *page) 10057419485fSAndrzej Pietrasiewicz { 100645b6a73fSChristoph Hellwig struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); 10077419485fSAndrzej Pietrasiewicz int len = ext_prop->data_len; 10087419485fSAndrzej Pietrasiewicz 10097419485fSAndrzej Pietrasiewicz if (ext_prop->type == USB_EXT_PROP_UNICODE || 10107419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_UNICODE_ENV || 10117419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_UNICODE_LINK) 10127419485fSAndrzej Pietrasiewicz len >>= 1; 10137419485fSAndrzej Pietrasiewicz memcpy(page, ext_prop->data, len); 10147419485fSAndrzej Pietrasiewicz 10157419485fSAndrzej Pietrasiewicz return len; 10167419485fSAndrzej Pietrasiewicz } 10177419485fSAndrzej Pietrasiewicz 101845b6a73fSChristoph Hellwig static ssize_t ext_prop_data_store(struct config_item *item, 10197419485fSAndrzej Pietrasiewicz const char *page, size_t len) 10207419485fSAndrzej Pietrasiewicz { 102145b6a73fSChristoph Hellwig struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); 10227419485fSAndrzej Pietrasiewicz struct usb_os_desc *desc = to_usb_os_desc(ext_prop->item.ci_parent); 10237419485fSAndrzej Pietrasiewicz char *new_data; 10247419485fSAndrzej Pietrasiewicz size_t ret_len = len; 10257419485fSAndrzej Pietrasiewicz 10267419485fSAndrzej Pietrasiewicz if (page[len - 1] == '\n' || page[len - 1] == '\0') 10277419485fSAndrzej Pietrasiewicz --len; 102858b949e0SBenoit Taine new_data = kmemdup(page, len, GFP_KERNEL); 10297419485fSAndrzej Pietrasiewicz if (!new_data) 10307419485fSAndrzej Pietrasiewicz return -ENOMEM; 10317419485fSAndrzej Pietrasiewicz 10327419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 10337419485fSAndrzej Pietrasiewicz mutex_lock(desc->opts_mutex); 10347419485fSAndrzej Pietrasiewicz kfree(ext_prop->data); 10357419485fSAndrzej Pietrasiewicz ext_prop->data = new_data; 10367419485fSAndrzej Pietrasiewicz desc->ext_prop_len -= ext_prop->data_len; 10377419485fSAndrzej Pietrasiewicz ext_prop->data_len = len; 10387419485fSAndrzej Pietrasiewicz desc->ext_prop_len += ext_prop->data_len; 10397419485fSAndrzej Pietrasiewicz if (ext_prop->type == USB_EXT_PROP_UNICODE || 10407419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_UNICODE_ENV || 10417419485fSAndrzej Pietrasiewicz ext_prop->type == USB_EXT_PROP_UNICODE_LINK) { 10427419485fSAndrzej Pietrasiewicz desc->ext_prop_len -= ext_prop->data_len; 10437419485fSAndrzej Pietrasiewicz ext_prop->data_len <<= 1; 10447419485fSAndrzej Pietrasiewicz ext_prop->data_len += 2; 10457419485fSAndrzej Pietrasiewicz desc->ext_prop_len += ext_prop->data_len; 10467419485fSAndrzej Pietrasiewicz } 10477419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 10487419485fSAndrzej Pietrasiewicz mutex_unlock(desc->opts_mutex); 10497419485fSAndrzej Pietrasiewicz return ret_len; 10507419485fSAndrzej Pietrasiewicz } 10517419485fSAndrzej Pietrasiewicz 105245b6a73fSChristoph Hellwig CONFIGFS_ATTR(ext_prop_, type); 105345b6a73fSChristoph Hellwig CONFIGFS_ATTR(ext_prop_, data); 10547419485fSAndrzej Pietrasiewicz 10557419485fSAndrzej Pietrasiewicz static struct configfs_attribute *ext_prop_attrs[] = { 105645b6a73fSChristoph Hellwig &ext_prop_attr_type, 105745b6a73fSChristoph Hellwig &ext_prop_attr_data, 10587419485fSAndrzej Pietrasiewicz NULL, 10597419485fSAndrzej Pietrasiewicz }; 10607419485fSAndrzej Pietrasiewicz 10617419485fSAndrzej Pietrasiewicz static void usb_os_desc_ext_prop_release(struct config_item *item) 10627419485fSAndrzej Pietrasiewicz { 10637419485fSAndrzej Pietrasiewicz struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); 10647419485fSAndrzej Pietrasiewicz 10657419485fSAndrzej Pietrasiewicz kfree(ext_prop); /* frees a whole chunk */ 10667419485fSAndrzej Pietrasiewicz } 10677419485fSAndrzej Pietrasiewicz 10687419485fSAndrzej Pietrasiewicz static struct configfs_item_operations ext_prop_ops = { 10697419485fSAndrzej Pietrasiewicz .release = usb_os_desc_ext_prop_release, 10707419485fSAndrzej Pietrasiewicz }; 10717419485fSAndrzej Pietrasiewicz 10727419485fSAndrzej Pietrasiewicz static struct config_item *ext_prop_make( 10737419485fSAndrzej Pietrasiewicz struct config_group *group, 10747419485fSAndrzej Pietrasiewicz const char *name) 10757419485fSAndrzej Pietrasiewicz { 10767419485fSAndrzej Pietrasiewicz struct usb_os_desc_ext_prop *ext_prop; 10777419485fSAndrzej Pietrasiewicz struct config_item_type *ext_prop_type; 10787419485fSAndrzej Pietrasiewicz struct usb_os_desc *desc; 10797419485fSAndrzej Pietrasiewicz char *vlabuf; 10807419485fSAndrzej Pietrasiewicz 10817419485fSAndrzej Pietrasiewicz vla_group(data_chunk); 10827419485fSAndrzej Pietrasiewicz vla_item(data_chunk, struct usb_os_desc_ext_prop, ext_prop, 1); 10837419485fSAndrzej Pietrasiewicz vla_item(data_chunk, struct config_item_type, ext_prop_type, 1); 10847419485fSAndrzej Pietrasiewicz 10857419485fSAndrzej Pietrasiewicz vlabuf = kzalloc(vla_group_size(data_chunk), GFP_KERNEL); 10867419485fSAndrzej Pietrasiewicz if (!vlabuf) 10877419485fSAndrzej Pietrasiewicz return ERR_PTR(-ENOMEM); 10887419485fSAndrzej Pietrasiewicz 10897419485fSAndrzej Pietrasiewicz ext_prop = vla_ptr(vlabuf, data_chunk, ext_prop); 10907419485fSAndrzej Pietrasiewicz ext_prop_type = vla_ptr(vlabuf, data_chunk, ext_prop_type); 10917419485fSAndrzej Pietrasiewicz 10927419485fSAndrzej Pietrasiewicz desc = container_of(group, struct usb_os_desc, group); 10937419485fSAndrzej Pietrasiewicz ext_prop_type->ct_item_ops = &ext_prop_ops; 10947419485fSAndrzej Pietrasiewicz ext_prop_type->ct_attrs = ext_prop_attrs; 10957419485fSAndrzej Pietrasiewicz ext_prop_type->ct_owner = desc->owner; 10967419485fSAndrzej Pietrasiewicz 10977419485fSAndrzej Pietrasiewicz config_item_init_type_name(&ext_prop->item, name, ext_prop_type); 10987419485fSAndrzej Pietrasiewicz 10997419485fSAndrzej Pietrasiewicz ext_prop->name = kstrdup(name, GFP_KERNEL); 11007419485fSAndrzej Pietrasiewicz if (!ext_prop->name) { 11017419485fSAndrzej Pietrasiewicz kfree(vlabuf); 11027419485fSAndrzej Pietrasiewicz return ERR_PTR(-ENOMEM); 11037419485fSAndrzej Pietrasiewicz } 11047419485fSAndrzej Pietrasiewicz desc->ext_prop_len += 14; 11057419485fSAndrzej Pietrasiewicz ext_prop->name_len = 2 * strlen(ext_prop->name) + 2; 11067419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 11077419485fSAndrzej Pietrasiewicz mutex_lock(desc->opts_mutex); 11087419485fSAndrzej Pietrasiewicz desc->ext_prop_len += ext_prop->name_len; 11097419485fSAndrzej Pietrasiewicz list_add_tail(&ext_prop->entry, &desc->ext_prop); 11107419485fSAndrzej Pietrasiewicz ++desc->ext_prop_count; 11117419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 11127419485fSAndrzej Pietrasiewicz mutex_unlock(desc->opts_mutex); 11137419485fSAndrzej Pietrasiewicz 11147419485fSAndrzej Pietrasiewicz return &ext_prop->item; 11157419485fSAndrzej Pietrasiewicz } 11167419485fSAndrzej Pietrasiewicz 11177419485fSAndrzej Pietrasiewicz static void ext_prop_drop(struct config_group *group, struct config_item *item) 11187419485fSAndrzej Pietrasiewicz { 11197419485fSAndrzej Pietrasiewicz struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); 11207419485fSAndrzej Pietrasiewicz struct usb_os_desc *desc = to_usb_os_desc(&group->cg_item); 11217419485fSAndrzej Pietrasiewicz 11227419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 11237419485fSAndrzej Pietrasiewicz mutex_lock(desc->opts_mutex); 11247419485fSAndrzej Pietrasiewicz list_del(&ext_prop->entry); 11257419485fSAndrzej Pietrasiewicz --desc->ext_prop_count; 11267419485fSAndrzej Pietrasiewicz kfree(ext_prop->name); 11277419485fSAndrzej Pietrasiewicz desc->ext_prop_len -= (ext_prop->name_len + ext_prop->data_len + 14); 11287419485fSAndrzej Pietrasiewicz if (desc->opts_mutex) 11297419485fSAndrzej Pietrasiewicz mutex_unlock(desc->opts_mutex); 11307419485fSAndrzej Pietrasiewicz config_item_put(item); 11317419485fSAndrzej Pietrasiewicz } 11327419485fSAndrzej Pietrasiewicz 11337419485fSAndrzej Pietrasiewicz static struct configfs_group_operations interf_grp_ops = { 11347419485fSAndrzej Pietrasiewicz .make_item = &ext_prop_make, 11357419485fSAndrzej Pietrasiewicz .drop_item = &ext_prop_drop, 11367419485fSAndrzej Pietrasiewicz }; 11377419485fSAndrzej Pietrasiewicz 113845b6a73fSChristoph Hellwig static ssize_t interf_grp_compatible_id_show(struct config_item *item, 1139da424314SAndrzej Pietrasiewicz char *page) 1140da424314SAndrzej Pietrasiewicz { 114145b6a73fSChristoph Hellwig memcpy(page, to_usb_os_desc(item)->ext_compat_id, 8); 1142da424314SAndrzej Pietrasiewicz return 8; 1143da424314SAndrzej Pietrasiewicz } 1144da424314SAndrzej Pietrasiewicz 114545b6a73fSChristoph Hellwig static ssize_t interf_grp_compatible_id_store(struct config_item *item, 1146da424314SAndrzej Pietrasiewicz const char *page, size_t len) 1147da424314SAndrzej Pietrasiewicz { 114845b6a73fSChristoph Hellwig struct usb_os_desc *desc = to_usb_os_desc(item); 1149da424314SAndrzej Pietrasiewicz int l; 1150da424314SAndrzej Pietrasiewicz 1151da424314SAndrzej Pietrasiewicz l = min_t(int, 8, len); 1152da424314SAndrzej Pietrasiewicz if (page[l - 1] == '\n') 1153da424314SAndrzej Pietrasiewicz --l; 1154da424314SAndrzej Pietrasiewicz if (desc->opts_mutex) 1155da424314SAndrzej Pietrasiewicz mutex_lock(desc->opts_mutex); 1156da424314SAndrzej Pietrasiewicz memcpy(desc->ext_compat_id, page, l); 1157da424314SAndrzej Pietrasiewicz 1158da424314SAndrzej Pietrasiewicz if (desc->opts_mutex) 1159da424314SAndrzej Pietrasiewicz mutex_unlock(desc->opts_mutex); 1160da424314SAndrzej Pietrasiewicz 1161da424314SAndrzej Pietrasiewicz return len; 1162da424314SAndrzej Pietrasiewicz } 1163da424314SAndrzej Pietrasiewicz 116445b6a73fSChristoph Hellwig static ssize_t interf_grp_sub_compatible_id_show(struct config_item *item, 1165da424314SAndrzej Pietrasiewicz char *page) 1166da424314SAndrzej Pietrasiewicz { 116745b6a73fSChristoph Hellwig memcpy(page, to_usb_os_desc(item)->ext_compat_id + 8, 8); 1168da424314SAndrzej Pietrasiewicz return 8; 1169da424314SAndrzej Pietrasiewicz } 1170da424314SAndrzej Pietrasiewicz 117145b6a73fSChristoph Hellwig static ssize_t interf_grp_sub_compatible_id_store(struct config_item *item, 1172da424314SAndrzej Pietrasiewicz const char *page, size_t len) 1173da424314SAndrzej Pietrasiewicz { 117445b6a73fSChristoph Hellwig struct usb_os_desc *desc = to_usb_os_desc(item); 1175da424314SAndrzej Pietrasiewicz int l; 1176da424314SAndrzej Pietrasiewicz 1177da424314SAndrzej Pietrasiewicz l = min_t(int, 8, len); 1178da424314SAndrzej Pietrasiewicz if (page[l - 1] == '\n') 1179da424314SAndrzej Pietrasiewicz --l; 1180da424314SAndrzej Pietrasiewicz if (desc->opts_mutex) 1181da424314SAndrzej Pietrasiewicz mutex_lock(desc->opts_mutex); 1182da424314SAndrzej Pietrasiewicz memcpy(desc->ext_compat_id + 8, page, l); 1183da424314SAndrzej Pietrasiewicz 1184da424314SAndrzej Pietrasiewicz if (desc->opts_mutex) 1185da424314SAndrzej Pietrasiewicz mutex_unlock(desc->opts_mutex); 1186da424314SAndrzej Pietrasiewicz 1187da424314SAndrzej Pietrasiewicz return len; 1188da424314SAndrzej Pietrasiewicz } 1189da424314SAndrzej Pietrasiewicz 119045b6a73fSChristoph Hellwig CONFIGFS_ATTR(interf_grp_, compatible_id); 119145b6a73fSChristoph Hellwig CONFIGFS_ATTR(interf_grp_, sub_compatible_id); 1192da424314SAndrzej Pietrasiewicz 1193da424314SAndrzej Pietrasiewicz static struct configfs_attribute *interf_grp_attrs[] = { 119445b6a73fSChristoph Hellwig &interf_grp_attr_compatible_id, 119545b6a73fSChristoph Hellwig &interf_grp_attr_sub_compatible_id, 1196da424314SAndrzej Pietrasiewicz NULL 1197da424314SAndrzej Pietrasiewicz }; 1198da424314SAndrzej Pietrasiewicz 1199ff74745eSAndrew Gabbasov struct config_group *usb_os_desc_prepare_interf_dir( 1200ff74745eSAndrew Gabbasov struct config_group *parent, 1201da424314SAndrzej Pietrasiewicz int n_interf, 1202da424314SAndrzej Pietrasiewicz struct usb_os_desc **desc, 120314574b54SAndrzej Pietrasiewicz char **names, 1204da424314SAndrzej Pietrasiewicz struct module *owner) 1205da424314SAndrzej Pietrasiewicz { 12061ae1602dSChristoph Hellwig struct config_group *os_desc_group; 1207da424314SAndrzej Pietrasiewicz struct config_item_type *os_desc_type, *interface_type; 1208da424314SAndrzej Pietrasiewicz 1209da424314SAndrzej Pietrasiewicz vla_group(data_chunk); 1210da424314SAndrzej Pietrasiewicz vla_item(data_chunk, struct config_group, os_desc_group, 1); 1211da424314SAndrzej Pietrasiewicz vla_item(data_chunk, struct config_item_type, os_desc_type, 1); 1212da424314SAndrzej Pietrasiewicz vla_item(data_chunk, struct config_item_type, interface_type, 1); 1213da424314SAndrzej Pietrasiewicz 1214da424314SAndrzej Pietrasiewicz char *vlabuf = kzalloc(vla_group_size(data_chunk), GFP_KERNEL); 1215da424314SAndrzej Pietrasiewicz if (!vlabuf) 1216ff74745eSAndrew Gabbasov return ERR_PTR(-ENOMEM); 1217da424314SAndrzej Pietrasiewicz 1218da424314SAndrzej Pietrasiewicz os_desc_group = vla_ptr(vlabuf, data_chunk, os_desc_group); 1219da424314SAndrzej Pietrasiewicz os_desc_type = vla_ptr(vlabuf, data_chunk, os_desc_type); 1220da424314SAndrzej Pietrasiewicz interface_type = vla_ptr(vlabuf, data_chunk, interface_type); 1221da424314SAndrzej Pietrasiewicz 1222da424314SAndrzej Pietrasiewicz os_desc_type->ct_owner = owner; 1223da424314SAndrzej Pietrasiewicz config_group_init_type_name(os_desc_group, "os_desc", os_desc_type); 12241ae1602dSChristoph Hellwig configfs_add_default_group(os_desc_group, parent); 1225da424314SAndrzej Pietrasiewicz 12267419485fSAndrzej Pietrasiewicz interface_type->ct_group_ops = &interf_grp_ops; 1227da424314SAndrzej Pietrasiewicz interface_type->ct_attrs = interf_grp_attrs; 1228da424314SAndrzej Pietrasiewicz interface_type->ct_owner = owner; 1229da424314SAndrzej Pietrasiewicz 1230da424314SAndrzej Pietrasiewicz while (n_interf--) { 1231da424314SAndrzej Pietrasiewicz struct usb_os_desc *d; 1232da424314SAndrzej Pietrasiewicz 1233da424314SAndrzej Pietrasiewicz d = desc[n_interf]; 12347419485fSAndrzej Pietrasiewicz d->owner = owner; 1235da424314SAndrzej Pietrasiewicz config_group_init_type_name(&d->group, "", interface_type); 123614574b54SAndrzej Pietrasiewicz config_item_set_name(&d->group.cg_item, "interface.%s", 123714574b54SAndrzej Pietrasiewicz names[n_interf]); 12381ae1602dSChristoph Hellwig configfs_add_default_group(&d->group, os_desc_group); 1239da424314SAndrzej Pietrasiewicz } 1240da424314SAndrzej Pietrasiewicz 1241ff74745eSAndrew Gabbasov return os_desc_group; 1242da424314SAndrzej Pietrasiewicz } 1243da424314SAndrzej Pietrasiewicz EXPORT_SYMBOL(usb_os_desc_prepare_interf_dir); 1244da424314SAndrzej Pietrasiewicz 124588af8bbeSSebastian Andrzej Siewior static int configfs_do_nothing(struct usb_composite_dev *cdev) 124688af8bbeSSebastian Andrzej Siewior { 124775bfe23aSDavid Rientjes WARN_ON(1); 124888af8bbeSSebastian Andrzej Siewior return -EINVAL; 124988af8bbeSSebastian Andrzej Siewior } 125088af8bbeSSebastian Andrzej Siewior 125188af8bbeSSebastian Andrzej Siewior int composite_dev_prepare(struct usb_composite_driver *composite, 125288af8bbeSSebastian Andrzej Siewior struct usb_composite_dev *dev); 125388af8bbeSSebastian Andrzej Siewior 1254da424314SAndrzej Pietrasiewicz int composite_os_desc_req_prepare(struct usb_composite_dev *cdev, 1255da424314SAndrzej Pietrasiewicz struct usb_ep *ep0); 1256da424314SAndrzej Pietrasiewicz 125788af8bbeSSebastian Andrzej Siewior static void purge_configs_funcs(struct gadget_info *gi) 125888af8bbeSSebastian Andrzej Siewior { 125988af8bbeSSebastian Andrzej Siewior struct usb_configuration *c; 126088af8bbeSSebastian Andrzej Siewior 126188af8bbeSSebastian Andrzej Siewior list_for_each_entry(c, &gi->cdev.configs, list) { 126288af8bbeSSebastian Andrzej Siewior struct usb_function *f, *tmp; 126388af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg; 126488af8bbeSSebastian Andrzej Siewior 126588af8bbeSSebastian Andrzej Siewior cfg = container_of(c, struct config_usb_cfg, c); 126688af8bbeSSebastian Andrzej Siewior 12676cd0fe91SChandana Kishori Chiluveru list_for_each_entry_safe_reverse(f, tmp, &c->functions, list) { 126888af8bbeSSebastian Andrzej Siewior 12696cd0fe91SChandana Kishori Chiluveru list_move(&f->list, &cfg->func_list); 127088af8bbeSSebastian Andrzej Siewior if (f->unbind) { 1271da7b895dSRomain Izard dev_dbg(&gi->cdev.gadget->dev, 1272a08f5dbfSRomain Izard "unbind function '%s'/%p\n", 1273a08f5dbfSRomain Izard f->name, f); 127488af8bbeSSebastian Andrzej Siewior f->unbind(c, f); 127588af8bbeSSebastian Andrzej Siewior } 127688af8bbeSSebastian Andrzej Siewior } 127788af8bbeSSebastian Andrzej Siewior c->next_interface_id = 0; 1278903124feSKrzysztof Opasiak memset(c->interface, 0, sizeof(c->interface)); 1279554eead5SJohn Youn c->superspeed_plus = 0; 128088af8bbeSSebastian Andrzej Siewior c->superspeed = 0; 128188af8bbeSSebastian Andrzej Siewior c->highspeed = 0; 128288af8bbeSSebastian Andrzej Siewior c->fullspeed = 0; 128388af8bbeSSebastian Andrzej Siewior } 128488af8bbeSSebastian Andrzej Siewior } 128588af8bbeSSebastian Andrzej Siewior 128688af8bbeSSebastian Andrzej Siewior static int configfs_composite_bind(struct usb_gadget *gadget, 128788af8bbeSSebastian Andrzej Siewior struct usb_gadget_driver *gdriver) 128888af8bbeSSebastian Andrzej Siewior { 128988af8bbeSSebastian Andrzej Siewior struct usb_composite_driver *composite = to_cdriver(gdriver); 129088af8bbeSSebastian Andrzej Siewior struct gadget_info *gi = container_of(composite, 129188af8bbeSSebastian Andrzej Siewior struct gadget_info, composite); 129288af8bbeSSebastian Andrzej Siewior struct usb_composite_dev *cdev = &gi->cdev; 129388af8bbeSSebastian Andrzej Siewior struct usb_configuration *c; 129488af8bbeSSebastian Andrzej Siewior struct usb_string *s; 129588af8bbeSSebastian Andrzej Siewior unsigned i; 129688af8bbeSSebastian Andrzej Siewior int ret; 129788af8bbeSSebastian Andrzej Siewior 129888af8bbeSSebastian Andrzej Siewior /* the gi->lock is hold by the caller */ 12991a1c851bSPeter Chen gi->unbind = 0; 130088af8bbeSSebastian Andrzej Siewior cdev->gadget = gadget; 130188af8bbeSSebastian Andrzej Siewior set_gadget_data(gadget, cdev); 130288af8bbeSSebastian Andrzej Siewior ret = composite_dev_prepare(composite, cdev); 130388af8bbeSSebastian Andrzej Siewior if (ret) 130488af8bbeSSebastian Andrzej Siewior return ret; 130588af8bbeSSebastian Andrzej Siewior /* and now the gadget bind */ 130688af8bbeSSebastian Andrzej Siewior ret = -EINVAL; 130788af8bbeSSebastian Andrzej Siewior 130888af8bbeSSebastian Andrzej Siewior if (list_empty(&gi->cdev.configs)) { 130988af8bbeSSebastian Andrzej Siewior pr_err("Need at least one configuration in %s.\n", 131088af8bbeSSebastian Andrzej Siewior gi->composite.name); 131188af8bbeSSebastian Andrzej Siewior goto err_comp_cleanup; 131288af8bbeSSebastian Andrzej Siewior } 131388af8bbeSSebastian Andrzej Siewior 131488af8bbeSSebastian Andrzej Siewior 131588af8bbeSSebastian Andrzej Siewior list_for_each_entry(c, &gi->cdev.configs, list) { 131688af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg; 131788af8bbeSSebastian Andrzej Siewior 131888af8bbeSSebastian Andrzej Siewior cfg = container_of(c, struct config_usb_cfg, c); 131988af8bbeSSebastian Andrzej Siewior if (list_empty(&cfg->func_list)) { 132088af8bbeSSebastian Andrzej Siewior pr_err("Config %s/%d of %s needs at least one function.\n", 132188af8bbeSSebastian Andrzej Siewior c->label, c->bConfigurationValue, 132288af8bbeSSebastian Andrzej Siewior gi->composite.name); 132388af8bbeSSebastian Andrzej Siewior goto err_comp_cleanup; 132488af8bbeSSebastian Andrzej Siewior } 132588af8bbeSSebastian Andrzej Siewior } 132688af8bbeSSebastian Andrzej Siewior 132788af8bbeSSebastian Andrzej Siewior /* init all strings */ 132888af8bbeSSebastian Andrzej Siewior if (!list_empty(&gi->string_list)) { 132988af8bbeSSebastian Andrzej Siewior struct gadget_strings *gs; 133088af8bbeSSebastian Andrzej Siewior 133188af8bbeSSebastian Andrzej Siewior i = 0; 133288af8bbeSSebastian Andrzej Siewior list_for_each_entry(gs, &gi->string_list, list) { 133388af8bbeSSebastian Andrzej Siewior 133488af8bbeSSebastian Andrzej Siewior gi->gstrings[i] = &gs->stringtab_dev; 133588af8bbeSSebastian Andrzej Siewior gs->stringtab_dev.strings = gs->strings; 133688af8bbeSSebastian Andrzej Siewior gs->strings[USB_GADGET_MANUFACTURER_IDX].s = 133788af8bbeSSebastian Andrzej Siewior gs->manufacturer; 133888af8bbeSSebastian Andrzej Siewior gs->strings[USB_GADGET_PRODUCT_IDX].s = gs->product; 133988af8bbeSSebastian Andrzej Siewior gs->strings[USB_GADGET_SERIAL_IDX].s = gs->serialnumber; 134088af8bbeSSebastian Andrzej Siewior i++; 134188af8bbeSSebastian Andrzej Siewior } 134288af8bbeSSebastian Andrzej Siewior gi->gstrings[i] = NULL; 134388af8bbeSSebastian Andrzej Siewior s = usb_gstrings_attach(&gi->cdev, gi->gstrings, 134488af8bbeSSebastian Andrzej Siewior USB_GADGET_FIRST_AVAIL_IDX); 1345fea77077SWei Yongjun if (IS_ERR(s)) { 1346fea77077SWei Yongjun ret = PTR_ERR(s); 134788af8bbeSSebastian Andrzej Siewior goto err_comp_cleanup; 1348fea77077SWei Yongjun } 134988af8bbeSSebastian Andrzej Siewior 135088af8bbeSSebastian Andrzej Siewior gi->cdev.desc.iManufacturer = s[USB_GADGET_MANUFACTURER_IDX].id; 135188af8bbeSSebastian Andrzej Siewior gi->cdev.desc.iProduct = s[USB_GADGET_PRODUCT_IDX].id; 135288af8bbeSSebastian Andrzej Siewior gi->cdev.desc.iSerialNumber = s[USB_GADGET_SERIAL_IDX].id; 135388af8bbeSSebastian Andrzej Siewior } 135488af8bbeSSebastian Andrzej Siewior 135587213d38SAndrzej Pietrasiewicz if (gi->use_os_desc) { 135687213d38SAndrzej Pietrasiewicz cdev->use_os_string = true; 135787213d38SAndrzej Pietrasiewicz cdev->b_vendor_code = gi->b_vendor_code; 135887213d38SAndrzej Pietrasiewicz memcpy(cdev->qw_sign, gi->qw_sign, OS_STRING_QW_SIGN_LEN); 135987213d38SAndrzej Pietrasiewicz } 136087213d38SAndrzej Pietrasiewicz 136141ce84c8SLi Jun if (gadget_is_otg(gadget) && !otg_desc[0]) { 136241ce84c8SLi Jun struct usb_descriptor_header *usb_desc; 136341ce84c8SLi Jun 136441ce84c8SLi Jun usb_desc = usb_otg_descriptor_alloc(gadget); 136541ce84c8SLi Jun if (!usb_desc) { 136641ce84c8SLi Jun ret = -ENOMEM; 136741ce84c8SLi Jun goto err_comp_cleanup; 136841ce84c8SLi Jun } 136941ce84c8SLi Jun usb_otg_descriptor_init(gadget, usb_desc); 137041ce84c8SLi Jun otg_desc[0] = usb_desc; 137141ce84c8SLi Jun otg_desc[1] = NULL; 137241ce84c8SLi Jun } 137341ce84c8SLi Jun 137488af8bbeSSebastian Andrzej Siewior /* Go through all configs, attach all functions */ 137588af8bbeSSebastian Andrzej Siewior list_for_each_entry(c, &gi->cdev.configs, list) { 137688af8bbeSSebastian Andrzej Siewior struct config_usb_cfg *cfg; 137788af8bbeSSebastian Andrzej Siewior struct usb_function *f; 137888af8bbeSSebastian Andrzej Siewior struct usb_function *tmp; 137988af8bbeSSebastian Andrzej Siewior struct gadget_config_name *cn; 138088af8bbeSSebastian Andrzej Siewior 138141ce84c8SLi Jun if (gadget_is_otg(gadget)) 138241ce84c8SLi Jun c->descriptors = otg_desc; 138341ce84c8SLi Jun 138488af8bbeSSebastian Andrzej Siewior cfg = container_of(c, struct config_usb_cfg, c); 138588af8bbeSSebastian Andrzej Siewior if (!list_empty(&cfg->string_list)) { 138688af8bbeSSebastian Andrzej Siewior i = 0; 138788af8bbeSSebastian Andrzej Siewior list_for_each_entry(cn, &cfg->string_list, list) { 138888af8bbeSSebastian Andrzej Siewior cfg->gstrings[i] = &cn->stringtab_dev; 138988af8bbeSSebastian Andrzej Siewior cn->stringtab_dev.strings = &cn->strings; 139088af8bbeSSebastian Andrzej Siewior cn->strings.s = cn->configuration; 139188af8bbeSSebastian Andrzej Siewior i++; 139288af8bbeSSebastian Andrzej Siewior } 139388af8bbeSSebastian Andrzej Siewior cfg->gstrings[i] = NULL; 139488af8bbeSSebastian Andrzej Siewior s = usb_gstrings_attach(&gi->cdev, cfg->gstrings, 1); 1395fea77077SWei Yongjun if (IS_ERR(s)) { 1396fea77077SWei Yongjun ret = PTR_ERR(s); 139788af8bbeSSebastian Andrzej Siewior goto err_comp_cleanup; 1398fea77077SWei Yongjun } 139988af8bbeSSebastian Andrzej Siewior c->iConfiguration = s[0].id; 140088af8bbeSSebastian Andrzej Siewior } 140188af8bbeSSebastian Andrzej Siewior 140288af8bbeSSebastian Andrzej Siewior list_for_each_entry_safe(f, tmp, &cfg->func_list, list) { 140388af8bbeSSebastian Andrzej Siewior list_del(&f->list); 140488af8bbeSSebastian Andrzej Siewior ret = usb_add_function(c, f); 14055a68e9b5SAndrzej Pietrasiewicz if (ret) { 14065a68e9b5SAndrzej Pietrasiewicz list_add(&f->list, &cfg->func_list); 140788af8bbeSSebastian Andrzej Siewior goto err_purge_funcs; 140888af8bbeSSebastian Andrzej Siewior } 14095a68e9b5SAndrzej Pietrasiewicz } 14107adf9e3aSWesley Cheng ret = usb_gadget_check_config(cdev->gadget); 14117adf9e3aSWesley Cheng if (ret) 14127adf9e3aSWesley Cheng goto err_purge_funcs; 14137adf9e3aSWesley Cheng 141488af8bbeSSebastian Andrzej Siewior usb_ep_autoconfig_reset(cdev->gadget); 141588af8bbeSSebastian Andrzej Siewior } 1416da424314SAndrzej Pietrasiewicz if (cdev->use_os_string) { 1417da424314SAndrzej Pietrasiewicz ret = composite_os_desc_req_prepare(cdev, gadget->ep0); 1418da424314SAndrzej Pietrasiewicz if (ret) 1419da424314SAndrzej Pietrasiewicz goto err_purge_funcs; 1420da424314SAndrzej Pietrasiewicz } 1421da424314SAndrzej Pietrasiewicz 142288af8bbeSSebastian Andrzej Siewior usb_ep_autoconfig_reset(cdev->gadget); 142388af8bbeSSebastian Andrzej Siewior return 0; 142488af8bbeSSebastian Andrzej Siewior 142588af8bbeSSebastian Andrzej Siewior err_purge_funcs: 142688af8bbeSSebastian Andrzej Siewior purge_configs_funcs(gi); 142788af8bbeSSebastian Andrzej Siewior err_comp_cleanup: 142888af8bbeSSebastian Andrzej Siewior composite_dev_cleanup(cdev); 142988af8bbeSSebastian Andrzej Siewior return ret; 143088af8bbeSSebastian Andrzej Siewior } 143188af8bbeSSebastian Andrzej Siewior 143288af8bbeSSebastian Andrzej Siewior static void configfs_composite_unbind(struct usb_gadget *gadget) 143388af8bbeSSebastian Andrzej Siewior { 143488af8bbeSSebastian Andrzej Siewior struct usb_composite_dev *cdev; 143588af8bbeSSebastian Andrzej Siewior struct gadget_info *gi; 14361a1c851bSPeter Chen unsigned long flags; 143788af8bbeSSebastian Andrzej Siewior 143888af8bbeSSebastian Andrzej Siewior /* the gi->lock is hold by the caller */ 143988af8bbeSSebastian Andrzej Siewior 144088af8bbeSSebastian Andrzej Siewior cdev = get_gadget_data(gadget); 144188af8bbeSSebastian Andrzej Siewior gi = container_of(cdev, struct gadget_info, cdev); 14421a1c851bSPeter Chen spin_lock_irqsave(&gi->spinlock, flags); 14431a1c851bSPeter Chen gi->unbind = 1; 14441a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 144588af8bbeSSebastian Andrzej Siewior 144641ce84c8SLi Jun kfree(otg_desc[0]); 144741ce84c8SLi Jun otg_desc[0] = NULL; 144888af8bbeSSebastian Andrzej Siewior purge_configs_funcs(gi); 144988af8bbeSSebastian Andrzej Siewior composite_dev_cleanup(cdev); 145088af8bbeSSebastian Andrzej Siewior usb_ep_autoconfig_reset(cdev->gadget); 14511a1c851bSPeter Chen spin_lock_irqsave(&gi->spinlock, flags); 145288af8bbeSSebastian Andrzej Siewior cdev->gadget = NULL; 145388af8bbeSSebastian Andrzej Siewior set_gadget_data(gadget, NULL); 14541a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 14551a1c851bSPeter Chen } 14561a1c851bSPeter Chen 14571a1c851bSPeter Chen static int configfs_composite_setup(struct usb_gadget *gadget, 14581a1c851bSPeter Chen const struct usb_ctrlrequest *ctrl) 14591a1c851bSPeter Chen { 14601a1c851bSPeter Chen struct usb_composite_dev *cdev; 14611a1c851bSPeter Chen struct gadget_info *gi; 14621a1c851bSPeter Chen unsigned long flags; 14631a1c851bSPeter Chen int ret; 14641a1c851bSPeter Chen 14651a1c851bSPeter Chen cdev = get_gadget_data(gadget); 14661a1c851bSPeter Chen if (!cdev) 14671a1c851bSPeter Chen return 0; 14681a1c851bSPeter Chen 14691a1c851bSPeter Chen gi = container_of(cdev, struct gadget_info, cdev); 14701a1c851bSPeter Chen spin_lock_irqsave(&gi->spinlock, flags); 14711a1c851bSPeter Chen cdev = get_gadget_data(gadget); 14721a1c851bSPeter Chen if (!cdev || gi->unbind) { 14731a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 14741a1c851bSPeter Chen return 0; 14751a1c851bSPeter Chen } 14761a1c851bSPeter Chen 14771a1c851bSPeter Chen ret = composite_setup(gadget, ctrl); 14781a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 14791a1c851bSPeter Chen return ret; 14801a1c851bSPeter Chen } 14811a1c851bSPeter Chen 14821a1c851bSPeter Chen static void configfs_composite_disconnect(struct usb_gadget *gadget) 14831a1c851bSPeter Chen { 14841a1c851bSPeter Chen struct usb_composite_dev *cdev; 14851a1c851bSPeter Chen struct gadget_info *gi; 14861a1c851bSPeter Chen unsigned long flags; 14871a1c851bSPeter Chen 14881a1c851bSPeter Chen cdev = get_gadget_data(gadget); 14891a1c851bSPeter Chen if (!cdev) 14901a1c851bSPeter Chen return; 14911a1c851bSPeter Chen 14921a1c851bSPeter Chen gi = container_of(cdev, struct gadget_info, cdev); 14931a1c851bSPeter Chen spin_lock_irqsave(&gi->spinlock, flags); 14941a1c851bSPeter Chen cdev = get_gadget_data(gadget); 14951a1c851bSPeter Chen if (!cdev || gi->unbind) { 14961a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 14971a1c851bSPeter Chen return; 14981a1c851bSPeter Chen } 14991a1c851bSPeter Chen 15001a1c851bSPeter Chen composite_disconnect(gadget); 15011a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 15021a1c851bSPeter Chen } 15031a1c851bSPeter Chen 15044d7aae9fSWesley Cheng static void configfs_composite_reset(struct usb_gadget *gadget) 15054d7aae9fSWesley Cheng { 15064d7aae9fSWesley Cheng struct usb_composite_dev *cdev; 15074d7aae9fSWesley Cheng struct gadget_info *gi; 15084d7aae9fSWesley Cheng unsigned long flags; 15094d7aae9fSWesley Cheng 15104d7aae9fSWesley Cheng cdev = get_gadget_data(gadget); 15114d7aae9fSWesley Cheng if (!cdev) 15124d7aae9fSWesley Cheng return; 15134d7aae9fSWesley Cheng 15144d7aae9fSWesley Cheng gi = container_of(cdev, struct gadget_info, cdev); 15154d7aae9fSWesley Cheng spin_lock_irqsave(&gi->spinlock, flags); 15164d7aae9fSWesley Cheng cdev = get_gadget_data(gadget); 15174d7aae9fSWesley Cheng if (!cdev || gi->unbind) { 15184d7aae9fSWesley Cheng spin_unlock_irqrestore(&gi->spinlock, flags); 15194d7aae9fSWesley Cheng return; 15204d7aae9fSWesley Cheng } 15214d7aae9fSWesley Cheng 15224d7aae9fSWesley Cheng composite_reset(gadget); 15234d7aae9fSWesley Cheng spin_unlock_irqrestore(&gi->spinlock, flags); 15244d7aae9fSWesley Cheng } 15254d7aae9fSWesley Cheng 15261a1c851bSPeter Chen static void configfs_composite_suspend(struct usb_gadget *gadget) 15271a1c851bSPeter Chen { 15281a1c851bSPeter Chen struct usb_composite_dev *cdev; 15291a1c851bSPeter Chen struct gadget_info *gi; 15301a1c851bSPeter Chen unsigned long flags; 15311a1c851bSPeter Chen 15321a1c851bSPeter Chen cdev = get_gadget_data(gadget); 15331a1c851bSPeter Chen if (!cdev) 15341a1c851bSPeter Chen return; 15351a1c851bSPeter Chen 15361a1c851bSPeter Chen gi = container_of(cdev, struct gadget_info, cdev); 15371a1c851bSPeter Chen spin_lock_irqsave(&gi->spinlock, flags); 15381a1c851bSPeter Chen cdev = get_gadget_data(gadget); 15391a1c851bSPeter Chen if (!cdev || gi->unbind) { 15401a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 15411a1c851bSPeter Chen return; 15421a1c851bSPeter Chen } 15431a1c851bSPeter Chen 15441a1c851bSPeter Chen composite_suspend(gadget); 15451a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 15461a1c851bSPeter Chen } 15471a1c851bSPeter Chen 15481a1c851bSPeter Chen static void configfs_composite_resume(struct usb_gadget *gadget) 15491a1c851bSPeter Chen { 15501a1c851bSPeter Chen struct usb_composite_dev *cdev; 15511a1c851bSPeter Chen struct gadget_info *gi; 15521a1c851bSPeter Chen unsigned long flags; 15531a1c851bSPeter Chen 15541a1c851bSPeter Chen cdev = get_gadget_data(gadget); 15551a1c851bSPeter Chen if (!cdev) 15561a1c851bSPeter Chen return; 15571a1c851bSPeter Chen 15581a1c851bSPeter Chen gi = container_of(cdev, struct gadget_info, cdev); 15591a1c851bSPeter Chen spin_lock_irqsave(&gi->spinlock, flags); 15601a1c851bSPeter Chen cdev = get_gadget_data(gadget); 15611a1c851bSPeter Chen if (!cdev || gi->unbind) { 15621a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 15631a1c851bSPeter Chen return; 15641a1c851bSPeter Chen } 15651a1c851bSPeter Chen 15661a1c851bSPeter Chen composite_resume(gadget); 15671a1c851bSPeter Chen spin_unlock_irqrestore(&gi->spinlock, flags); 156888af8bbeSSebastian Andrzej Siewior } 156988af8bbeSSebastian Andrzej Siewior 157088af8bbeSSebastian Andrzej Siewior static const struct usb_gadget_driver configfs_driver_template = { 157188af8bbeSSebastian Andrzej Siewior .bind = configfs_composite_bind, 157288af8bbeSSebastian Andrzej Siewior .unbind = configfs_composite_unbind, 157388af8bbeSSebastian Andrzej Siewior 15741a1c851bSPeter Chen .setup = configfs_composite_setup, 15754d7aae9fSWesley Cheng .reset = configfs_composite_reset, 15761a1c851bSPeter Chen .disconnect = configfs_composite_disconnect, 157788af8bbeSSebastian Andrzej Siewior 15781a1c851bSPeter Chen .suspend = configfs_composite_suspend, 15791a1c851bSPeter Chen .resume = configfs_composite_resume, 15803a571870SAndrzej Pietrasiewicz 1581e2459108Staehyun.cho .max_speed = USB_SPEED_SUPER_PLUS, 158288af8bbeSSebastian Andrzej Siewior .driver = { 158388af8bbeSSebastian Andrzej Siewior .owner = THIS_MODULE, 158488af8bbeSSebastian Andrzej Siewior .name = "configfs-gadget", 158588af8bbeSSebastian Andrzej Siewior }, 1586f1bddbb3SKrzysztof Opasiak .match_existing_only = 1, 158788af8bbeSSebastian Andrzej Siewior }; 158888af8bbeSSebastian Andrzej Siewior 158988af8bbeSSebastian Andrzej Siewior static struct config_group *gadgets_make( 159088af8bbeSSebastian Andrzej Siewior struct config_group *group, 159188af8bbeSSebastian Andrzej Siewior const char *name) 159288af8bbeSSebastian Andrzej Siewior { 159388af8bbeSSebastian Andrzej Siewior struct gadget_info *gi; 159488af8bbeSSebastian Andrzej Siewior 159588af8bbeSSebastian Andrzej Siewior gi = kzalloc(sizeof(*gi), GFP_KERNEL); 159688af8bbeSSebastian Andrzej Siewior if (!gi) 159788af8bbeSSebastian Andrzej Siewior return ERR_PTR(-ENOMEM); 159888af8bbeSSebastian Andrzej Siewior 15991ae1602dSChristoph Hellwig config_group_init_type_name(&gi->group, name, &gadget_root_type); 160088af8bbeSSebastian Andrzej Siewior 160188af8bbeSSebastian Andrzej Siewior config_group_init_type_name(&gi->functions_group, "functions", 160288af8bbeSSebastian Andrzej Siewior &functions_type); 16031ae1602dSChristoph Hellwig configfs_add_default_group(&gi->functions_group, &gi->group); 16041ae1602dSChristoph Hellwig 160588af8bbeSSebastian Andrzej Siewior config_group_init_type_name(&gi->configs_group, "configs", 160688af8bbeSSebastian Andrzej Siewior &config_desc_type); 16071ae1602dSChristoph Hellwig configfs_add_default_group(&gi->configs_group, &gi->group); 16081ae1602dSChristoph Hellwig 160988af8bbeSSebastian Andrzej Siewior config_group_init_type_name(&gi->strings_group, "strings", 161088af8bbeSSebastian Andrzej Siewior &gadget_strings_strings_type); 16111ae1602dSChristoph Hellwig configfs_add_default_group(&gi->strings_group, &gi->group); 16121ae1602dSChristoph Hellwig 161387213d38SAndrzej Pietrasiewicz config_group_init_type_name(&gi->os_desc_group, "os_desc", 161487213d38SAndrzej Pietrasiewicz &os_desc_type); 16151ae1602dSChristoph Hellwig configfs_add_default_group(&gi->os_desc_group, &gi->group); 161688af8bbeSSebastian Andrzej Siewior 161788af8bbeSSebastian Andrzej Siewior gi->composite.bind = configfs_do_nothing; 161888af8bbeSSebastian Andrzej Siewior gi->composite.unbind = configfs_do_nothing; 161988af8bbeSSebastian Andrzej Siewior gi->composite.suspend = NULL; 162088af8bbeSSebastian Andrzej Siewior gi->composite.resume = NULL; 1621e2459108Staehyun.cho gi->composite.max_speed = USB_SPEED_SUPER_PLUS; 162288af8bbeSSebastian Andrzej Siewior 1623093edc2bSWei Yongjun spin_lock_init(&gi->spinlock); 162488af8bbeSSebastian Andrzej Siewior mutex_init(&gi->lock); 162588af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&gi->string_list); 162688af8bbeSSebastian Andrzej Siewior INIT_LIST_HEAD(&gi->available_func); 162788af8bbeSSebastian Andrzej Siewior 162888af8bbeSSebastian Andrzej Siewior composite_init_dev(&gi->cdev); 162988af8bbeSSebastian Andrzej Siewior gi->cdev.desc.bLength = USB_DT_DEVICE_SIZE; 163088af8bbeSSebastian Andrzej Siewior gi->cdev.desc.bDescriptorType = USB_DT_DEVICE; 163188af8bbeSSebastian Andrzej Siewior gi->cdev.desc.bcdDevice = cpu_to_le16(get_default_bcdDevice()); 163288af8bbeSSebastian Andrzej Siewior 163388af8bbeSSebastian Andrzej Siewior gi->composite.gadget_driver = configfs_driver_template; 163488af8bbeSSebastian Andrzej Siewior 163588af8bbeSSebastian Andrzej Siewior gi->composite.gadget_driver.function = kstrdup(name, GFP_KERNEL); 163688af8bbeSSebastian Andrzej Siewior gi->composite.name = gi->composite.gadget_driver.function; 163788af8bbeSSebastian Andrzej Siewior 163888af8bbeSSebastian Andrzej Siewior if (!gi->composite.gadget_driver.function) 163988af8bbeSSebastian Andrzej Siewior goto err; 164088af8bbeSSebastian Andrzej Siewior 164188af8bbeSSebastian Andrzej Siewior return &gi->group; 164288af8bbeSSebastian Andrzej Siewior err: 164388af8bbeSSebastian Andrzej Siewior kfree(gi); 164488af8bbeSSebastian Andrzej Siewior return ERR_PTR(-ENOMEM); 164588af8bbeSSebastian Andrzej Siewior } 164688af8bbeSSebastian Andrzej Siewior 164788af8bbeSSebastian Andrzej Siewior static void gadgets_drop(struct config_group *group, struct config_item *item) 164888af8bbeSSebastian Andrzej Siewior { 164988af8bbeSSebastian Andrzej Siewior config_item_put(item); 165088af8bbeSSebastian Andrzej Siewior } 165188af8bbeSSebastian Andrzej Siewior 165288af8bbeSSebastian Andrzej Siewior static struct configfs_group_operations gadgets_ops = { 165388af8bbeSSebastian Andrzej Siewior .make_group = &gadgets_make, 165488af8bbeSSebastian Andrzej Siewior .drop_item = &gadgets_drop, 165588af8bbeSSebastian Andrzej Siewior }; 165688af8bbeSSebastian Andrzej Siewior 16574ad01412SBhumika Goyal static const struct config_item_type gadgets_type = { 165888af8bbeSSebastian Andrzej Siewior .ct_group_ops = &gadgets_ops, 165988af8bbeSSebastian Andrzej Siewior .ct_owner = THIS_MODULE, 166088af8bbeSSebastian Andrzej Siewior }; 166188af8bbeSSebastian Andrzej Siewior 166288af8bbeSSebastian Andrzej Siewior static struct configfs_subsystem gadget_subsys = { 166388af8bbeSSebastian Andrzej Siewior .su_group = { 166488af8bbeSSebastian Andrzej Siewior .cg_item = { 166588af8bbeSSebastian Andrzej Siewior .ci_namebuf = "usb_gadget", 166688af8bbeSSebastian Andrzej Siewior .ci_type = &gadgets_type, 166788af8bbeSSebastian Andrzej Siewior }, 166888af8bbeSSebastian Andrzej Siewior }, 166988af8bbeSSebastian Andrzej Siewior .su_mutex = __MUTEX_INITIALIZER(gadget_subsys.su_mutex), 167088af8bbeSSebastian Andrzej Siewior }; 167188af8bbeSSebastian Andrzej Siewior 1672092a4bd0SAndrzej Pietrasiewicz void unregister_gadget_item(struct config_item *item) 1673092a4bd0SAndrzej Pietrasiewicz { 1674092a4bd0SAndrzej Pietrasiewicz struct gadget_info *gi = to_gadget_info(item); 1675092a4bd0SAndrzej Pietrasiewicz 1676cee51c33SWinter Wang mutex_lock(&gi->lock); 1677092a4bd0SAndrzej Pietrasiewicz unregister_gadget(gi); 1678cee51c33SWinter Wang mutex_unlock(&gi->lock); 1679092a4bd0SAndrzej Pietrasiewicz } 16800700faafSFelipe Balbi EXPORT_SYMBOL_GPL(unregister_gadget_item); 1681092a4bd0SAndrzej Pietrasiewicz 168288af8bbeSSebastian Andrzej Siewior static int __init gadget_cfs_init(void) 168388af8bbeSSebastian Andrzej Siewior { 168488af8bbeSSebastian Andrzej Siewior int ret; 168588af8bbeSSebastian Andrzej Siewior 168688af8bbeSSebastian Andrzej Siewior config_group_init(&gadget_subsys.su_group); 168788af8bbeSSebastian Andrzej Siewior 168888af8bbeSSebastian Andrzej Siewior ret = configfs_register_subsystem(&gadget_subsys); 168988af8bbeSSebastian Andrzej Siewior return ret; 169088af8bbeSSebastian Andrzej Siewior } 169188af8bbeSSebastian Andrzej Siewior module_init(gadget_cfs_init); 169288af8bbeSSebastian Andrzej Siewior 169388af8bbeSSebastian Andrzej Siewior static void __exit gadget_cfs_exit(void) 169488af8bbeSSebastian Andrzej Siewior { 169588af8bbeSSebastian Andrzej Siewior configfs_unregister_subsystem(&gadget_subsys); 169688af8bbeSSebastian Andrzej Siewior } 169788af8bbeSSebastian Andrzej Siewior module_exit(gadget_cfs_exit); 1698