18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * g_ffs.c -- user mode file system API for USB composite function controllers 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2010 Samsung Electronics 68c2ecf20Sopenharmony_ci * Author: Michal Nazarewicz <mina86@mina86.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "g_ffs: " fmt 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS 148c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci# if defined USB_ETH_RNDIS 178c2ecf20Sopenharmony_ci# undef USB_ETH_RNDIS 188c2ecf20Sopenharmony_ci# endif 198c2ecf20Sopenharmony_ci# ifdef CONFIG_USB_FUNCTIONFS_RNDIS 208c2ecf20Sopenharmony_ci# define USB_ETH_RNDIS y 218c2ecf20Sopenharmony_ci# endif 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci# include "u_ecm.h" 248c2ecf20Sopenharmony_ci# include "u_gether.h" 258c2ecf20Sopenharmony_ci# ifdef USB_ETH_RNDIS 268c2ecf20Sopenharmony_ci# include "u_rndis.h" 278c2ecf20Sopenharmony_ci# include "rndis.h" 288c2ecf20Sopenharmony_ci# endif 298c2ecf20Sopenharmony_ci# include "u_ether.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ciUSB_ETHERNET_MODULE_PARAMETERS(); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci# ifdef CONFIG_USB_FUNCTIONFS_ETH 348c2ecf20Sopenharmony_cistatic int eth_bind_config(struct usb_configuration *c); 358c2ecf20Sopenharmony_cistatic struct usb_function_instance *fi_ecm; 368c2ecf20Sopenharmony_cistatic struct usb_function *f_ecm; 378c2ecf20Sopenharmony_cistatic struct usb_function_instance *fi_geth; 388c2ecf20Sopenharmony_cistatic struct usb_function *f_geth; 398c2ecf20Sopenharmony_ci# endif 408c2ecf20Sopenharmony_ci# ifdef CONFIG_USB_FUNCTIONFS_RNDIS 418c2ecf20Sopenharmony_cistatic int bind_rndis_config(struct usb_configuration *c); 428c2ecf20Sopenharmony_cistatic struct usb_function_instance *fi_rndis; 438c2ecf20Sopenharmony_cistatic struct usb_function *f_rndis; 448c2ecf20Sopenharmony_ci# endif 458c2ecf20Sopenharmony_ci#endif 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#include "u_fs.h" 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define DRIVER_NAME "g_ffs" 508c2ecf20Sopenharmony_ci#define DRIVER_DESC "USB Function Filesystem" 518c2ecf20Sopenharmony_ci#define DRIVER_VERSION "24 Aug 2004" 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 548c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michal Nazarewicz"); 558c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define GFS_VENDOR_ID 0x1d6b /* Linux Foundation */ 588c2ecf20Sopenharmony_ci#define GFS_PRODUCT_ID 0x0105 /* FunctionFS Gadget */ 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define GFS_MAX_DEVS 10 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ciUSB_GADGET_COMPOSITE_OPTIONS(); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic struct usb_device_descriptor gfs_dev_desc = { 658c2ecf20Sopenharmony_ci .bLength = sizeof gfs_dev_desc, 668c2ecf20Sopenharmony_ci .bDescriptorType = USB_DT_DEVICE, 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci /* .bcdUSB = DYNAMIC */ 698c2ecf20Sopenharmony_ci .bDeviceClass = USB_CLASS_PER_INTERFACE, 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci .idVendor = cpu_to_le16(GFS_VENDOR_ID), 728c2ecf20Sopenharmony_ci .idProduct = cpu_to_le16(GFS_PRODUCT_ID), 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic char *func_names[GFS_MAX_DEVS]; 768c2ecf20Sopenharmony_cistatic unsigned int func_num; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cimodule_param_named(bDeviceClass, gfs_dev_desc.bDeviceClass, byte, 0644); 798c2ecf20Sopenharmony_ciMODULE_PARM_DESC(bDeviceClass, "USB Device class"); 808c2ecf20Sopenharmony_cimodule_param_named(bDeviceSubClass, gfs_dev_desc.bDeviceSubClass, byte, 0644); 818c2ecf20Sopenharmony_ciMODULE_PARM_DESC(bDeviceSubClass, "USB Device subclass"); 828c2ecf20Sopenharmony_cimodule_param_named(bDeviceProtocol, gfs_dev_desc.bDeviceProtocol, byte, 0644); 838c2ecf20Sopenharmony_ciMODULE_PARM_DESC(bDeviceProtocol, "USB Device protocol"); 848c2ecf20Sopenharmony_cimodule_param_array_named(functions, func_names, charp, &func_num, 0); 858c2ecf20Sopenharmony_ciMODULE_PARM_DESC(functions, "USB Functions list"); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic const struct usb_descriptor_header *gfs_otg_desc[2]; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* String IDs are assigned dynamically */ 908c2ecf20Sopenharmony_cistatic struct usb_string gfs_strings[] = { 918c2ecf20Sopenharmony_ci [USB_GADGET_MANUFACTURER_IDX].s = "", 928c2ecf20Sopenharmony_ci [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, 938c2ecf20Sopenharmony_ci [USB_GADGET_SERIAL_IDX].s = "", 948c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_FUNCTIONFS_RNDIS 958c2ecf20Sopenharmony_ci { .s = "FunctionFS + RNDIS" }, 968c2ecf20Sopenharmony_ci#endif 978c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_FUNCTIONFS_ETH 988c2ecf20Sopenharmony_ci { .s = "FunctionFS + ECM" }, 998c2ecf20Sopenharmony_ci#endif 1008c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_FUNCTIONFS_GENERIC 1018c2ecf20Sopenharmony_ci { .s = "FunctionFS" }, 1028c2ecf20Sopenharmony_ci#endif 1038c2ecf20Sopenharmony_ci { } /* end of list */ 1048c2ecf20Sopenharmony_ci}; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic struct usb_gadget_strings *gfs_dev_strings[] = { 1078c2ecf20Sopenharmony_ci &(struct usb_gadget_strings) { 1088c2ecf20Sopenharmony_ci .language = 0x0409, /* en-us */ 1098c2ecf20Sopenharmony_ci .strings = gfs_strings, 1108c2ecf20Sopenharmony_ci }, 1118c2ecf20Sopenharmony_ci NULL, 1128c2ecf20Sopenharmony_ci}; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistruct gfs_configuration { 1158c2ecf20Sopenharmony_ci struct usb_configuration c; 1168c2ecf20Sopenharmony_ci int (*eth)(struct usb_configuration *c); 1178c2ecf20Sopenharmony_ci int num; 1188c2ecf20Sopenharmony_ci}; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic struct gfs_configuration gfs_configurations[] = { 1218c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_FUNCTIONFS_RNDIS 1228c2ecf20Sopenharmony_ci { 1238c2ecf20Sopenharmony_ci .eth = bind_rndis_config, 1248c2ecf20Sopenharmony_ci }, 1258c2ecf20Sopenharmony_ci#endif 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_FUNCTIONFS_ETH 1288c2ecf20Sopenharmony_ci { 1298c2ecf20Sopenharmony_ci .eth = eth_bind_config, 1308c2ecf20Sopenharmony_ci }, 1318c2ecf20Sopenharmony_ci#endif 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_FUNCTIONFS_GENERIC 1348c2ecf20Sopenharmony_ci { 1358c2ecf20Sopenharmony_ci }, 1368c2ecf20Sopenharmony_ci#endif 1378c2ecf20Sopenharmony_ci}; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic void *functionfs_acquire_dev(struct ffs_dev *dev); 1408c2ecf20Sopenharmony_cistatic void functionfs_release_dev(struct ffs_dev *dev); 1418c2ecf20Sopenharmony_cistatic int functionfs_ready_callback(struct ffs_data *ffs); 1428c2ecf20Sopenharmony_cistatic void functionfs_closed_callback(struct ffs_data *ffs); 1438c2ecf20Sopenharmony_cistatic int gfs_bind(struct usb_composite_dev *cdev); 1448c2ecf20Sopenharmony_cistatic int gfs_unbind(struct usb_composite_dev *cdev); 1458c2ecf20Sopenharmony_cistatic int gfs_do_config(struct usb_configuration *c); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic struct usb_composite_driver gfs_driver = { 1498c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 1508c2ecf20Sopenharmony_ci .dev = &gfs_dev_desc, 1518c2ecf20Sopenharmony_ci .strings = gfs_dev_strings, 1528c2ecf20Sopenharmony_ci .max_speed = USB_SPEED_SUPER, 1538c2ecf20Sopenharmony_ci .bind = gfs_bind, 1548c2ecf20Sopenharmony_ci .unbind = gfs_unbind, 1558c2ecf20Sopenharmony_ci}; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic unsigned int missing_funcs; 1588c2ecf20Sopenharmony_cistatic bool gfs_registered; 1598c2ecf20Sopenharmony_cistatic bool gfs_single_func; 1608c2ecf20Sopenharmony_cistatic struct usb_function_instance **fi_ffs; 1618c2ecf20Sopenharmony_cistatic struct usb_function **f_ffs[] = { 1628c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_FUNCTIONFS_RNDIS 1638c2ecf20Sopenharmony_ci NULL, 1648c2ecf20Sopenharmony_ci#endif 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_FUNCTIONFS_ETH 1678c2ecf20Sopenharmony_ci NULL, 1688c2ecf20Sopenharmony_ci#endif 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_FUNCTIONFS_GENERIC 1718c2ecf20Sopenharmony_ci NULL, 1728c2ecf20Sopenharmony_ci#endif 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci#define N_CONF ARRAY_SIZE(f_ffs) 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic int __init gfs_init(void) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci struct f_fs_opts *opts; 1808c2ecf20Sopenharmony_ci int i; 1818c2ecf20Sopenharmony_ci int ret = 0; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci ENTER(); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (func_num < 2) { 1868c2ecf20Sopenharmony_ci gfs_single_func = true; 1878c2ecf20Sopenharmony_ci func_num = 1; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* 1918c2ecf20Sopenharmony_ci * Allocate in one chunk for easier maintenance 1928c2ecf20Sopenharmony_ci */ 1938c2ecf20Sopenharmony_ci f_ffs[0] = kcalloc(func_num * N_CONF, sizeof(*f_ffs), GFP_KERNEL); 1948c2ecf20Sopenharmony_ci if (!f_ffs[0]) { 1958c2ecf20Sopenharmony_ci ret = -ENOMEM; 1968c2ecf20Sopenharmony_ci goto no_func; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci for (i = 1; i < N_CONF; ++i) 1998c2ecf20Sopenharmony_ci f_ffs[i] = f_ffs[0] + i * func_num; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci fi_ffs = kcalloc(func_num, sizeof(*fi_ffs), GFP_KERNEL); 2028c2ecf20Sopenharmony_ci if (!fi_ffs) { 2038c2ecf20Sopenharmony_ci ret = -ENOMEM; 2048c2ecf20Sopenharmony_ci goto no_func; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci for (i = 0; i < func_num; i++) { 2088c2ecf20Sopenharmony_ci fi_ffs[i] = usb_get_function_instance("ffs"); 2098c2ecf20Sopenharmony_ci if (IS_ERR(fi_ffs[i])) { 2108c2ecf20Sopenharmony_ci ret = PTR_ERR(fi_ffs[i]); 2118c2ecf20Sopenharmony_ci --i; 2128c2ecf20Sopenharmony_ci goto no_dev; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci opts = to_f_fs_opts(fi_ffs[i]); 2158c2ecf20Sopenharmony_ci if (gfs_single_func) 2168c2ecf20Sopenharmony_ci ret = ffs_single_dev(opts->dev); 2178c2ecf20Sopenharmony_ci else 2188c2ecf20Sopenharmony_ci ret = ffs_name_dev(opts->dev, func_names[i]); 2198c2ecf20Sopenharmony_ci if (ret) 2208c2ecf20Sopenharmony_ci goto no_dev; 2218c2ecf20Sopenharmony_ci opts->dev->ffs_ready_callback = functionfs_ready_callback; 2228c2ecf20Sopenharmony_ci opts->dev->ffs_closed_callback = functionfs_closed_callback; 2238c2ecf20Sopenharmony_ci opts->dev->ffs_acquire_dev_callback = functionfs_acquire_dev; 2248c2ecf20Sopenharmony_ci opts->dev->ffs_release_dev_callback = functionfs_release_dev; 2258c2ecf20Sopenharmony_ci opts->no_configfs = true; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci missing_funcs = func_num; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci return 0; 2318c2ecf20Sopenharmony_cino_dev: 2328c2ecf20Sopenharmony_ci while (i >= 0) 2338c2ecf20Sopenharmony_ci usb_put_function_instance(fi_ffs[i--]); 2348c2ecf20Sopenharmony_ci kfree(fi_ffs); 2358c2ecf20Sopenharmony_cino_func: 2368c2ecf20Sopenharmony_ci kfree(f_ffs[0]); 2378c2ecf20Sopenharmony_ci return ret; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_cimodule_init(gfs_init); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic void __exit gfs_exit(void) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci int i; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci ENTER(); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (gfs_registered) 2488c2ecf20Sopenharmony_ci usb_composite_unregister(&gfs_driver); 2498c2ecf20Sopenharmony_ci gfs_registered = false; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci kfree(f_ffs[0]); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci for (i = 0; i < func_num; i++) 2548c2ecf20Sopenharmony_ci usb_put_function_instance(fi_ffs[i]); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci kfree(fi_ffs); 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_cimodule_exit(gfs_exit); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic void *functionfs_acquire_dev(struct ffs_dev *dev) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci if (!try_module_get(THIS_MODULE)) 2638c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci return NULL; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic void functionfs_release_dev(struct ffs_dev *dev) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci module_put(THIS_MODULE); 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci/* 2748c2ecf20Sopenharmony_ci * The caller of this function takes ffs_lock 2758c2ecf20Sopenharmony_ci */ 2768c2ecf20Sopenharmony_cistatic int functionfs_ready_callback(struct ffs_data *ffs) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci int ret = 0; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (--missing_funcs) 2818c2ecf20Sopenharmony_ci return 0; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (gfs_registered) 2848c2ecf20Sopenharmony_ci return -EBUSY; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci gfs_registered = true; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci ret = usb_composite_probe(&gfs_driver); 2898c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) { 2908c2ecf20Sopenharmony_ci ++missing_funcs; 2918c2ecf20Sopenharmony_ci gfs_registered = false; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return ret; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci/* 2988c2ecf20Sopenharmony_ci * The caller of this function takes ffs_lock 2998c2ecf20Sopenharmony_ci */ 3008c2ecf20Sopenharmony_cistatic void functionfs_closed_callback(struct ffs_data *ffs) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci missing_funcs++; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (gfs_registered) 3058c2ecf20Sopenharmony_ci usb_composite_unregister(&gfs_driver); 3068c2ecf20Sopenharmony_ci gfs_registered = false; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci/* 3108c2ecf20Sopenharmony_ci * It is assumed that gfs_bind is called from a context where ffs_lock is held 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_cistatic int gfs_bind(struct usb_composite_dev *cdev) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS 3158c2ecf20Sopenharmony_ci struct net_device *net; 3168c2ecf20Sopenharmony_ci#endif 3178c2ecf20Sopenharmony_ci int ret, i; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci ENTER(); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci if (missing_funcs) 3228c2ecf20Sopenharmony_ci return -ENODEV; 3238c2ecf20Sopenharmony_ci#if defined CONFIG_USB_FUNCTIONFS_ETH 3248c2ecf20Sopenharmony_ci if (can_support_ecm(cdev->gadget)) { 3258c2ecf20Sopenharmony_ci struct f_ecm_opts *ecm_opts; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci fi_ecm = usb_get_function_instance("ecm"); 3288c2ecf20Sopenharmony_ci if (IS_ERR(fi_ecm)) 3298c2ecf20Sopenharmony_ci return PTR_ERR(fi_ecm); 3308c2ecf20Sopenharmony_ci ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst); 3318c2ecf20Sopenharmony_ci net = ecm_opts->net; 3328c2ecf20Sopenharmony_ci } else { 3338c2ecf20Sopenharmony_ci struct f_gether_opts *geth_opts; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci fi_geth = usb_get_function_instance("geth"); 3368c2ecf20Sopenharmony_ci if (IS_ERR(fi_geth)) 3378c2ecf20Sopenharmony_ci return PTR_ERR(fi_geth); 3388c2ecf20Sopenharmony_ci geth_opts = container_of(fi_geth, struct f_gether_opts, 3398c2ecf20Sopenharmony_ci func_inst); 3408c2ecf20Sopenharmony_ci net = geth_opts->net; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci#endif 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_FUNCTIONFS_RNDIS 3458c2ecf20Sopenharmony_ci { 3468c2ecf20Sopenharmony_ci fi_rndis = usb_get_function_instance("rndis"); 3478c2ecf20Sopenharmony_ci if (IS_ERR(fi_rndis)) { 3488c2ecf20Sopenharmony_ci ret = PTR_ERR(fi_rndis); 3498c2ecf20Sopenharmony_ci goto error; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci#ifndef CONFIG_USB_FUNCTIONFS_ETH 3528c2ecf20Sopenharmony_ci net = container_of(fi_rndis, struct f_rndis_opts, 3538c2ecf20Sopenharmony_ci func_inst)->net; 3548c2ecf20Sopenharmony_ci#endif 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci#endif 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS 3598c2ecf20Sopenharmony_ci gether_set_qmult(net, qmult); 3608c2ecf20Sopenharmony_ci if (!gether_set_host_addr(net, host_addr)) 3618c2ecf20Sopenharmony_ci pr_info("using host ethernet address: %s", host_addr); 3628c2ecf20Sopenharmony_ci if (!gether_set_dev_addr(net, dev_addr)) 3638c2ecf20Sopenharmony_ci pr_info("using self ethernet address: %s", dev_addr); 3648c2ecf20Sopenharmony_ci#endif 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci#if defined CONFIG_USB_FUNCTIONFS_RNDIS && defined CONFIG_USB_FUNCTIONFS_ETH 3678c2ecf20Sopenharmony_ci gether_set_gadget(net, cdev->gadget); 3688c2ecf20Sopenharmony_ci ret = gether_register_netdev(net); 3698c2ecf20Sopenharmony_ci if (ret) 3708c2ecf20Sopenharmony_ci goto error_rndis; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (can_support_ecm(cdev->gadget)) { 3738c2ecf20Sopenharmony_ci struct f_ecm_opts *ecm_opts; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst); 3768c2ecf20Sopenharmony_ci ecm_opts->bound = true; 3778c2ecf20Sopenharmony_ci } else { 3788c2ecf20Sopenharmony_ci struct f_gether_opts *geth_opts; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci geth_opts = container_of(fi_geth, struct f_gether_opts, 3818c2ecf20Sopenharmony_ci func_inst); 3828c2ecf20Sopenharmony_ci geth_opts->bound = true; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci rndis_borrow_net(fi_rndis, net); 3868c2ecf20Sopenharmony_ci#endif 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci /* TODO: gstrings_attach? */ 3898c2ecf20Sopenharmony_ci ret = usb_string_ids_tab(cdev, gfs_strings); 3908c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) 3918c2ecf20Sopenharmony_ci goto error_rndis; 3928c2ecf20Sopenharmony_ci gfs_dev_desc.iProduct = gfs_strings[USB_GADGET_PRODUCT_IDX].id; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (gadget_is_otg(cdev->gadget) && !gfs_otg_desc[0]) { 3958c2ecf20Sopenharmony_ci struct usb_descriptor_header *usb_desc; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci usb_desc = usb_otg_descriptor_alloc(cdev->gadget); 3988c2ecf20Sopenharmony_ci if (!usb_desc) 3998c2ecf20Sopenharmony_ci goto error_rndis; 4008c2ecf20Sopenharmony_ci usb_otg_descriptor_init(cdev->gadget, usb_desc); 4018c2ecf20Sopenharmony_ci gfs_otg_desc[0] = usb_desc; 4028c2ecf20Sopenharmony_ci gfs_otg_desc[1] = NULL; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(gfs_configurations); ++i) { 4068c2ecf20Sopenharmony_ci struct gfs_configuration *c = gfs_configurations + i; 4078c2ecf20Sopenharmony_ci int sid = USB_GADGET_FIRST_AVAIL_IDX + i; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci c->c.label = gfs_strings[sid].s; 4108c2ecf20Sopenharmony_ci c->c.iConfiguration = gfs_strings[sid].id; 4118c2ecf20Sopenharmony_ci c->c.bConfigurationValue = 1 + i; 4128c2ecf20Sopenharmony_ci c->c.bmAttributes = USB_CONFIG_ATT_SELFPOWER; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci c->num = i; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci ret = usb_add_config(cdev, &c->c, gfs_do_config); 4178c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) 4188c2ecf20Sopenharmony_ci goto error_unbind; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci usb_composite_overwrite_options(cdev, &coverwrite); 4218c2ecf20Sopenharmony_ci return 0; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci/* TODO */ 4248c2ecf20Sopenharmony_cierror_unbind: 4258c2ecf20Sopenharmony_ci kfree(gfs_otg_desc[0]); 4268c2ecf20Sopenharmony_ci gfs_otg_desc[0] = NULL; 4278c2ecf20Sopenharmony_cierror_rndis: 4288c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_FUNCTIONFS_RNDIS 4298c2ecf20Sopenharmony_ci usb_put_function_instance(fi_rndis); 4308c2ecf20Sopenharmony_cierror: 4318c2ecf20Sopenharmony_ci#endif 4328c2ecf20Sopenharmony_ci#if defined CONFIG_USB_FUNCTIONFS_ETH 4338c2ecf20Sopenharmony_ci if (can_support_ecm(cdev->gadget)) 4348c2ecf20Sopenharmony_ci usb_put_function_instance(fi_ecm); 4358c2ecf20Sopenharmony_ci else 4368c2ecf20Sopenharmony_ci usb_put_function_instance(fi_geth); 4378c2ecf20Sopenharmony_ci#endif 4388c2ecf20Sopenharmony_ci return ret; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci/* 4428c2ecf20Sopenharmony_ci * It is assumed that gfs_unbind is called from a context where ffs_lock is held 4438c2ecf20Sopenharmony_ci */ 4448c2ecf20Sopenharmony_cistatic int gfs_unbind(struct usb_composite_dev *cdev) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci int i; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci ENTER(); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_FUNCTIONFS_RNDIS 4528c2ecf20Sopenharmony_ci usb_put_function(f_rndis); 4538c2ecf20Sopenharmony_ci usb_put_function_instance(fi_rndis); 4548c2ecf20Sopenharmony_ci#endif 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci#if defined CONFIG_USB_FUNCTIONFS_ETH 4578c2ecf20Sopenharmony_ci if (can_support_ecm(cdev->gadget)) { 4588c2ecf20Sopenharmony_ci usb_put_function(f_ecm); 4598c2ecf20Sopenharmony_ci usb_put_function_instance(fi_ecm); 4608c2ecf20Sopenharmony_ci } else { 4618c2ecf20Sopenharmony_ci usb_put_function(f_geth); 4628c2ecf20Sopenharmony_ci usb_put_function_instance(fi_geth); 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci#endif 4658c2ecf20Sopenharmony_ci for (i = 0; i < N_CONF * func_num; ++i) 4668c2ecf20Sopenharmony_ci usb_put_function(*(f_ffs[0] + i)); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci kfree(gfs_otg_desc[0]); 4698c2ecf20Sopenharmony_ci gfs_otg_desc[0] = NULL; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci return 0; 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci/* 4758c2ecf20Sopenharmony_ci * It is assumed that gfs_do_config is called from a context where 4768c2ecf20Sopenharmony_ci * ffs_lock is held 4778c2ecf20Sopenharmony_ci */ 4788c2ecf20Sopenharmony_cistatic int gfs_do_config(struct usb_configuration *c) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci struct gfs_configuration *gc = 4818c2ecf20Sopenharmony_ci container_of(c, struct gfs_configuration, c); 4828c2ecf20Sopenharmony_ci int i; 4838c2ecf20Sopenharmony_ci int ret; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (missing_funcs) 4868c2ecf20Sopenharmony_ci return -ENODEV; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci if (gadget_is_otg(c->cdev->gadget)) { 4898c2ecf20Sopenharmony_ci c->descriptors = gfs_otg_desc; 4908c2ecf20Sopenharmony_ci c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci if (gc->eth) { 4948c2ecf20Sopenharmony_ci ret = gc->eth(c); 4958c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) 4968c2ecf20Sopenharmony_ci return ret; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci for (i = 0; i < func_num; i++) { 5008c2ecf20Sopenharmony_ci f_ffs[gc->num][i] = usb_get_function(fi_ffs[i]); 5018c2ecf20Sopenharmony_ci if (IS_ERR(f_ffs[gc->num][i])) { 5028c2ecf20Sopenharmony_ci ret = PTR_ERR(f_ffs[gc->num][i]); 5038c2ecf20Sopenharmony_ci goto error; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci ret = usb_add_function(c, f_ffs[gc->num][i]); 5068c2ecf20Sopenharmony_ci if (ret < 0) { 5078c2ecf20Sopenharmony_ci usb_put_function(f_ffs[gc->num][i]); 5088c2ecf20Sopenharmony_ci goto error; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci /* 5138c2ecf20Sopenharmony_ci * After previous do_configs there may be some invalid 5148c2ecf20Sopenharmony_ci * pointers in c->interface array. This happens every time 5158c2ecf20Sopenharmony_ci * a user space function with fewer interfaces than a user 5168c2ecf20Sopenharmony_ci * space function that was run before the new one is run. The 5178c2ecf20Sopenharmony_ci * compasit's set_config() assumes that if there is no more 5188c2ecf20Sopenharmony_ci * then MAX_CONFIG_INTERFACES interfaces in a configuration 5198c2ecf20Sopenharmony_ci * then there is a NULL pointer after the last interface in 5208c2ecf20Sopenharmony_ci * c->interface array. We need to make sure this is true. 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_ci if (c->next_interface_id < ARRAY_SIZE(c->interface)) 5238c2ecf20Sopenharmony_ci c->interface[c->next_interface_id] = NULL; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci return 0; 5268c2ecf20Sopenharmony_cierror: 5278c2ecf20Sopenharmony_ci while (--i >= 0) { 5288c2ecf20Sopenharmony_ci if (!IS_ERR(f_ffs[gc->num][i])) 5298c2ecf20Sopenharmony_ci usb_remove_function(c, f_ffs[gc->num][i]); 5308c2ecf20Sopenharmony_ci usb_put_function(f_ffs[gc->num][i]); 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci return ret; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_FUNCTIONFS_ETH 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_cistatic int eth_bind_config(struct usb_configuration *c) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci int status = 0; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci if (can_support_ecm(c->cdev->gadget)) { 5428c2ecf20Sopenharmony_ci f_ecm = usb_get_function(fi_ecm); 5438c2ecf20Sopenharmony_ci if (IS_ERR(f_ecm)) 5448c2ecf20Sopenharmony_ci return PTR_ERR(f_ecm); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci status = usb_add_function(c, f_ecm); 5478c2ecf20Sopenharmony_ci if (status < 0) 5488c2ecf20Sopenharmony_ci usb_put_function(f_ecm); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci } else { 5518c2ecf20Sopenharmony_ci f_geth = usb_get_function(fi_geth); 5528c2ecf20Sopenharmony_ci if (IS_ERR(f_geth)) 5538c2ecf20Sopenharmony_ci return PTR_ERR(f_geth); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci status = usb_add_function(c, f_geth); 5568c2ecf20Sopenharmony_ci if (status < 0) 5578c2ecf20Sopenharmony_ci usb_put_function(f_geth); 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci return status; 5608c2ecf20Sopenharmony_ci} 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci#endif 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_FUNCTIONFS_RNDIS 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic int bind_rndis_config(struct usb_configuration *c) 5678c2ecf20Sopenharmony_ci{ 5688c2ecf20Sopenharmony_ci int status = 0; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci f_rndis = usb_get_function(fi_rndis); 5718c2ecf20Sopenharmony_ci if (IS_ERR(f_rndis)) 5728c2ecf20Sopenharmony_ci return PTR_ERR(f_rndis); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci status = usb_add_function(c, f_rndis); 5758c2ecf20Sopenharmony_ci if (status < 0) 5768c2ecf20Sopenharmony_ci usb_put_function(f_rndis); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci return status; 5798c2ecf20Sopenharmony_ci} 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci#endif 582