18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/configfs.h> 38c2ecf20Sopenharmony_ci#include <linux/module.h> 48c2ecf20Sopenharmony_ci#include <linux/slab.h> 58c2ecf20Sopenharmony_ci#include <linux/device.h> 68c2ecf20Sopenharmony_ci#include <linux/nls.h> 78c2ecf20Sopenharmony_ci#include <linux/usb/composite.h> 88c2ecf20Sopenharmony_ci#include <linux/usb/gadget_configfs.h> 98c2ecf20Sopenharmony_ci#include "configfs.h" 108c2ecf20Sopenharmony_ci#include "u_f.h" 118c2ecf20Sopenharmony_ci#include "u_os_desc.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ciint check_user_usb_string(const char *name, 148c2ecf20Sopenharmony_ci struct usb_gadget_strings *stringtab_dev) 158c2ecf20Sopenharmony_ci{ 168c2ecf20Sopenharmony_ci u16 num; 178c2ecf20Sopenharmony_ci int ret; 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci ret = kstrtou16(name, 0, &num); 208c2ecf20Sopenharmony_ci if (ret) 218c2ecf20Sopenharmony_ci return ret; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci if (!usb_validate_langid(num)) 248c2ecf20Sopenharmony_ci return -EINVAL; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci stringtab_dev->language = num; 278c2ecf20Sopenharmony_ci return 0; 288c2ecf20Sopenharmony_ci} 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define MAX_NAME_LEN 40 318c2ecf20Sopenharmony_ci#define MAX_USB_STRING_LANGS 2 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic const struct usb_descriptor_header *otg_desc[2]; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistruct gadget_info { 368c2ecf20Sopenharmony_ci struct config_group group; 378c2ecf20Sopenharmony_ci struct config_group functions_group; 388c2ecf20Sopenharmony_ci struct config_group configs_group; 398c2ecf20Sopenharmony_ci struct config_group strings_group; 408c2ecf20Sopenharmony_ci struct config_group os_desc_group; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci struct mutex lock; 438c2ecf20Sopenharmony_ci struct usb_gadget_strings *gstrings[MAX_USB_STRING_LANGS + 1]; 448c2ecf20Sopenharmony_ci struct list_head string_list; 458c2ecf20Sopenharmony_ci struct list_head available_func; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci struct usb_composite_driver composite; 488c2ecf20Sopenharmony_ci struct usb_composite_dev cdev; 498c2ecf20Sopenharmony_ci bool use_os_desc; 508c2ecf20Sopenharmony_ci char b_vendor_code; 518c2ecf20Sopenharmony_ci char qw_sign[OS_STRING_QW_SIGN_LEN]; 528c2ecf20Sopenharmony_ci spinlock_t spinlock; 538c2ecf20Sopenharmony_ci bool unbind; 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic inline struct gadget_info *to_gadget_info(struct config_item *item) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci return container_of(to_config_group(item), struct gadget_info, group); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistruct config_usb_cfg { 628c2ecf20Sopenharmony_ci struct config_group group; 638c2ecf20Sopenharmony_ci struct config_group strings_group; 648c2ecf20Sopenharmony_ci struct list_head string_list; 658c2ecf20Sopenharmony_ci struct usb_configuration c; 668c2ecf20Sopenharmony_ci struct list_head func_list; 678c2ecf20Sopenharmony_ci struct usb_gadget_strings *gstrings[MAX_USB_STRING_LANGS + 1]; 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic inline struct config_usb_cfg *to_config_usb_cfg(struct config_item *item) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci return container_of(to_config_group(item), struct config_usb_cfg, 738c2ecf20Sopenharmony_ci group); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistruct gadget_strings { 778c2ecf20Sopenharmony_ci struct usb_gadget_strings stringtab_dev; 788c2ecf20Sopenharmony_ci struct usb_string strings[USB_GADGET_FIRST_AVAIL_IDX]; 798c2ecf20Sopenharmony_ci char *manufacturer; 808c2ecf20Sopenharmony_ci char *product; 818c2ecf20Sopenharmony_ci char *serialnumber; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci struct config_group group; 848c2ecf20Sopenharmony_ci struct list_head list; 858c2ecf20Sopenharmony_ci}; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistruct os_desc { 888c2ecf20Sopenharmony_ci struct config_group group; 898c2ecf20Sopenharmony_ci}; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistruct gadget_config_name { 928c2ecf20Sopenharmony_ci struct usb_gadget_strings stringtab_dev; 938c2ecf20Sopenharmony_ci struct usb_string strings; 948c2ecf20Sopenharmony_ci char *configuration; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci struct config_group group; 978c2ecf20Sopenharmony_ci struct list_head list; 988c2ecf20Sopenharmony_ci}; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci#define USB_MAX_STRING_WITH_NULL_LEN (USB_MAX_STRING_LEN+1) 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic int usb_string_copy(const char *s, char **s_copy) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci int ret; 1058c2ecf20Sopenharmony_ci char *str; 1068c2ecf20Sopenharmony_ci char *copy = *s_copy; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci ret = strlen(s); 1098c2ecf20Sopenharmony_ci if (ret > USB_MAX_STRING_LEN) 1108c2ecf20Sopenharmony_ci return -EOVERFLOW; 1118c2ecf20Sopenharmony_ci if (ret < 1) 1128c2ecf20Sopenharmony_ci return -EINVAL; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (copy) { 1158c2ecf20Sopenharmony_ci str = copy; 1168c2ecf20Sopenharmony_ci } else { 1178c2ecf20Sopenharmony_ci str = kmalloc(USB_MAX_STRING_WITH_NULL_LEN, GFP_KERNEL); 1188c2ecf20Sopenharmony_ci if (!str) 1198c2ecf20Sopenharmony_ci return -ENOMEM; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci strcpy(str, s); 1228c2ecf20Sopenharmony_ci if (str[ret - 1] == '\n') 1238c2ecf20Sopenharmony_ci str[ret - 1] = '\0'; 1248c2ecf20Sopenharmony_ci *s_copy = str; 1258c2ecf20Sopenharmony_ci return 0; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci#define GI_DEVICE_DESC_SIMPLE_R_u8(__name) \ 1298c2ecf20Sopenharmony_cistatic ssize_t gadget_dev_desc_##__name##_show(struct config_item *item, \ 1308c2ecf20Sopenharmony_ci char *page) \ 1318c2ecf20Sopenharmony_ci{ \ 1328c2ecf20Sopenharmony_ci return sprintf(page, "0x%02x\n", \ 1338c2ecf20Sopenharmony_ci to_gadget_info(item)->cdev.desc.__name); \ 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci#define GI_DEVICE_DESC_SIMPLE_R_u16(__name) \ 1378c2ecf20Sopenharmony_cistatic ssize_t gadget_dev_desc_##__name##_show(struct config_item *item, \ 1388c2ecf20Sopenharmony_ci char *page) \ 1398c2ecf20Sopenharmony_ci{ \ 1408c2ecf20Sopenharmony_ci return sprintf(page, "0x%04x\n", \ 1418c2ecf20Sopenharmony_ci le16_to_cpup(&to_gadget_info(item)->cdev.desc.__name)); \ 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci#define GI_DEVICE_DESC_SIMPLE_W_u8(_name) \ 1468c2ecf20Sopenharmony_cistatic ssize_t gadget_dev_desc_##_name##_store(struct config_item *item, \ 1478c2ecf20Sopenharmony_ci const char *page, size_t len) \ 1488c2ecf20Sopenharmony_ci{ \ 1498c2ecf20Sopenharmony_ci u8 val; \ 1508c2ecf20Sopenharmony_ci int ret; \ 1518c2ecf20Sopenharmony_ci ret = kstrtou8(page, 0, &val); \ 1528c2ecf20Sopenharmony_ci if (ret) \ 1538c2ecf20Sopenharmony_ci return ret; \ 1548c2ecf20Sopenharmony_ci to_gadget_info(item)->cdev.desc._name = val; \ 1558c2ecf20Sopenharmony_ci return len; \ 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci#define GI_DEVICE_DESC_SIMPLE_W_u16(_name) \ 1598c2ecf20Sopenharmony_cistatic ssize_t gadget_dev_desc_##_name##_store(struct config_item *item, \ 1608c2ecf20Sopenharmony_ci const char *page, size_t len) \ 1618c2ecf20Sopenharmony_ci{ \ 1628c2ecf20Sopenharmony_ci u16 val; \ 1638c2ecf20Sopenharmony_ci int ret; \ 1648c2ecf20Sopenharmony_ci ret = kstrtou16(page, 0, &val); \ 1658c2ecf20Sopenharmony_ci if (ret) \ 1668c2ecf20Sopenharmony_ci return ret; \ 1678c2ecf20Sopenharmony_ci to_gadget_info(item)->cdev.desc._name = cpu_to_le16p(&val); \ 1688c2ecf20Sopenharmony_ci return len; \ 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci#define GI_DEVICE_DESC_SIMPLE_RW(_name, _type) \ 1728c2ecf20Sopenharmony_ci GI_DEVICE_DESC_SIMPLE_R_##_type(_name) \ 1738c2ecf20Sopenharmony_ci GI_DEVICE_DESC_SIMPLE_W_##_type(_name) 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ciGI_DEVICE_DESC_SIMPLE_R_u16(bcdUSB); 1768c2ecf20Sopenharmony_ciGI_DEVICE_DESC_SIMPLE_RW(bDeviceClass, u8); 1778c2ecf20Sopenharmony_ciGI_DEVICE_DESC_SIMPLE_RW(bDeviceSubClass, u8); 1788c2ecf20Sopenharmony_ciGI_DEVICE_DESC_SIMPLE_RW(bDeviceProtocol, u8); 1798c2ecf20Sopenharmony_ciGI_DEVICE_DESC_SIMPLE_RW(bMaxPacketSize0, u8); 1808c2ecf20Sopenharmony_ciGI_DEVICE_DESC_SIMPLE_RW(idVendor, u16); 1818c2ecf20Sopenharmony_ciGI_DEVICE_DESC_SIMPLE_RW(idProduct, u16); 1828c2ecf20Sopenharmony_ciGI_DEVICE_DESC_SIMPLE_R_u16(bcdDevice); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic ssize_t is_valid_bcd(u16 bcd_val) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci if ((bcd_val & 0xf) > 9) 1878c2ecf20Sopenharmony_ci return -EINVAL; 1888c2ecf20Sopenharmony_ci if (((bcd_val >> 4) & 0xf) > 9) 1898c2ecf20Sopenharmony_ci return -EINVAL; 1908c2ecf20Sopenharmony_ci if (((bcd_val >> 8) & 0xf) > 9) 1918c2ecf20Sopenharmony_ci return -EINVAL; 1928c2ecf20Sopenharmony_ci if (((bcd_val >> 12) & 0xf) > 9) 1938c2ecf20Sopenharmony_ci return -EINVAL; 1948c2ecf20Sopenharmony_ci return 0; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic ssize_t gadget_dev_desc_bcdDevice_store(struct config_item *item, 1988c2ecf20Sopenharmony_ci const char *page, size_t len) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci u16 bcdDevice; 2018c2ecf20Sopenharmony_ci int ret; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci ret = kstrtou16(page, 0, &bcdDevice); 2048c2ecf20Sopenharmony_ci if (ret) 2058c2ecf20Sopenharmony_ci return ret; 2068c2ecf20Sopenharmony_ci ret = is_valid_bcd(bcdDevice); 2078c2ecf20Sopenharmony_ci if (ret) 2088c2ecf20Sopenharmony_ci return ret; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci to_gadget_info(item)->cdev.desc.bcdDevice = cpu_to_le16(bcdDevice); 2118c2ecf20Sopenharmony_ci return len; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic ssize_t gadget_dev_desc_bcdUSB_store(struct config_item *item, 2158c2ecf20Sopenharmony_ci const char *page, size_t len) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci u16 bcdUSB; 2188c2ecf20Sopenharmony_ci int ret; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci ret = kstrtou16(page, 0, &bcdUSB); 2218c2ecf20Sopenharmony_ci if (ret) 2228c2ecf20Sopenharmony_ci return ret; 2238c2ecf20Sopenharmony_ci ret = is_valid_bcd(bcdUSB); 2248c2ecf20Sopenharmony_ci if (ret) 2258c2ecf20Sopenharmony_ci return ret; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci to_gadget_info(item)->cdev.desc.bcdUSB = cpu_to_le16(bcdUSB); 2288c2ecf20Sopenharmony_ci return len; 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic ssize_t gadget_dev_desc_UDC_show(struct config_item *item, char *page) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct gadget_info *gi = to_gadget_info(item); 2348c2ecf20Sopenharmony_ci char *udc_name; 2358c2ecf20Sopenharmony_ci int ret; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci mutex_lock(&gi->lock); 2388c2ecf20Sopenharmony_ci udc_name = gi->composite.gadget_driver.udc_name; 2398c2ecf20Sopenharmony_ci ret = sprintf(page, "%s\n", udc_name ?: ""); 2408c2ecf20Sopenharmony_ci mutex_unlock(&gi->lock); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci return ret; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int unregister_gadget(struct gadget_info *gi) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci int ret; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (!gi->composite.gadget_driver.udc_name) 2508c2ecf20Sopenharmony_ci return -ENODEV; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci ret = usb_gadget_unregister_driver(&gi->composite.gadget_driver); 2538c2ecf20Sopenharmony_ci if (ret) 2548c2ecf20Sopenharmony_ci return ret; 2558c2ecf20Sopenharmony_ci kfree(gi->composite.gadget_driver.udc_name); 2568c2ecf20Sopenharmony_ci gi->composite.gadget_driver.udc_name = NULL; 2578c2ecf20Sopenharmony_ci return 0; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic ssize_t gadget_dev_desc_UDC_store(struct config_item *item, 2618c2ecf20Sopenharmony_ci const char *page, size_t len) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci struct gadget_info *gi = to_gadget_info(item); 2648c2ecf20Sopenharmony_ci char *name; 2658c2ecf20Sopenharmony_ci int ret; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci if (strlen(page) < len) 2688c2ecf20Sopenharmony_ci return -EOVERFLOW; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci name = kstrdup(page, GFP_KERNEL); 2718c2ecf20Sopenharmony_ci if (!name) 2728c2ecf20Sopenharmony_ci return -ENOMEM; 2738c2ecf20Sopenharmony_ci if (name[len - 1] == '\n') 2748c2ecf20Sopenharmony_ci name[len - 1] = '\0'; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci mutex_lock(&gi->lock); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (!strlen(name)) { 2798c2ecf20Sopenharmony_ci ret = unregister_gadget(gi); 2808c2ecf20Sopenharmony_ci if (ret) 2818c2ecf20Sopenharmony_ci goto err; 2828c2ecf20Sopenharmony_ci kfree(name); 2838c2ecf20Sopenharmony_ci } else { 2848c2ecf20Sopenharmony_ci if (gi->composite.gadget_driver.udc_name) { 2858c2ecf20Sopenharmony_ci ret = -EBUSY; 2868c2ecf20Sopenharmony_ci goto err; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci gi->composite.gadget_driver.udc_name = name; 2898c2ecf20Sopenharmony_ci ret = usb_gadget_probe_driver(&gi->composite.gadget_driver); 2908c2ecf20Sopenharmony_ci if (ret) { 2918c2ecf20Sopenharmony_ci gi->composite.gadget_driver.udc_name = NULL; 2928c2ecf20Sopenharmony_ci goto err; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci mutex_unlock(&gi->lock); 2968c2ecf20Sopenharmony_ci return len; 2978c2ecf20Sopenharmony_cierr: 2988c2ecf20Sopenharmony_ci kfree(name); 2998c2ecf20Sopenharmony_ci mutex_unlock(&gi->lock); 3008c2ecf20Sopenharmony_ci return ret; 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic ssize_t gadget_dev_desc_max_speed_show(struct config_item *item, 3048c2ecf20Sopenharmony_ci char *page) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci enum usb_device_speed speed = to_gadget_info(item)->composite.max_speed; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci return sprintf(page, "%s\n", usb_speed_string(speed)); 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic ssize_t gadget_dev_desc_max_speed_store(struct config_item *item, 3128c2ecf20Sopenharmony_ci const char *page, size_t len) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci struct gadget_info *gi = to_gadget_info(item); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci mutex_lock(&gi->lock); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* Prevent changing of max_speed after the driver is binded */ 3198c2ecf20Sopenharmony_ci if (gi->composite.gadget_driver.udc_name) 3208c2ecf20Sopenharmony_ci goto err; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (strncmp(page, "super-speed-plus", 16) == 0) 3238c2ecf20Sopenharmony_ci gi->composite.max_speed = USB_SPEED_SUPER_PLUS; 3248c2ecf20Sopenharmony_ci else if (strncmp(page, "super-speed", 11) == 0) 3258c2ecf20Sopenharmony_ci gi->composite.max_speed = USB_SPEED_SUPER; 3268c2ecf20Sopenharmony_ci else if (strncmp(page, "high-speed", 10) == 0) 3278c2ecf20Sopenharmony_ci gi->composite.max_speed = USB_SPEED_HIGH; 3288c2ecf20Sopenharmony_ci else if (strncmp(page, "full-speed", 10) == 0) 3298c2ecf20Sopenharmony_ci gi->composite.max_speed = USB_SPEED_FULL; 3308c2ecf20Sopenharmony_ci else if (strncmp(page, "low-speed", 9) == 0) 3318c2ecf20Sopenharmony_ci gi->composite.max_speed = USB_SPEED_LOW; 3328c2ecf20Sopenharmony_ci else 3338c2ecf20Sopenharmony_ci goto err; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci gi->composite.gadget_driver.max_speed = gi->composite.max_speed; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci mutex_unlock(&gi->lock); 3388c2ecf20Sopenharmony_ci return len; 3398c2ecf20Sopenharmony_cierr: 3408c2ecf20Sopenharmony_ci mutex_unlock(&gi->lock); 3418c2ecf20Sopenharmony_ci return -EINVAL; 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ciCONFIGFS_ATTR(gadget_dev_desc_, bDeviceClass); 3458c2ecf20Sopenharmony_ciCONFIGFS_ATTR(gadget_dev_desc_, bDeviceSubClass); 3468c2ecf20Sopenharmony_ciCONFIGFS_ATTR(gadget_dev_desc_, bDeviceProtocol); 3478c2ecf20Sopenharmony_ciCONFIGFS_ATTR(gadget_dev_desc_, bMaxPacketSize0); 3488c2ecf20Sopenharmony_ciCONFIGFS_ATTR(gadget_dev_desc_, idVendor); 3498c2ecf20Sopenharmony_ciCONFIGFS_ATTR(gadget_dev_desc_, idProduct); 3508c2ecf20Sopenharmony_ciCONFIGFS_ATTR(gadget_dev_desc_, bcdDevice); 3518c2ecf20Sopenharmony_ciCONFIGFS_ATTR(gadget_dev_desc_, bcdUSB); 3528c2ecf20Sopenharmony_ciCONFIGFS_ATTR(gadget_dev_desc_, UDC); 3538c2ecf20Sopenharmony_ciCONFIGFS_ATTR(gadget_dev_desc_, max_speed); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic struct configfs_attribute *gadget_root_attrs[] = { 3568c2ecf20Sopenharmony_ci &gadget_dev_desc_attr_bDeviceClass, 3578c2ecf20Sopenharmony_ci &gadget_dev_desc_attr_bDeviceSubClass, 3588c2ecf20Sopenharmony_ci &gadget_dev_desc_attr_bDeviceProtocol, 3598c2ecf20Sopenharmony_ci &gadget_dev_desc_attr_bMaxPacketSize0, 3608c2ecf20Sopenharmony_ci &gadget_dev_desc_attr_idVendor, 3618c2ecf20Sopenharmony_ci &gadget_dev_desc_attr_idProduct, 3628c2ecf20Sopenharmony_ci &gadget_dev_desc_attr_bcdDevice, 3638c2ecf20Sopenharmony_ci &gadget_dev_desc_attr_bcdUSB, 3648c2ecf20Sopenharmony_ci &gadget_dev_desc_attr_UDC, 3658c2ecf20Sopenharmony_ci &gadget_dev_desc_attr_max_speed, 3668c2ecf20Sopenharmony_ci NULL, 3678c2ecf20Sopenharmony_ci}; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic inline struct gadget_strings *to_gadget_strings(struct config_item *item) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci return container_of(to_config_group(item), struct gadget_strings, 3728c2ecf20Sopenharmony_ci group); 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic inline struct gadget_config_name *to_gadget_config_name( 3768c2ecf20Sopenharmony_ci struct config_item *item) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci return container_of(to_config_group(item), struct gadget_config_name, 3798c2ecf20Sopenharmony_ci group); 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic inline struct usb_function_instance *to_usb_function_instance( 3838c2ecf20Sopenharmony_ci struct config_item *item) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci return container_of(to_config_group(item), 3868c2ecf20Sopenharmony_ci struct usb_function_instance, group); 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_cistatic void gadget_info_attr_release(struct config_item *item) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci struct gadget_info *gi = to_gadget_info(item); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci WARN_ON(!list_empty(&gi->cdev.configs)); 3948c2ecf20Sopenharmony_ci WARN_ON(!list_empty(&gi->string_list)); 3958c2ecf20Sopenharmony_ci WARN_ON(!list_empty(&gi->available_func)); 3968c2ecf20Sopenharmony_ci kfree(gi->composite.gadget_driver.function); 3978c2ecf20Sopenharmony_ci kfree(gi); 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic struct configfs_item_operations gadget_root_item_ops = { 4018c2ecf20Sopenharmony_ci .release = gadget_info_attr_release, 4028c2ecf20Sopenharmony_ci}; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic void gadget_config_attr_release(struct config_item *item) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci struct config_usb_cfg *cfg = to_config_usb_cfg(item); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci WARN_ON(!list_empty(&cfg->c.functions)); 4098c2ecf20Sopenharmony_ci list_del(&cfg->c.list); 4108c2ecf20Sopenharmony_ci kfree(cfg->c.label); 4118c2ecf20Sopenharmony_ci kfree(cfg); 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic int config_usb_cfg_link( 4158c2ecf20Sopenharmony_ci struct config_item *usb_cfg_ci, 4168c2ecf20Sopenharmony_ci struct config_item *usb_func_ci) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci struct config_usb_cfg *cfg = to_config_usb_cfg(usb_cfg_ci); 4198c2ecf20Sopenharmony_ci struct usb_composite_dev *cdev = cfg->c.cdev; 4208c2ecf20Sopenharmony_ci struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci struct config_group *group = to_config_group(usb_func_ci); 4238c2ecf20Sopenharmony_ci struct usb_function_instance *fi = container_of(group, 4248c2ecf20Sopenharmony_ci struct usb_function_instance, group); 4258c2ecf20Sopenharmony_ci struct usb_function_instance *a_fi; 4268c2ecf20Sopenharmony_ci struct usb_function *f; 4278c2ecf20Sopenharmony_ci int ret; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci mutex_lock(&gi->lock); 4308c2ecf20Sopenharmony_ci /* 4318c2ecf20Sopenharmony_ci * Make sure this function is from within our _this_ gadget and not 4328c2ecf20Sopenharmony_ci * from another gadget or a random directory. 4338c2ecf20Sopenharmony_ci * Also a function instance can only be linked once. 4348c2ecf20Sopenharmony_ci */ 4358c2ecf20Sopenharmony_ci list_for_each_entry(a_fi, &gi->available_func, cfs_list) { 4368c2ecf20Sopenharmony_ci if (a_fi == fi) 4378c2ecf20Sopenharmony_ci break; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci if (a_fi != fi) { 4408c2ecf20Sopenharmony_ci ret = -EINVAL; 4418c2ecf20Sopenharmony_ci goto out; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci list_for_each_entry(f, &cfg->func_list, list) { 4458c2ecf20Sopenharmony_ci if (f->fi == fi) { 4468c2ecf20Sopenharmony_ci ret = -EEXIST; 4478c2ecf20Sopenharmony_ci goto out; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci f = usb_get_function(fi); 4528c2ecf20Sopenharmony_ci if (IS_ERR(f)) { 4538c2ecf20Sopenharmony_ci ret = PTR_ERR(f); 4548c2ecf20Sopenharmony_ci goto out; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* stash the function until we bind it to the gadget */ 4588c2ecf20Sopenharmony_ci list_add_tail(&f->list, &cfg->func_list); 4598c2ecf20Sopenharmony_ci ret = 0; 4608c2ecf20Sopenharmony_ciout: 4618c2ecf20Sopenharmony_ci mutex_unlock(&gi->lock); 4628c2ecf20Sopenharmony_ci return ret; 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cistatic void config_usb_cfg_unlink( 4668c2ecf20Sopenharmony_ci struct config_item *usb_cfg_ci, 4678c2ecf20Sopenharmony_ci struct config_item *usb_func_ci) 4688c2ecf20Sopenharmony_ci{ 4698c2ecf20Sopenharmony_ci struct config_usb_cfg *cfg = to_config_usb_cfg(usb_cfg_ci); 4708c2ecf20Sopenharmony_ci struct usb_composite_dev *cdev = cfg->c.cdev; 4718c2ecf20Sopenharmony_ci struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci struct config_group *group = to_config_group(usb_func_ci); 4748c2ecf20Sopenharmony_ci struct usb_function_instance *fi = container_of(group, 4758c2ecf20Sopenharmony_ci struct usb_function_instance, group); 4768c2ecf20Sopenharmony_ci struct usb_function *f; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci /* 4798c2ecf20Sopenharmony_ci * ideally I would like to forbid to unlink functions while a gadget is 4808c2ecf20Sopenharmony_ci * bound to an UDC. Since this isn't possible at the moment, we simply 4818c2ecf20Sopenharmony_ci * force an unbind, the function is available here and then we can 4828c2ecf20Sopenharmony_ci * remove the function. 4838c2ecf20Sopenharmony_ci */ 4848c2ecf20Sopenharmony_ci mutex_lock(&gi->lock); 4858c2ecf20Sopenharmony_ci if (gi->composite.gadget_driver.udc_name) 4868c2ecf20Sopenharmony_ci unregister_gadget(gi); 4878c2ecf20Sopenharmony_ci WARN_ON(gi->composite.gadget_driver.udc_name); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci list_for_each_entry(f, &cfg->func_list, list) { 4908c2ecf20Sopenharmony_ci if (f->fi == fi) { 4918c2ecf20Sopenharmony_ci list_del(&f->list); 4928c2ecf20Sopenharmony_ci usb_put_function(f); 4938c2ecf20Sopenharmony_ci mutex_unlock(&gi->lock); 4948c2ecf20Sopenharmony_ci return; 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci mutex_unlock(&gi->lock); 4988c2ecf20Sopenharmony_ci WARN(1, "Unable to locate function to unbind\n"); 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cistatic struct configfs_item_operations gadget_config_item_ops = { 5028c2ecf20Sopenharmony_ci .release = gadget_config_attr_release, 5038c2ecf20Sopenharmony_ci .allow_link = config_usb_cfg_link, 5048c2ecf20Sopenharmony_ci .drop_link = config_usb_cfg_unlink, 5058c2ecf20Sopenharmony_ci}; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cistatic ssize_t gadget_config_desc_MaxPower_show(struct config_item *item, 5098c2ecf20Sopenharmony_ci char *page) 5108c2ecf20Sopenharmony_ci{ 5118c2ecf20Sopenharmony_ci return sprintf(page, "%u\n", to_config_usb_cfg(item)->c.MaxPower); 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cistatic ssize_t gadget_config_desc_MaxPower_store(struct config_item *item, 5158c2ecf20Sopenharmony_ci const char *page, size_t len) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci u16 val; 5188c2ecf20Sopenharmony_ci int ret; 5198c2ecf20Sopenharmony_ci ret = kstrtou16(page, 0, &val); 5208c2ecf20Sopenharmony_ci if (ret) 5218c2ecf20Sopenharmony_ci return ret; 5228c2ecf20Sopenharmony_ci if (DIV_ROUND_UP(val, 8) > 0xff) 5238c2ecf20Sopenharmony_ci return -ERANGE; 5248c2ecf20Sopenharmony_ci to_config_usb_cfg(item)->c.MaxPower = val; 5258c2ecf20Sopenharmony_ci return len; 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cistatic ssize_t gadget_config_desc_bmAttributes_show(struct config_item *item, 5298c2ecf20Sopenharmony_ci char *page) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci return sprintf(page, "0x%02x\n", 5328c2ecf20Sopenharmony_ci to_config_usb_cfg(item)->c.bmAttributes); 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic ssize_t gadget_config_desc_bmAttributes_store(struct config_item *item, 5368c2ecf20Sopenharmony_ci const char *page, size_t len) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci u8 val; 5398c2ecf20Sopenharmony_ci int ret; 5408c2ecf20Sopenharmony_ci ret = kstrtou8(page, 0, &val); 5418c2ecf20Sopenharmony_ci if (ret) 5428c2ecf20Sopenharmony_ci return ret; 5438c2ecf20Sopenharmony_ci if (!(val & USB_CONFIG_ATT_ONE)) 5448c2ecf20Sopenharmony_ci return -EINVAL; 5458c2ecf20Sopenharmony_ci if (val & ~(USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER | 5468c2ecf20Sopenharmony_ci USB_CONFIG_ATT_WAKEUP)) 5478c2ecf20Sopenharmony_ci return -EINVAL; 5488c2ecf20Sopenharmony_ci to_config_usb_cfg(item)->c.bmAttributes = val; 5498c2ecf20Sopenharmony_ci return len; 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ciCONFIGFS_ATTR(gadget_config_desc_, MaxPower); 5538c2ecf20Sopenharmony_ciCONFIGFS_ATTR(gadget_config_desc_, bmAttributes); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic struct configfs_attribute *gadget_config_attrs[] = { 5568c2ecf20Sopenharmony_ci &gadget_config_desc_attr_MaxPower, 5578c2ecf20Sopenharmony_ci &gadget_config_desc_attr_bmAttributes, 5588c2ecf20Sopenharmony_ci NULL, 5598c2ecf20Sopenharmony_ci}; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_cistatic const struct config_item_type gadget_config_type = { 5628c2ecf20Sopenharmony_ci .ct_item_ops = &gadget_config_item_ops, 5638c2ecf20Sopenharmony_ci .ct_attrs = gadget_config_attrs, 5648c2ecf20Sopenharmony_ci .ct_owner = THIS_MODULE, 5658c2ecf20Sopenharmony_ci}; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_cistatic const struct config_item_type gadget_root_type = { 5688c2ecf20Sopenharmony_ci .ct_item_ops = &gadget_root_item_ops, 5698c2ecf20Sopenharmony_ci .ct_attrs = gadget_root_attrs, 5708c2ecf20Sopenharmony_ci .ct_owner = THIS_MODULE, 5718c2ecf20Sopenharmony_ci}; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_cistatic void composite_init_dev(struct usb_composite_dev *cdev) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci spin_lock_init(&cdev->lock); 5768c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&cdev->configs); 5778c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&cdev->gstrings); 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_cistatic struct config_group *function_make( 5818c2ecf20Sopenharmony_ci struct config_group *group, 5828c2ecf20Sopenharmony_ci const char *name) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci struct gadget_info *gi; 5858c2ecf20Sopenharmony_ci struct usb_function_instance *fi; 5868c2ecf20Sopenharmony_ci char buf[MAX_NAME_LEN]; 5878c2ecf20Sopenharmony_ci char *func_name; 5888c2ecf20Sopenharmony_ci char *instance_name; 5898c2ecf20Sopenharmony_ci int ret; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci ret = snprintf(buf, MAX_NAME_LEN, "%s", name); 5928c2ecf20Sopenharmony_ci if (ret >= MAX_NAME_LEN) 5938c2ecf20Sopenharmony_ci return ERR_PTR(-ENAMETOOLONG); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci func_name = buf; 5968c2ecf20Sopenharmony_ci instance_name = strchr(func_name, '.'); 5978c2ecf20Sopenharmony_ci if (!instance_name) { 5988c2ecf20Sopenharmony_ci pr_err("Unable to locate . in FUNC.INSTANCE\n"); 5998c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci *instance_name = '\0'; 6028c2ecf20Sopenharmony_ci instance_name++; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci fi = usb_get_function_instance(func_name); 6058c2ecf20Sopenharmony_ci if (IS_ERR(fi)) 6068c2ecf20Sopenharmony_ci return ERR_CAST(fi); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci ret = config_item_set_name(&fi->group.cg_item, "%s", name); 6098c2ecf20Sopenharmony_ci if (ret) { 6108c2ecf20Sopenharmony_ci usb_put_function_instance(fi); 6118c2ecf20Sopenharmony_ci return ERR_PTR(ret); 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci if (fi->set_inst_name) { 6148c2ecf20Sopenharmony_ci ret = fi->set_inst_name(fi, instance_name); 6158c2ecf20Sopenharmony_ci if (ret) { 6168c2ecf20Sopenharmony_ci usb_put_function_instance(fi); 6178c2ecf20Sopenharmony_ci return ERR_PTR(ret); 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci } 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci gi = container_of(group, struct gadget_info, functions_group); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci mutex_lock(&gi->lock); 6248c2ecf20Sopenharmony_ci list_add_tail(&fi->cfs_list, &gi->available_func); 6258c2ecf20Sopenharmony_ci mutex_unlock(&gi->lock); 6268c2ecf20Sopenharmony_ci return &fi->group; 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_cistatic void function_drop( 6308c2ecf20Sopenharmony_ci struct config_group *group, 6318c2ecf20Sopenharmony_ci struct config_item *item) 6328c2ecf20Sopenharmony_ci{ 6338c2ecf20Sopenharmony_ci struct usb_function_instance *fi = to_usb_function_instance(item); 6348c2ecf20Sopenharmony_ci struct gadget_info *gi; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci gi = container_of(group, struct gadget_info, functions_group); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci mutex_lock(&gi->lock); 6398c2ecf20Sopenharmony_ci list_del(&fi->cfs_list); 6408c2ecf20Sopenharmony_ci mutex_unlock(&gi->lock); 6418c2ecf20Sopenharmony_ci config_item_put(item); 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_cistatic struct configfs_group_operations functions_ops = { 6458c2ecf20Sopenharmony_ci .make_group = &function_make, 6468c2ecf20Sopenharmony_ci .drop_item = &function_drop, 6478c2ecf20Sopenharmony_ci}; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_cistatic const struct config_item_type functions_type = { 6508c2ecf20Sopenharmony_ci .ct_group_ops = &functions_ops, 6518c2ecf20Sopenharmony_ci .ct_owner = THIS_MODULE, 6528c2ecf20Sopenharmony_ci}; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ciGS_STRINGS_RW(gadget_config_name, configuration); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_cistatic struct configfs_attribute *gadget_config_name_langid_attrs[] = { 6578c2ecf20Sopenharmony_ci &gadget_config_name_attr_configuration, 6588c2ecf20Sopenharmony_ci NULL, 6598c2ecf20Sopenharmony_ci}; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_cistatic void gadget_config_name_attr_release(struct config_item *item) 6628c2ecf20Sopenharmony_ci{ 6638c2ecf20Sopenharmony_ci struct gadget_config_name *cn = to_gadget_config_name(item); 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci kfree(cn->configuration); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci list_del(&cn->list); 6688c2ecf20Sopenharmony_ci kfree(cn); 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ciUSB_CONFIG_STRING_RW_OPS(gadget_config_name); 6728c2ecf20Sopenharmony_ciUSB_CONFIG_STRINGS_LANG(gadget_config_name, config_usb_cfg); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_cistatic struct config_group *config_desc_make( 6758c2ecf20Sopenharmony_ci struct config_group *group, 6768c2ecf20Sopenharmony_ci const char *name) 6778c2ecf20Sopenharmony_ci{ 6788c2ecf20Sopenharmony_ci struct gadget_info *gi; 6798c2ecf20Sopenharmony_ci struct config_usb_cfg *cfg; 6808c2ecf20Sopenharmony_ci char buf[MAX_NAME_LEN]; 6818c2ecf20Sopenharmony_ci char *num_str; 6828c2ecf20Sopenharmony_ci u8 num; 6838c2ecf20Sopenharmony_ci int ret; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci gi = container_of(group, struct gadget_info, configs_group); 6868c2ecf20Sopenharmony_ci ret = snprintf(buf, MAX_NAME_LEN, "%s", name); 6878c2ecf20Sopenharmony_ci if (ret >= MAX_NAME_LEN) 6888c2ecf20Sopenharmony_ci return ERR_PTR(-ENAMETOOLONG); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci num_str = strchr(buf, '.'); 6918c2ecf20Sopenharmony_ci if (!num_str) { 6928c2ecf20Sopenharmony_ci pr_err("Unable to locate . in name.bConfigurationValue\n"); 6938c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci *num_str = '\0'; 6978c2ecf20Sopenharmony_ci num_str++; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci if (!strlen(buf)) 7008c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci ret = kstrtou8(num_str, 0, &num); 7038c2ecf20Sopenharmony_ci if (ret) 7048c2ecf20Sopenharmony_ci return ERR_PTR(ret); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); 7078c2ecf20Sopenharmony_ci if (!cfg) 7088c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 7098c2ecf20Sopenharmony_ci cfg->c.label = kstrdup(buf, GFP_KERNEL); 7108c2ecf20Sopenharmony_ci if (!cfg->c.label) { 7118c2ecf20Sopenharmony_ci ret = -ENOMEM; 7128c2ecf20Sopenharmony_ci goto err; 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci cfg->c.bConfigurationValue = num; 7158c2ecf20Sopenharmony_ci cfg->c.MaxPower = CONFIG_USB_GADGET_VBUS_DRAW; 7168c2ecf20Sopenharmony_ci cfg->c.bmAttributes = USB_CONFIG_ATT_ONE; 7178c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&cfg->string_list); 7188c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&cfg->func_list); 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci config_group_init_type_name(&cfg->group, name, 7218c2ecf20Sopenharmony_ci &gadget_config_type); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci config_group_init_type_name(&cfg->strings_group, "strings", 7248c2ecf20Sopenharmony_ci &gadget_config_name_strings_type); 7258c2ecf20Sopenharmony_ci configfs_add_default_group(&cfg->strings_group, &cfg->group); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci ret = usb_add_config_only(&gi->cdev, &cfg->c); 7288c2ecf20Sopenharmony_ci if (ret) 7298c2ecf20Sopenharmony_ci goto err; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci return &cfg->group; 7328c2ecf20Sopenharmony_cierr: 7338c2ecf20Sopenharmony_ci kfree(cfg->c.label); 7348c2ecf20Sopenharmony_ci kfree(cfg); 7358c2ecf20Sopenharmony_ci return ERR_PTR(ret); 7368c2ecf20Sopenharmony_ci} 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_cistatic void config_desc_drop( 7398c2ecf20Sopenharmony_ci struct config_group *group, 7408c2ecf20Sopenharmony_ci struct config_item *item) 7418c2ecf20Sopenharmony_ci{ 7428c2ecf20Sopenharmony_ci config_item_put(item); 7438c2ecf20Sopenharmony_ci} 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_cistatic struct configfs_group_operations config_desc_ops = { 7468c2ecf20Sopenharmony_ci .make_group = &config_desc_make, 7478c2ecf20Sopenharmony_ci .drop_item = &config_desc_drop, 7488c2ecf20Sopenharmony_ci}; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_cistatic const struct config_item_type config_desc_type = { 7518c2ecf20Sopenharmony_ci .ct_group_ops = &config_desc_ops, 7528c2ecf20Sopenharmony_ci .ct_owner = THIS_MODULE, 7538c2ecf20Sopenharmony_ci}; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ciGS_STRINGS_RW(gadget_strings, manufacturer); 7568c2ecf20Sopenharmony_ciGS_STRINGS_RW(gadget_strings, product); 7578c2ecf20Sopenharmony_ciGS_STRINGS_RW(gadget_strings, serialnumber); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_cistatic struct configfs_attribute *gadget_strings_langid_attrs[] = { 7608c2ecf20Sopenharmony_ci &gadget_strings_attr_manufacturer, 7618c2ecf20Sopenharmony_ci &gadget_strings_attr_product, 7628c2ecf20Sopenharmony_ci &gadget_strings_attr_serialnumber, 7638c2ecf20Sopenharmony_ci NULL, 7648c2ecf20Sopenharmony_ci}; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_cistatic void gadget_strings_attr_release(struct config_item *item) 7678c2ecf20Sopenharmony_ci{ 7688c2ecf20Sopenharmony_ci struct gadget_strings *gs = to_gadget_strings(item); 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci kfree(gs->manufacturer); 7718c2ecf20Sopenharmony_ci kfree(gs->product); 7728c2ecf20Sopenharmony_ci kfree(gs->serialnumber); 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci list_del(&gs->list); 7758c2ecf20Sopenharmony_ci kfree(gs); 7768c2ecf20Sopenharmony_ci} 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ciUSB_CONFIG_STRING_RW_OPS(gadget_strings); 7798c2ecf20Sopenharmony_ciUSB_CONFIG_STRINGS_LANG(gadget_strings, gadget_info); 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_cistatic inline struct os_desc *to_os_desc(struct config_item *item) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci return container_of(to_config_group(item), struct os_desc, group); 7848c2ecf20Sopenharmony_ci} 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_cistatic inline struct gadget_info *os_desc_item_to_gadget_info( 7878c2ecf20Sopenharmony_ci struct config_item *item) 7888c2ecf20Sopenharmony_ci{ 7898c2ecf20Sopenharmony_ci return to_gadget_info(to_os_desc(item)->group.cg_item.ci_parent); 7908c2ecf20Sopenharmony_ci} 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_cistatic ssize_t os_desc_use_show(struct config_item *item, char *page) 7938c2ecf20Sopenharmony_ci{ 7948c2ecf20Sopenharmony_ci return sprintf(page, "%d\n", 7958c2ecf20Sopenharmony_ci os_desc_item_to_gadget_info(item)->use_os_desc); 7968c2ecf20Sopenharmony_ci} 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_cistatic ssize_t os_desc_use_store(struct config_item *item, const char *page, 7998c2ecf20Sopenharmony_ci size_t len) 8008c2ecf20Sopenharmony_ci{ 8018c2ecf20Sopenharmony_ci struct gadget_info *gi = os_desc_item_to_gadget_info(item); 8028c2ecf20Sopenharmony_ci int ret; 8038c2ecf20Sopenharmony_ci bool use; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci mutex_lock(&gi->lock); 8068c2ecf20Sopenharmony_ci ret = strtobool(page, &use); 8078c2ecf20Sopenharmony_ci if (!ret) { 8088c2ecf20Sopenharmony_ci gi->use_os_desc = use; 8098c2ecf20Sopenharmony_ci ret = len; 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci mutex_unlock(&gi->lock); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci return ret; 8148c2ecf20Sopenharmony_ci} 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_cistatic ssize_t os_desc_b_vendor_code_show(struct config_item *item, char *page) 8178c2ecf20Sopenharmony_ci{ 8188c2ecf20Sopenharmony_ci return sprintf(page, "0x%02x\n", 8198c2ecf20Sopenharmony_ci os_desc_item_to_gadget_info(item)->b_vendor_code); 8208c2ecf20Sopenharmony_ci} 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_cistatic ssize_t os_desc_b_vendor_code_store(struct config_item *item, 8238c2ecf20Sopenharmony_ci const char *page, size_t len) 8248c2ecf20Sopenharmony_ci{ 8258c2ecf20Sopenharmony_ci struct gadget_info *gi = os_desc_item_to_gadget_info(item); 8268c2ecf20Sopenharmony_ci int ret; 8278c2ecf20Sopenharmony_ci u8 b_vendor_code; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci mutex_lock(&gi->lock); 8308c2ecf20Sopenharmony_ci ret = kstrtou8(page, 0, &b_vendor_code); 8318c2ecf20Sopenharmony_ci if (!ret) { 8328c2ecf20Sopenharmony_ci gi->b_vendor_code = b_vendor_code; 8338c2ecf20Sopenharmony_ci ret = len; 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci mutex_unlock(&gi->lock); 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci return ret; 8388c2ecf20Sopenharmony_ci} 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_cistatic ssize_t os_desc_qw_sign_show(struct config_item *item, char *page) 8418c2ecf20Sopenharmony_ci{ 8428c2ecf20Sopenharmony_ci struct gadget_info *gi = os_desc_item_to_gadget_info(item); 8438c2ecf20Sopenharmony_ci int res; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci res = utf16s_to_utf8s((wchar_t *) gi->qw_sign, OS_STRING_QW_SIGN_LEN, 8468c2ecf20Sopenharmony_ci UTF16_LITTLE_ENDIAN, page, PAGE_SIZE - 1); 8478c2ecf20Sopenharmony_ci page[res++] = '\n'; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci return res; 8508c2ecf20Sopenharmony_ci} 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_cistatic ssize_t os_desc_qw_sign_store(struct config_item *item, const char *page, 8538c2ecf20Sopenharmony_ci size_t len) 8548c2ecf20Sopenharmony_ci{ 8558c2ecf20Sopenharmony_ci struct gadget_info *gi = os_desc_item_to_gadget_info(item); 8568c2ecf20Sopenharmony_ci int res, l; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci l = min((int)len, OS_STRING_QW_SIGN_LEN >> 1); 8598c2ecf20Sopenharmony_ci if (page[l - 1] == '\n') 8608c2ecf20Sopenharmony_ci --l; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci mutex_lock(&gi->lock); 8638c2ecf20Sopenharmony_ci res = utf8s_to_utf16s(page, l, 8648c2ecf20Sopenharmony_ci UTF16_LITTLE_ENDIAN, (wchar_t *) gi->qw_sign, 8658c2ecf20Sopenharmony_ci OS_STRING_QW_SIGN_LEN); 8668c2ecf20Sopenharmony_ci if (res > 0) 8678c2ecf20Sopenharmony_ci res = len; 8688c2ecf20Sopenharmony_ci mutex_unlock(&gi->lock); 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci return res; 8718c2ecf20Sopenharmony_ci} 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ciCONFIGFS_ATTR(os_desc_, use); 8748c2ecf20Sopenharmony_ciCONFIGFS_ATTR(os_desc_, b_vendor_code); 8758c2ecf20Sopenharmony_ciCONFIGFS_ATTR(os_desc_, qw_sign); 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_cistatic struct configfs_attribute *os_desc_attrs[] = { 8788c2ecf20Sopenharmony_ci &os_desc_attr_use, 8798c2ecf20Sopenharmony_ci &os_desc_attr_b_vendor_code, 8808c2ecf20Sopenharmony_ci &os_desc_attr_qw_sign, 8818c2ecf20Sopenharmony_ci NULL, 8828c2ecf20Sopenharmony_ci}; 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_cistatic void os_desc_attr_release(struct config_item *item) 8858c2ecf20Sopenharmony_ci{ 8868c2ecf20Sopenharmony_ci struct os_desc *os_desc = to_os_desc(item); 8878c2ecf20Sopenharmony_ci kfree(os_desc); 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_cistatic int os_desc_link(struct config_item *os_desc_ci, 8918c2ecf20Sopenharmony_ci struct config_item *usb_cfg_ci) 8928c2ecf20Sopenharmony_ci{ 8938c2ecf20Sopenharmony_ci struct gadget_info *gi = container_of(to_config_group(os_desc_ci), 8948c2ecf20Sopenharmony_ci struct gadget_info, os_desc_group); 8958c2ecf20Sopenharmony_ci struct usb_composite_dev *cdev = &gi->cdev; 8968c2ecf20Sopenharmony_ci struct config_usb_cfg *c_target = 8978c2ecf20Sopenharmony_ci container_of(to_config_group(usb_cfg_ci), 8988c2ecf20Sopenharmony_ci struct config_usb_cfg, group); 8998c2ecf20Sopenharmony_ci struct usb_configuration *c; 9008c2ecf20Sopenharmony_ci int ret; 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci mutex_lock(&gi->lock); 9038c2ecf20Sopenharmony_ci list_for_each_entry(c, &cdev->configs, list) { 9048c2ecf20Sopenharmony_ci if (c == &c_target->c) 9058c2ecf20Sopenharmony_ci break; 9068c2ecf20Sopenharmony_ci } 9078c2ecf20Sopenharmony_ci if (c != &c_target->c) { 9088c2ecf20Sopenharmony_ci ret = -EINVAL; 9098c2ecf20Sopenharmony_ci goto out; 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci if (cdev->os_desc_config) { 9138c2ecf20Sopenharmony_ci ret = -EBUSY; 9148c2ecf20Sopenharmony_ci goto out; 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci cdev->os_desc_config = &c_target->c; 9188c2ecf20Sopenharmony_ci ret = 0; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ciout: 9218c2ecf20Sopenharmony_ci mutex_unlock(&gi->lock); 9228c2ecf20Sopenharmony_ci return ret; 9238c2ecf20Sopenharmony_ci} 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_cistatic void os_desc_unlink(struct config_item *os_desc_ci, 9268c2ecf20Sopenharmony_ci struct config_item *usb_cfg_ci) 9278c2ecf20Sopenharmony_ci{ 9288c2ecf20Sopenharmony_ci struct gadget_info *gi = container_of(to_config_group(os_desc_ci), 9298c2ecf20Sopenharmony_ci struct gadget_info, os_desc_group); 9308c2ecf20Sopenharmony_ci struct usb_composite_dev *cdev = &gi->cdev; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci mutex_lock(&gi->lock); 9338c2ecf20Sopenharmony_ci if (gi->composite.gadget_driver.udc_name) 9348c2ecf20Sopenharmony_ci unregister_gadget(gi); 9358c2ecf20Sopenharmony_ci cdev->os_desc_config = NULL; 9368c2ecf20Sopenharmony_ci WARN_ON(gi->composite.gadget_driver.udc_name); 9378c2ecf20Sopenharmony_ci mutex_unlock(&gi->lock); 9388c2ecf20Sopenharmony_ci} 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_cistatic struct configfs_item_operations os_desc_ops = { 9418c2ecf20Sopenharmony_ci .release = os_desc_attr_release, 9428c2ecf20Sopenharmony_ci .allow_link = os_desc_link, 9438c2ecf20Sopenharmony_ci .drop_link = os_desc_unlink, 9448c2ecf20Sopenharmony_ci}; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_cistatic struct config_item_type os_desc_type = { 9478c2ecf20Sopenharmony_ci .ct_item_ops = &os_desc_ops, 9488c2ecf20Sopenharmony_ci .ct_attrs = os_desc_attrs, 9498c2ecf20Sopenharmony_ci .ct_owner = THIS_MODULE, 9508c2ecf20Sopenharmony_ci}; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_cistatic inline struct usb_os_desc_ext_prop 9538c2ecf20Sopenharmony_ci*to_usb_os_desc_ext_prop(struct config_item *item) 9548c2ecf20Sopenharmony_ci{ 9558c2ecf20Sopenharmony_ci return container_of(item, struct usb_os_desc_ext_prop, item); 9568c2ecf20Sopenharmony_ci} 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_cistatic ssize_t ext_prop_type_show(struct config_item *item, char *page) 9598c2ecf20Sopenharmony_ci{ 9608c2ecf20Sopenharmony_ci return sprintf(page, "%d\n", to_usb_os_desc_ext_prop(item)->type); 9618c2ecf20Sopenharmony_ci} 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_cistatic ssize_t ext_prop_type_store(struct config_item *item, 9648c2ecf20Sopenharmony_ci const char *page, size_t len) 9658c2ecf20Sopenharmony_ci{ 9668c2ecf20Sopenharmony_ci struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); 9678c2ecf20Sopenharmony_ci struct usb_os_desc *desc = to_usb_os_desc(ext_prop->item.ci_parent); 9688c2ecf20Sopenharmony_ci u8 type; 9698c2ecf20Sopenharmony_ci int ret; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci if (desc->opts_mutex) 9728c2ecf20Sopenharmony_ci mutex_lock(desc->opts_mutex); 9738c2ecf20Sopenharmony_ci ret = kstrtou8(page, 0, &type); 9748c2ecf20Sopenharmony_ci if (ret) 9758c2ecf20Sopenharmony_ci goto end; 9768c2ecf20Sopenharmony_ci if (type < USB_EXT_PROP_UNICODE || type > USB_EXT_PROP_UNICODE_MULTI) { 9778c2ecf20Sopenharmony_ci ret = -EINVAL; 9788c2ecf20Sopenharmony_ci goto end; 9798c2ecf20Sopenharmony_ci } 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci if ((ext_prop->type == USB_EXT_PROP_BINARY || 9828c2ecf20Sopenharmony_ci ext_prop->type == USB_EXT_PROP_LE32 || 9838c2ecf20Sopenharmony_ci ext_prop->type == USB_EXT_PROP_BE32) && 9848c2ecf20Sopenharmony_ci (type == USB_EXT_PROP_UNICODE || 9858c2ecf20Sopenharmony_ci type == USB_EXT_PROP_UNICODE_ENV || 9868c2ecf20Sopenharmony_ci type == USB_EXT_PROP_UNICODE_LINK)) 9878c2ecf20Sopenharmony_ci ext_prop->data_len <<= 1; 9888c2ecf20Sopenharmony_ci else if ((ext_prop->type == USB_EXT_PROP_UNICODE || 9898c2ecf20Sopenharmony_ci ext_prop->type == USB_EXT_PROP_UNICODE_ENV || 9908c2ecf20Sopenharmony_ci ext_prop->type == USB_EXT_PROP_UNICODE_LINK) && 9918c2ecf20Sopenharmony_ci (type == USB_EXT_PROP_BINARY || 9928c2ecf20Sopenharmony_ci type == USB_EXT_PROP_LE32 || 9938c2ecf20Sopenharmony_ci type == USB_EXT_PROP_BE32)) 9948c2ecf20Sopenharmony_ci ext_prop->data_len >>= 1; 9958c2ecf20Sopenharmony_ci ext_prop->type = type; 9968c2ecf20Sopenharmony_ci ret = len; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ciend: 9998c2ecf20Sopenharmony_ci if (desc->opts_mutex) 10008c2ecf20Sopenharmony_ci mutex_unlock(desc->opts_mutex); 10018c2ecf20Sopenharmony_ci return ret; 10028c2ecf20Sopenharmony_ci} 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_cistatic ssize_t ext_prop_data_show(struct config_item *item, char *page) 10058c2ecf20Sopenharmony_ci{ 10068c2ecf20Sopenharmony_ci struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); 10078c2ecf20Sopenharmony_ci int len = ext_prop->data_len; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci if (ext_prop->type == USB_EXT_PROP_UNICODE || 10108c2ecf20Sopenharmony_ci ext_prop->type == USB_EXT_PROP_UNICODE_ENV || 10118c2ecf20Sopenharmony_ci ext_prop->type == USB_EXT_PROP_UNICODE_LINK) 10128c2ecf20Sopenharmony_ci len >>= 1; 10138c2ecf20Sopenharmony_ci memcpy(page, ext_prop->data, len); 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci return len; 10168c2ecf20Sopenharmony_ci} 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_cistatic ssize_t ext_prop_data_store(struct config_item *item, 10198c2ecf20Sopenharmony_ci const char *page, size_t len) 10208c2ecf20Sopenharmony_ci{ 10218c2ecf20Sopenharmony_ci struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); 10228c2ecf20Sopenharmony_ci struct usb_os_desc *desc = to_usb_os_desc(ext_prop->item.ci_parent); 10238c2ecf20Sopenharmony_ci char *new_data; 10248c2ecf20Sopenharmony_ci size_t ret_len = len; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci if (page[len - 1] == '\n' || page[len - 1] == '\0') 10278c2ecf20Sopenharmony_ci --len; 10288c2ecf20Sopenharmony_ci new_data = kmemdup(page, len, GFP_KERNEL); 10298c2ecf20Sopenharmony_ci if (!new_data) 10308c2ecf20Sopenharmony_ci return -ENOMEM; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci if (desc->opts_mutex) 10338c2ecf20Sopenharmony_ci mutex_lock(desc->opts_mutex); 10348c2ecf20Sopenharmony_ci kfree(ext_prop->data); 10358c2ecf20Sopenharmony_ci ext_prop->data = new_data; 10368c2ecf20Sopenharmony_ci desc->ext_prop_len -= ext_prop->data_len; 10378c2ecf20Sopenharmony_ci ext_prop->data_len = len; 10388c2ecf20Sopenharmony_ci desc->ext_prop_len += ext_prop->data_len; 10398c2ecf20Sopenharmony_ci if (ext_prop->type == USB_EXT_PROP_UNICODE || 10408c2ecf20Sopenharmony_ci ext_prop->type == USB_EXT_PROP_UNICODE_ENV || 10418c2ecf20Sopenharmony_ci ext_prop->type == USB_EXT_PROP_UNICODE_LINK) { 10428c2ecf20Sopenharmony_ci desc->ext_prop_len -= ext_prop->data_len; 10438c2ecf20Sopenharmony_ci ext_prop->data_len <<= 1; 10448c2ecf20Sopenharmony_ci ext_prop->data_len += 2; 10458c2ecf20Sopenharmony_ci desc->ext_prop_len += ext_prop->data_len; 10468c2ecf20Sopenharmony_ci } 10478c2ecf20Sopenharmony_ci if (desc->opts_mutex) 10488c2ecf20Sopenharmony_ci mutex_unlock(desc->opts_mutex); 10498c2ecf20Sopenharmony_ci return ret_len; 10508c2ecf20Sopenharmony_ci} 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ciCONFIGFS_ATTR(ext_prop_, type); 10538c2ecf20Sopenharmony_ciCONFIGFS_ATTR(ext_prop_, data); 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_cistatic struct configfs_attribute *ext_prop_attrs[] = { 10568c2ecf20Sopenharmony_ci &ext_prop_attr_type, 10578c2ecf20Sopenharmony_ci &ext_prop_attr_data, 10588c2ecf20Sopenharmony_ci NULL, 10598c2ecf20Sopenharmony_ci}; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_cistatic void usb_os_desc_ext_prop_release(struct config_item *item) 10628c2ecf20Sopenharmony_ci{ 10638c2ecf20Sopenharmony_ci struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci kfree(ext_prop); /* frees a whole chunk */ 10668c2ecf20Sopenharmony_ci} 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_cistatic struct configfs_item_operations ext_prop_ops = { 10698c2ecf20Sopenharmony_ci .release = usb_os_desc_ext_prop_release, 10708c2ecf20Sopenharmony_ci}; 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_cistatic struct config_item *ext_prop_make( 10738c2ecf20Sopenharmony_ci struct config_group *group, 10748c2ecf20Sopenharmony_ci const char *name) 10758c2ecf20Sopenharmony_ci{ 10768c2ecf20Sopenharmony_ci struct usb_os_desc_ext_prop *ext_prop; 10778c2ecf20Sopenharmony_ci struct config_item_type *ext_prop_type; 10788c2ecf20Sopenharmony_ci struct usb_os_desc *desc; 10798c2ecf20Sopenharmony_ci char *vlabuf; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci vla_group(data_chunk); 10828c2ecf20Sopenharmony_ci vla_item(data_chunk, struct usb_os_desc_ext_prop, ext_prop, 1); 10838c2ecf20Sopenharmony_ci vla_item(data_chunk, struct config_item_type, ext_prop_type, 1); 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci vlabuf = kzalloc(vla_group_size(data_chunk), GFP_KERNEL); 10868c2ecf20Sopenharmony_ci if (!vlabuf) 10878c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci ext_prop = vla_ptr(vlabuf, data_chunk, ext_prop); 10908c2ecf20Sopenharmony_ci ext_prop_type = vla_ptr(vlabuf, data_chunk, ext_prop_type); 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci desc = container_of(group, struct usb_os_desc, group); 10938c2ecf20Sopenharmony_ci ext_prop_type->ct_item_ops = &ext_prop_ops; 10948c2ecf20Sopenharmony_ci ext_prop_type->ct_attrs = ext_prop_attrs; 10958c2ecf20Sopenharmony_ci ext_prop_type->ct_owner = desc->owner; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci config_item_init_type_name(&ext_prop->item, name, ext_prop_type); 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci ext_prop->name = kstrdup(name, GFP_KERNEL); 11008c2ecf20Sopenharmony_ci if (!ext_prop->name) { 11018c2ecf20Sopenharmony_ci kfree(vlabuf); 11028c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 11038c2ecf20Sopenharmony_ci } 11048c2ecf20Sopenharmony_ci desc->ext_prop_len += 14; 11058c2ecf20Sopenharmony_ci ext_prop->name_len = 2 * strlen(ext_prop->name) + 2; 11068c2ecf20Sopenharmony_ci if (desc->opts_mutex) 11078c2ecf20Sopenharmony_ci mutex_lock(desc->opts_mutex); 11088c2ecf20Sopenharmony_ci desc->ext_prop_len += ext_prop->name_len; 11098c2ecf20Sopenharmony_ci list_add_tail(&ext_prop->entry, &desc->ext_prop); 11108c2ecf20Sopenharmony_ci ++desc->ext_prop_count; 11118c2ecf20Sopenharmony_ci if (desc->opts_mutex) 11128c2ecf20Sopenharmony_ci mutex_unlock(desc->opts_mutex); 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci return &ext_prop->item; 11158c2ecf20Sopenharmony_ci} 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_cistatic void ext_prop_drop(struct config_group *group, struct config_item *item) 11188c2ecf20Sopenharmony_ci{ 11198c2ecf20Sopenharmony_ci struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); 11208c2ecf20Sopenharmony_ci struct usb_os_desc *desc = to_usb_os_desc(&group->cg_item); 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci if (desc->opts_mutex) 11238c2ecf20Sopenharmony_ci mutex_lock(desc->opts_mutex); 11248c2ecf20Sopenharmony_ci list_del(&ext_prop->entry); 11258c2ecf20Sopenharmony_ci --desc->ext_prop_count; 11268c2ecf20Sopenharmony_ci kfree(ext_prop->name); 11278c2ecf20Sopenharmony_ci desc->ext_prop_len -= (ext_prop->name_len + ext_prop->data_len + 14); 11288c2ecf20Sopenharmony_ci if (desc->opts_mutex) 11298c2ecf20Sopenharmony_ci mutex_unlock(desc->opts_mutex); 11308c2ecf20Sopenharmony_ci config_item_put(item); 11318c2ecf20Sopenharmony_ci} 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_cistatic struct configfs_group_operations interf_grp_ops = { 11348c2ecf20Sopenharmony_ci .make_item = &ext_prop_make, 11358c2ecf20Sopenharmony_ci .drop_item = &ext_prop_drop, 11368c2ecf20Sopenharmony_ci}; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_cistatic ssize_t interf_grp_compatible_id_show(struct config_item *item, 11398c2ecf20Sopenharmony_ci char *page) 11408c2ecf20Sopenharmony_ci{ 11418c2ecf20Sopenharmony_ci memcpy(page, to_usb_os_desc(item)->ext_compat_id, 8); 11428c2ecf20Sopenharmony_ci return 8; 11438c2ecf20Sopenharmony_ci} 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_cistatic ssize_t interf_grp_compatible_id_store(struct config_item *item, 11468c2ecf20Sopenharmony_ci const char *page, size_t len) 11478c2ecf20Sopenharmony_ci{ 11488c2ecf20Sopenharmony_ci struct usb_os_desc *desc = to_usb_os_desc(item); 11498c2ecf20Sopenharmony_ci int l; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci l = min_t(int, 8, len); 11528c2ecf20Sopenharmony_ci if (page[l - 1] == '\n') 11538c2ecf20Sopenharmony_ci --l; 11548c2ecf20Sopenharmony_ci if (desc->opts_mutex) 11558c2ecf20Sopenharmony_ci mutex_lock(desc->opts_mutex); 11568c2ecf20Sopenharmony_ci memcpy(desc->ext_compat_id, page, l); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci if (desc->opts_mutex) 11598c2ecf20Sopenharmony_ci mutex_unlock(desc->opts_mutex); 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci return len; 11628c2ecf20Sopenharmony_ci} 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_cistatic ssize_t interf_grp_sub_compatible_id_show(struct config_item *item, 11658c2ecf20Sopenharmony_ci char *page) 11668c2ecf20Sopenharmony_ci{ 11678c2ecf20Sopenharmony_ci memcpy(page, to_usb_os_desc(item)->ext_compat_id + 8, 8); 11688c2ecf20Sopenharmony_ci return 8; 11698c2ecf20Sopenharmony_ci} 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_cistatic ssize_t interf_grp_sub_compatible_id_store(struct config_item *item, 11728c2ecf20Sopenharmony_ci const char *page, size_t len) 11738c2ecf20Sopenharmony_ci{ 11748c2ecf20Sopenharmony_ci struct usb_os_desc *desc = to_usb_os_desc(item); 11758c2ecf20Sopenharmony_ci int l; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci l = min_t(int, 8, len); 11788c2ecf20Sopenharmony_ci if (page[l - 1] == '\n') 11798c2ecf20Sopenharmony_ci --l; 11808c2ecf20Sopenharmony_ci if (desc->opts_mutex) 11818c2ecf20Sopenharmony_ci mutex_lock(desc->opts_mutex); 11828c2ecf20Sopenharmony_ci memcpy(desc->ext_compat_id + 8, page, l); 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci if (desc->opts_mutex) 11858c2ecf20Sopenharmony_ci mutex_unlock(desc->opts_mutex); 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci return len; 11888c2ecf20Sopenharmony_ci} 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ciCONFIGFS_ATTR(interf_grp_, compatible_id); 11918c2ecf20Sopenharmony_ciCONFIGFS_ATTR(interf_grp_, sub_compatible_id); 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_cistatic struct configfs_attribute *interf_grp_attrs[] = { 11948c2ecf20Sopenharmony_ci &interf_grp_attr_compatible_id, 11958c2ecf20Sopenharmony_ci &interf_grp_attr_sub_compatible_id, 11968c2ecf20Sopenharmony_ci NULL 11978c2ecf20Sopenharmony_ci}; 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_cistruct config_group *usb_os_desc_prepare_interf_dir( 12008c2ecf20Sopenharmony_ci struct config_group *parent, 12018c2ecf20Sopenharmony_ci int n_interf, 12028c2ecf20Sopenharmony_ci struct usb_os_desc **desc, 12038c2ecf20Sopenharmony_ci char **names, 12048c2ecf20Sopenharmony_ci struct module *owner) 12058c2ecf20Sopenharmony_ci{ 12068c2ecf20Sopenharmony_ci struct config_group *os_desc_group; 12078c2ecf20Sopenharmony_ci struct config_item_type *os_desc_type, *interface_type; 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci vla_group(data_chunk); 12108c2ecf20Sopenharmony_ci vla_item(data_chunk, struct config_group, os_desc_group, 1); 12118c2ecf20Sopenharmony_ci vla_item(data_chunk, struct config_item_type, os_desc_type, 1); 12128c2ecf20Sopenharmony_ci vla_item(data_chunk, struct config_item_type, interface_type, 1); 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci char *vlabuf = kzalloc(vla_group_size(data_chunk), GFP_KERNEL); 12158c2ecf20Sopenharmony_ci if (!vlabuf) 12168c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci os_desc_group = vla_ptr(vlabuf, data_chunk, os_desc_group); 12198c2ecf20Sopenharmony_ci os_desc_type = vla_ptr(vlabuf, data_chunk, os_desc_type); 12208c2ecf20Sopenharmony_ci interface_type = vla_ptr(vlabuf, data_chunk, interface_type); 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci os_desc_type->ct_owner = owner; 12238c2ecf20Sopenharmony_ci config_group_init_type_name(os_desc_group, "os_desc", os_desc_type); 12248c2ecf20Sopenharmony_ci configfs_add_default_group(os_desc_group, parent); 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci interface_type->ct_group_ops = &interf_grp_ops; 12278c2ecf20Sopenharmony_ci interface_type->ct_attrs = interf_grp_attrs; 12288c2ecf20Sopenharmony_ci interface_type->ct_owner = owner; 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci while (n_interf--) { 12318c2ecf20Sopenharmony_ci struct usb_os_desc *d; 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci d = desc[n_interf]; 12348c2ecf20Sopenharmony_ci d->owner = owner; 12358c2ecf20Sopenharmony_ci config_group_init_type_name(&d->group, "", interface_type); 12368c2ecf20Sopenharmony_ci config_item_set_name(&d->group.cg_item, "interface.%s", 12378c2ecf20Sopenharmony_ci names[n_interf]); 12388c2ecf20Sopenharmony_ci configfs_add_default_group(&d->group, os_desc_group); 12398c2ecf20Sopenharmony_ci } 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci return os_desc_group; 12428c2ecf20Sopenharmony_ci} 12438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(usb_os_desc_prepare_interf_dir); 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_cistatic int configfs_do_nothing(struct usb_composite_dev *cdev) 12468c2ecf20Sopenharmony_ci{ 12478c2ecf20Sopenharmony_ci WARN_ON(1); 12488c2ecf20Sopenharmony_ci return -EINVAL; 12498c2ecf20Sopenharmony_ci} 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ciint composite_dev_prepare(struct usb_composite_driver *composite, 12528c2ecf20Sopenharmony_ci struct usb_composite_dev *dev); 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ciint composite_os_desc_req_prepare(struct usb_composite_dev *cdev, 12558c2ecf20Sopenharmony_ci struct usb_ep *ep0); 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_cistatic void purge_configs_funcs(struct gadget_info *gi) 12588c2ecf20Sopenharmony_ci{ 12598c2ecf20Sopenharmony_ci struct usb_configuration *c; 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci list_for_each_entry(c, &gi->cdev.configs, list) { 12628c2ecf20Sopenharmony_ci struct usb_function *f, *tmp; 12638c2ecf20Sopenharmony_ci struct config_usb_cfg *cfg; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci cfg = container_of(c, struct config_usb_cfg, c); 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci list_for_each_entry_safe_reverse(f, tmp, &c->functions, list) { 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci list_move(&f->list, &cfg->func_list); 12708c2ecf20Sopenharmony_ci if (f->unbind) { 12718c2ecf20Sopenharmony_ci dev_dbg(&gi->cdev.gadget->dev, 12728c2ecf20Sopenharmony_ci "unbind function '%s'/%p\n", 12738c2ecf20Sopenharmony_ci f->name, f); 12748c2ecf20Sopenharmony_ci f->unbind(c, f); 12758c2ecf20Sopenharmony_ci } 12768c2ecf20Sopenharmony_ci } 12778c2ecf20Sopenharmony_ci c->next_interface_id = 0; 12788c2ecf20Sopenharmony_ci memset(c->interface, 0, sizeof(c->interface)); 12798c2ecf20Sopenharmony_ci c->superspeed_plus = 0; 12808c2ecf20Sopenharmony_ci c->superspeed = 0; 12818c2ecf20Sopenharmony_ci c->highspeed = 0; 12828c2ecf20Sopenharmony_ci c->fullspeed = 0; 12838c2ecf20Sopenharmony_ci } 12848c2ecf20Sopenharmony_ci} 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_cistatic int configfs_composite_bind(struct usb_gadget *gadget, 12878c2ecf20Sopenharmony_ci struct usb_gadget_driver *gdriver) 12888c2ecf20Sopenharmony_ci{ 12898c2ecf20Sopenharmony_ci struct usb_composite_driver *composite = to_cdriver(gdriver); 12908c2ecf20Sopenharmony_ci struct gadget_info *gi = container_of(composite, 12918c2ecf20Sopenharmony_ci struct gadget_info, composite); 12928c2ecf20Sopenharmony_ci struct usb_composite_dev *cdev = &gi->cdev; 12938c2ecf20Sopenharmony_ci struct usb_configuration *c; 12948c2ecf20Sopenharmony_ci struct usb_string *s; 12958c2ecf20Sopenharmony_ci unsigned i; 12968c2ecf20Sopenharmony_ci int ret; 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci /* the gi->lock is hold by the caller */ 12998c2ecf20Sopenharmony_ci gi->unbind = 0; 13008c2ecf20Sopenharmony_ci cdev->gadget = gadget; 13018c2ecf20Sopenharmony_ci set_gadget_data(gadget, cdev); 13028c2ecf20Sopenharmony_ci ret = composite_dev_prepare(composite, cdev); 13038c2ecf20Sopenharmony_ci if (ret) 13048c2ecf20Sopenharmony_ci return ret; 13058c2ecf20Sopenharmony_ci /* and now the gadget bind */ 13068c2ecf20Sopenharmony_ci ret = -EINVAL; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci if (list_empty(&gi->cdev.configs)) { 13098c2ecf20Sopenharmony_ci pr_err("Need at least one configuration in %s.\n", 13108c2ecf20Sopenharmony_ci gi->composite.name); 13118c2ecf20Sopenharmony_ci goto err_comp_cleanup; 13128c2ecf20Sopenharmony_ci } 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci list_for_each_entry(c, &gi->cdev.configs, list) { 13168c2ecf20Sopenharmony_ci struct config_usb_cfg *cfg; 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci cfg = container_of(c, struct config_usb_cfg, c); 13198c2ecf20Sopenharmony_ci if (list_empty(&cfg->func_list)) { 13208c2ecf20Sopenharmony_ci pr_err("Config %s/%d of %s needs at least one function.\n", 13218c2ecf20Sopenharmony_ci c->label, c->bConfigurationValue, 13228c2ecf20Sopenharmony_ci gi->composite.name); 13238c2ecf20Sopenharmony_ci goto err_comp_cleanup; 13248c2ecf20Sopenharmony_ci } 13258c2ecf20Sopenharmony_ci } 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci /* init all strings */ 13288c2ecf20Sopenharmony_ci if (!list_empty(&gi->string_list)) { 13298c2ecf20Sopenharmony_ci struct gadget_strings *gs; 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci i = 0; 13328c2ecf20Sopenharmony_ci list_for_each_entry(gs, &gi->string_list, list) { 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci gi->gstrings[i] = &gs->stringtab_dev; 13358c2ecf20Sopenharmony_ci gs->stringtab_dev.strings = gs->strings; 13368c2ecf20Sopenharmony_ci gs->strings[USB_GADGET_MANUFACTURER_IDX].s = 13378c2ecf20Sopenharmony_ci gs->manufacturer; 13388c2ecf20Sopenharmony_ci gs->strings[USB_GADGET_PRODUCT_IDX].s = gs->product; 13398c2ecf20Sopenharmony_ci gs->strings[USB_GADGET_SERIAL_IDX].s = gs->serialnumber; 13408c2ecf20Sopenharmony_ci i++; 13418c2ecf20Sopenharmony_ci } 13428c2ecf20Sopenharmony_ci gi->gstrings[i] = NULL; 13438c2ecf20Sopenharmony_ci s = usb_gstrings_attach(&gi->cdev, gi->gstrings, 13448c2ecf20Sopenharmony_ci USB_GADGET_FIRST_AVAIL_IDX); 13458c2ecf20Sopenharmony_ci if (IS_ERR(s)) { 13468c2ecf20Sopenharmony_ci ret = PTR_ERR(s); 13478c2ecf20Sopenharmony_ci goto err_comp_cleanup; 13488c2ecf20Sopenharmony_ci } 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci gi->cdev.desc.iManufacturer = s[USB_GADGET_MANUFACTURER_IDX].id; 13518c2ecf20Sopenharmony_ci gi->cdev.desc.iProduct = s[USB_GADGET_PRODUCT_IDX].id; 13528c2ecf20Sopenharmony_ci gi->cdev.desc.iSerialNumber = s[USB_GADGET_SERIAL_IDX].id; 13538c2ecf20Sopenharmony_ci } 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci if (gi->use_os_desc) { 13568c2ecf20Sopenharmony_ci cdev->use_os_string = true; 13578c2ecf20Sopenharmony_ci cdev->b_vendor_code = gi->b_vendor_code; 13588c2ecf20Sopenharmony_ci memcpy(cdev->qw_sign, gi->qw_sign, OS_STRING_QW_SIGN_LEN); 13598c2ecf20Sopenharmony_ci } 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci if (gadget_is_otg(gadget) && !otg_desc[0]) { 13628c2ecf20Sopenharmony_ci struct usb_descriptor_header *usb_desc; 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci usb_desc = usb_otg_descriptor_alloc(gadget); 13658c2ecf20Sopenharmony_ci if (!usb_desc) { 13668c2ecf20Sopenharmony_ci ret = -ENOMEM; 13678c2ecf20Sopenharmony_ci goto err_comp_cleanup; 13688c2ecf20Sopenharmony_ci } 13698c2ecf20Sopenharmony_ci usb_otg_descriptor_init(gadget, usb_desc); 13708c2ecf20Sopenharmony_ci otg_desc[0] = usb_desc; 13718c2ecf20Sopenharmony_ci otg_desc[1] = NULL; 13728c2ecf20Sopenharmony_ci } 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci /* Go through all configs, attach all functions */ 13758c2ecf20Sopenharmony_ci list_for_each_entry(c, &gi->cdev.configs, list) { 13768c2ecf20Sopenharmony_ci struct config_usb_cfg *cfg; 13778c2ecf20Sopenharmony_ci struct usb_function *f; 13788c2ecf20Sopenharmony_ci struct usb_function *tmp; 13798c2ecf20Sopenharmony_ci struct gadget_config_name *cn; 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci if (gadget_is_otg(gadget)) 13828c2ecf20Sopenharmony_ci c->descriptors = otg_desc; 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci cfg = container_of(c, struct config_usb_cfg, c); 13858c2ecf20Sopenharmony_ci if (!list_empty(&cfg->string_list)) { 13868c2ecf20Sopenharmony_ci i = 0; 13878c2ecf20Sopenharmony_ci list_for_each_entry(cn, &cfg->string_list, list) { 13888c2ecf20Sopenharmony_ci cfg->gstrings[i] = &cn->stringtab_dev; 13898c2ecf20Sopenharmony_ci cn->stringtab_dev.strings = &cn->strings; 13908c2ecf20Sopenharmony_ci cn->strings.s = cn->configuration; 13918c2ecf20Sopenharmony_ci i++; 13928c2ecf20Sopenharmony_ci } 13938c2ecf20Sopenharmony_ci cfg->gstrings[i] = NULL; 13948c2ecf20Sopenharmony_ci s = usb_gstrings_attach(&gi->cdev, cfg->gstrings, 1); 13958c2ecf20Sopenharmony_ci if (IS_ERR(s)) { 13968c2ecf20Sopenharmony_ci ret = PTR_ERR(s); 13978c2ecf20Sopenharmony_ci goto err_comp_cleanup; 13988c2ecf20Sopenharmony_ci } 13998c2ecf20Sopenharmony_ci c->iConfiguration = s[0].id; 14008c2ecf20Sopenharmony_ci } 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci list_for_each_entry_safe(f, tmp, &cfg->func_list, list) { 14038c2ecf20Sopenharmony_ci list_del(&f->list); 14048c2ecf20Sopenharmony_ci ret = usb_add_function(c, f); 14058c2ecf20Sopenharmony_ci if (ret) { 14068c2ecf20Sopenharmony_ci list_add(&f->list, &cfg->func_list); 14078c2ecf20Sopenharmony_ci goto err_purge_funcs; 14088c2ecf20Sopenharmony_ci } 14098c2ecf20Sopenharmony_ci } 14108c2ecf20Sopenharmony_ci usb_ep_autoconfig_reset(cdev->gadget); 14118c2ecf20Sopenharmony_ci } 14128c2ecf20Sopenharmony_ci if (cdev->use_os_string) { 14138c2ecf20Sopenharmony_ci ret = composite_os_desc_req_prepare(cdev, gadget->ep0); 14148c2ecf20Sopenharmony_ci if (ret) 14158c2ecf20Sopenharmony_ci goto err_purge_funcs; 14168c2ecf20Sopenharmony_ci } 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci usb_ep_autoconfig_reset(cdev->gadget); 14198c2ecf20Sopenharmony_ci return 0; 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_cierr_purge_funcs: 14228c2ecf20Sopenharmony_ci purge_configs_funcs(gi); 14238c2ecf20Sopenharmony_cierr_comp_cleanup: 14248c2ecf20Sopenharmony_ci composite_dev_cleanup(cdev); 14258c2ecf20Sopenharmony_ci return ret; 14268c2ecf20Sopenharmony_ci} 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_cistatic void configfs_composite_unbind(struct usb_gadget *gadget) 14298c2ecf20Sopenharmony_ci{ 14308c2ecf20Sopenharmony_ci struct usb_composite_dev *cdev; 14318c2ecf20Sopenharmony_ci struct gadget_info *gi; 14328c2ecf20Sopenharmony_ci unsigned long flags; 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci /* the gi->lock is hold by the caller */ 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci cdev = get_gadget_data(gadget); 14378c2ecf20Sopenharmony_ci gi = container_of(cdev, struct gadget_info, cdev); 14388c2ecf20Sopenharmony_ci spin_lock_irqsave(&gi->spinlock, flags); 14398c2ecf20Sopenharmony_ci gi->unbind = 1; 14408c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&gi->spinlock, flags); 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci kfree(otg_desc[0]); 14438c2ecf20Sopenharmony_ci otg_desc[0] = NULL; 14448c2ecf20Sopenharmony_ci purge_configs_funcs(gi); 14458c2ecf20Sopenharmony_ci composite_dev_cleanup(cdev); 14468c2ecf20Sopenharmony_ci usb_ep_autoconfig_reset(cdev->gadget); 14478c2ecf20Sopenharmony_ci spin_lock_irqsave(&gi->spinlock, flags); 14488c2ecf20Sopenharmony_ci cdev->gadget = NULL; 14498c2ecf20Sopenharmony_ci cdev->deactivations = 0; 14508c2ecf20Sopenharmony_ci gadget->deactivated = false; 14518c2ecf20Sopenharmony_ci set_gadget_data(gadget, NULL); 14528c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&gi->spinlock, flags); 14538c2ecf20Sopenharmony_ci} 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_cistatic int configfs_composite_setup(struct usb_gadget *gadget, 14568c2ecf20Sopenharmony_ci const struct usb_ctrlrequest *ctrl) 14578c2ecf20Sopenharmony_ci{ 14588c2ecf20Sopenharmony_ci struct usb_composite_dev *cdev; 14598c2ecf20Sopenharmony_ci struct gadget_info *gi; 14608c2ecf20Sopenharmony_ci unsigned long flags; 14618c2ecf20Sopenharmony_ci int ret; 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci cdev = get_gadget_data(gadget); 14648c2ecf20Sopenharmony_ci if (!cdev) 14658c2ecf20Sopenharmony_ci return 0; 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci gi = container_of(cdev, struct gadget_info, cdev); 14688c2ecf20Sopenharmony_ci spin_lock_irqsave(&gi->spinlock, flags); 14698c2ecf20Sopenharmony_ci cdev = get_gadget_data(gadget); 14708c2ecf20Sopenharmony_ci if (!cdev || gi->unbind) { 14718c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&gi->spinlock, flags); 14728c2ecf20Sopenharmony_ci return 0; 14738c2ecf20Sopenharmony_ci } 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci ret = composite_setup(gadget, ctrl); 14768c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&gi->spinlock, flags); 14778c2ecf20Sopenharmony_ci return ret; 14788c2ecf20Sopenharmony_ci} 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_cistatic void configfs_composite_disconnect(struct usb_gadget *gadget) 14818c2ecf20Sopenharmony_ci{ 14828c2ecf20Sopenharmony_ci struct usb_composite_dev *cdev; 14838c2ecf20Sopenharmony_ci struct gadget_info *gi; 14848c2ecf20Sopenharmony_ci unsigned long flags; 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci cdev = get_gadget_data(gadget); 14878c2ecf20Sopenharmony_ci if (!cdev) 14888c2ecf20Sopenharmony_ci return; 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci gi = container_of(cdev, struct gadget_info, cdev); 14918c2ecf20Sopenharmony_ci spin_lock_irqsave(&gi->spinlock, flags); 14928c2ecf20Sopenharmony_ci cdev = get_gadget_data(gadget); 14938c2ecf20Sopenharmony_ci if (!cdev || gi->unbind) { 14948c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&gi->spinlock, flags); 14958c2ecf20Sopenharmony_ci return; 14968c2ecf20Sopenharmony_ci } 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci composite_disconnect(gadget); 14998c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&gi->spinlock, flags); 15008c2ecf20Sopenharmony_ci} 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_cistatic void configfs_composite_suspend(struct usb_gadget *gadget) 15038c2ecf20Sopenharmony_ci{ 15048c2ecf20Sopenharmony_ci struct usb_composite_dev *cdev; 15058c2ecf20Sopenharmony_ci struct gadget_info *gi; 15068c2ecf20Sopenharmony_ci unsigned long flags; 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci cdev = get_gadget_data(gadget); 15098c2ecf20Sopenharmony_ci if (!cdev) 15108c2ecf20Sopenharmony_ci return; 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci gi = container_of(cdev, struct gadget_info, cdev); 15138c2ecf20Sopenharmony_ci spin_lock_irqsave(&gi->spinlock, flags); 15148c2ecf20Sopenharmony_ci cdev = get_gadget_data(gadget); 15158c2ecf20Sopenharmony_ci if (!cdev || gi->unbind) { 15168c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&gi->spinlock, flags); 15178c2ecf20Sopenharmony_ci return; 15188c2ecf20Sopenharmony_ci } 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci composite_suspend(gadget); 15218c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&gi->spinlock, flags); 15228c2ecf20Sopenharmony_ci} 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_cistatic void configfs_composite_resume(struct usb_gadget *gadget) 15258c2ecf20Sopenharmony_ci{ 15268c2ecf20Sopenharmony_ci struct usb_composite_dev *cdev; 15278c2ecf20Sopenharmony_ci struct gadget_info *gi; 15288c2ecf20Sopenharmony_ci unsigned long flags; 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci cdev = get_gadget_data(gadget); 15318c2ecf20Sopenharmony_ci if (!cdev) 15328c2ecf20Sopenharmony_ci return; 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci gi = container_of(cdev, struct gadget_info, cdev); 15358c2ecf20Sopenharmony_ci spin_lock_irqsave(&gi->spinlock, flags); 15368c2ecf20Sopenharmony_ci cdev = get_gadget_data(gadget); 15378c2ecf20Sopenharmony_ci if (!cdev || gi->unbind) { 15388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&gi->spinlock, flags); 15398c2ecf20Sopenharmony_ci return; 15408c2ecf20Sopenharmony_ci } 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci composite_resume(gadget); 15438c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&gi->spinlock, flags); 15448c2ecf20Sopenharmony_ci} 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_cistatic const struct usb_gadget_driver configfs_driver_template = { 15478c2ecf20Sopenharmony_ci .bind = configfs_composite_bind, 15488c2ecf20Sopenharmony_ci .unbind = configfs_composite_unbind, 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci .setup = configfs_composite_setup, 15518c2ecf20Sopenharmony_ci .reset = configfs_composite_disconnect, 15528c2ecf20Sopenharmony_ci .disconnect = configfs_composite_disconnect, 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci .suspend = configfs_composite_suspend, 15558c2ecf20Sopenharmony_ci .resume = configfs_composite_resume, 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci .max_speed = USB_SPEED_SUPER_PLUS, 15588c2ecf20Sopenharmony_ci .driver = { 15598c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 15608c2ecf20Sopenharmony_ci .name = "configfs-gadget", 15618c2ecf20Sopenharmony_ci }, 15628c2ecf20Sopenharmony_ci .match_existing_only = 1, 15638c2ecf20Sopenharmony_ci}; 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_cistatic struct config_group *gadgets_make( 15668c2ecf20Sopenharmony_ci struct config_group *group, 15678c2ecf20Sopenharmony_ci const char *name) 15688c2ecf20Sopenharmony_ci{ 15698c2ecf20Sopenharmony_ci struct gadget_info *gi; 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci gi = kzalloc(sizeof(*gi), GFP_KERNEL); 15728c2ecf20Sopenharmony_ci if (!gi) 15738c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci config_group_init_type_name(&gi->group, name, &gadget_root_type); 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci config_group_init_type_name(&gi->functions_group, "functions", 15788c2ecf20Sopenharmony_ci &functions_type); 15798c2ecf20Sopenharmony_ci configfs_add_default_group(&gi->functions_group, &gi->group); 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci config_group_init_type_name(&gi->configs_group, "configs", 15828c2ecf20Sopenharmony_ci &config_desc_type); 15838c2ecf20Sopenharmony_ci configfs_add_default_group(&gi->configs_group, &gi->group); 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci config_group_init_type_name(&gi->strings_group, "strings", 15868c2ecf20Sopenharmony_ci &gadget_strings_strings_type); 15878c2ecf20Sopenharmony_ci configfs_add_default_group(&gi->strings_group, &gi->group); 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci config_group_init_type_name(&gi->os_desc_group, "os_desc", 15908c2ecf20Sopenharmony_ci &os_desc_type); 15918c2ecf20Sopenharmony_ci configfs_add_default_group(&gi->os_desc_group, &gi->group); 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci gi->composite.bind = configfs_do_nothing; 15948c2ecf20Sopenharmony_ci gi->composite.unbind = configfs_do_nothing; 15958c2ecf20Sopenharmony_ci gi->composite.suspend = NULL; 15968c2ecf20Sopenharmony_ci gi->composite.resume = NULL; 15978c2ecf20Sopenharmony_ci gi->composite.max_speed = USB_SPEED_SUPER_PLUS; 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci spin_lock_init(&gi->spinlock); 16008c2ecf20Sopenharmony_ci mutex_init(&gi->lock); 16018c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&gi->string_list); 16028c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&gi->available_func); 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci composite_init_dev(&gi->cdev); 16058c2ecf20Sopenharmony_ci gi->cdev.desc.bLength = USB_DT_DEVICE_SIZE; 16068c2ecf20Sopenharmony_ci gi->cdev.desc.bDescriptorType = USB_DT_DEVICE; 16078c2ecf20Sopenharmony_ci gi->cdev.desc.bcdDevice = cpu_to_le16(get_default_bcdDevice()); 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci gi->composite.gadget_driver = configfs_driver_template; 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci gi->composite.gadget_driver.function = kstrdup(name, GFP_KERNEL); 16128c2ecf20Sopenharmony_ci gi->composite.name = gi->composite.gadget_driver.function; 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci if (!gi->composite.gadget_driver.function) 16158c2ecf20Sopenharmony_ci goto err; 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci return &gi->group; 16188c2ecf20Sopenharmony_cierr: 16198c2ecf20Sopenharmony_ci kfree(gi); 16208c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 16218c2ecf20Sopenharmony_ci} 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_cistatic void gadgets_drop(struct config_group *group, struct config_item *item) 16248c2ecf20Sopenharmony_ci{ 16258c2ecf20Sopenharmony_ci config_item_put(item); 16268c2ecf20Sopenharmony_ci} 16278c2ecf20Sopenharmony_ci 16288c2ecf20Sopenharmony_cistatic struct configfs_group_operations gadgets_ops = { 16298c2ecf20Sopenharmony_ci .make_group = &gadgets_make, 16308c2ecf20Sopenharmony_ci .drop_item = &gadgets_drop, 16318c2ecf20Sopenharmony_ci}; 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_cistatic const struct config_item_type gadgets_type = { 16348c2ecf20Sopenharmony_ci .ct_group_ops = &gadgets_ops, 16358c2ecf20Sopenharmony_ci .ct_owner = THIS_MODULE, 16368c2ecf20Sopenharmony_ci}; 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_cistatic struct configfs_subsystem gadget_subsys = { 16398c2ecf20Sopenharmony_ci .su_group = { 16408c2ecf20Sopenharmony_ci .cg_item = { 16418c2ecf20Sopenharmony_ci .ci_namebuf = "usb_gadget", 16428c2ecf20Sopenharmony_ci .ci_type = &gadgets_type, 16438c2ecf20Sopenharmony_ci }, 16448c2ecf20Sopenharmony_ci }, 16458c2ecf20Sopenharmony_ci .su_mutex = __MUTEX_INITIALIZER(gadget_subsys.su_mutex), 16468c2ecf20Sopenharmony_ci}; 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_civoid unregister_gadget_item(struct config_item *item) 16498c2ecf20Sopenharmony_ci{ 16508c2ecf20Sopenharmony_ci struct gadget_info *gi = to_gadget_info(item); 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci mutex_lock(&gi->lock); 16538c2ecf20Sopenharmony_ci unregister_gadget(gi); 16548c2ecf20Sopenharmony_ci mutex_unlock(&gi->lock); 16558c2ecf20Sopenharmony_ci} 16568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(unregister_gadget_item); 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_cistatic int __init gadget_cfs_init(void) 16598c2ecf20Sopenharmony_ci{ 16608c2ecf20Sopenharmony_ci int ret; 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci config_group_init(&gadget_subsys.su_group); 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci ret = configfs_register_subsystem(&gadget_subsys); 16658c2ecf20Sopenharmony_ci return ret; 16668c2ecf20Sopenharmony_ci} 16678c2ecf20Sopenharmony_cimodule_init(gadget_cfs_init); 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_cistatic void __exit gadget_cfs_exit(void) 16708c2ecf20Sopenharmony_ci{ 16718c2ecf20Sopenharmony_ci configfs_unregister_subsystem(&gadget_subsys); 16728c2ecf20Sopenharmony_ci} 16738c2ecf20Sopenharmony_cimodule_exit(gadget_cfs_exit); 1674