18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ether.c -- Ethernet gadget driver, with CDC and non-CDC options 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2003-2005,2008 David Brownell 68c2ecf20Sopenharmony_ci * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger 78c2ecf20Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci/* #define VERBOSE_DEBUG */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#if defined USB_ETH_RNDIS 168c2ecf20Sopenharmony_ci# undef USB_ETH_RNDIS 178c2ecf20Sopenharmony_ci#endif 188c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_ETH_RNDIS 198c2ecf20Sopenharmony_ci# define USB_ETH_RNDIS y 208c2ecf20Sopenharmony_ci#endif 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "u_ether.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* 268c2ecf20Sopenharmony_ci * Ethernet gadget driver -- with CDC and non-CDC options 278c2ecf20Sopenharmony_ci * Builds on hardware support for a full duplex link. 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * CDC Ethernet is the standard USB solution for sending Ethernet frames 308c2ecf20Sopenharmony_ci * using USB. Real hardware tends to use the same framing protocol but look 318c2ecf20Sopenharmony_ci * different for control features. This driver strongly prefers to use 328c2ecf20Sopenharmony_ci * this USB-IF standard as its open-systems interoperability solution; 338c2ecf20Sopenharmony_ci * most host side USB stacks (except from Microsoft) support it. 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * This is sometimes called "CDC ECM" (Ethernet Control Model) to support 368c2ecf20Sopenharmony_ci * TLA-soup. "CDC ACM" (Abstract Control Model) is for modems, and a new 378c2ecf20Sopenharmony_ci * "CDC EEM" (Ethernet Emulation Model) is starting to spread. 388c2ecf20Sopenharmony_ci * 398c2ecf20Sopenharmony_ci * There's some hardware that can't talk CDC ECM. We make that hardware 408c2ecf20Sopenharmony_ci * implement a "minimalist" vendor-agnostic CDC core: same framing, but 418c2ecf20Sopenharmony_ci * link-level setup only requires activating the configuration. Only the 428c2ecf20Sopenharmony_ci * endpoint descriptors, and product/vendor IDs, are relevant; no control 438c2ecf20Sopenharmony_ci * operations are available. Linux supports it, but other host operating 448c2ecf20Sopenharmony_ci * systems may not. (This is a subset of CDC Ethernet.) 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci * It turns out that if you add a few descriptors to that "CDC Subset", 478c2ecf20Sopenharmony_ci * (Windows) host side drivers from MCCI can treat it as one submode of 488c2ecf20Sopenharmony_ci * a proprietary scheme called "SAFE" ... without needing to know about 498c2ecf20Sopenharmony_ci * specific product/vendor IDs. So we do that, making it easier to use 508c2ecf20Sopenharmony_ci * those MS-Windows drivers. Those added descriptors make it resemble a 518c2ecf20Sopenharmony_ci * CDC MDLM device, but they don't change device behavior at all. (See 528c2ecf20Sopenharmony_ci * MCCI Engineering report 950198 "SAFE Networking Functions".) 538c2ecf20Sopenharmony_ci * 548c2ecf20Sopenharmony_ci * A third option is also in use. Rather than CDC Ethernet, or something 558c2ecf20Sopenharmony_ci * simpler, Microsoft pushes their own approach: RNDIS. The published 568c2ecf20Sopenharmony_ci * RNDIS specs are ambiguous and appear to be incomplete, and are also 578c2ecf20Sopenharmony_ci * needlessly complex. They borrow more from CDC ACM than CDC ECM. 588c2ecf20Sopenharmony_ci */ 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define DRIVER_DESC "Ethernet Gadget" 618c2ecf20Sopenharmony_ci#define DRIVER_VERSION "Memorial Day 2008" 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#ifdef USB_ETH_RNDIS 648c2ecf20Sopenharmony_ci#define PREFIX "RNDIS/" 658c2ecf20Sopenharmony_ci#else 668c2ecf20Sopenharmony_ci#define PREFIX "" 678c2ecf20Sopenharmony_ci#endif 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* 708c2ecf20Sopenharmony_ci * This driver aims for interoperability by using CDC ECM unless 718c2ecf20Sopenharmony_ci * 728c2ecf20Sopenharmony_ci * can_support_ecm() 738c2ecf20Sopenharmony_ci * 748c2ecf20Sopenharmony_ci * returns false, in which case it supports the CDC Subset. By default, 758c2ecf20Sopenharmony_ci * that returns true; most hardware has no problems with CDC ECM, that's 768c2ecf20Sopenharmony_ci * a good default. Previous versions of this driver had no default; this 778c2ecf20Sopenharmony_ci * version changes that, removing overhead for new controller support. 788c2ecf20Sopenharmony_ci * 798c2ecf20Sopenharmony_ci * IF YOUR HARDWARE CAN'T SUPPORT CDC ECM, UPDATE THAT ROUTINE! 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic inline bool has_rndis(void) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci#ifdef USB_ETH_RNDIS 858c2ecf20Sopenharmony_ci return true; 868c2ecf20Sopenharmony_ci#else 878c2ecf20Sopenharmony_ci return false; 888c2ecf20Sopenharmony_ci#endif 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci#include <linux/module.h> 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci#include "u_ecm.h" 948c2ecf20Sopenharmony_ci#include "u_gether.h" 958c2ecf20Sopenharmony_ci#ifdef USB_ETH_RNDIS 968c2ecf20Sopenharmony_ci#include "u_rndis.h" 978c2ecf20Sopenharmony_ci#include "rndis.h" 988c2ecf20Sopenharmony_ci#else 998c2ecf20Sopenharmony_ci#define rndis_borrow_net(...) do {} while (0) 1008c2ecf20Sopenharmony_ci#endif 1018c2ecf20Sopenharmony_ci#include "u_eem.h" 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 1048c2ecf20Sopenharmony_ciUSB_GADGET_COMPOSITE_OPTIONS(); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ciUSB_ETHERNET_MODULE_PARAMETERS(); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! 1098c2ecf20Sopenharmony_ci * Instead: allocate your own, using normal USB-IF procedures. 1108c2ecf20Sopenharmony_ci */ 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/* Thanks to NetChip Technologies for donating this product ID. 1138c2ecf20Sopenharmony_ci * It's for devices with only CDC Ethernet configurations. 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_ci#define CDC_VENDOR_NUM 0x0525 /* NetChip */ 1168c2ecf20Sopenharmony_ci#define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */ 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci/* For hardware that can't talk CDC, we use the same vendor ID that 1198c2ecf20Sopenharmony_ci * ARM Linux has used for ethernet-over-usb, both with sa1100 and 1208c2ecf20Sopenharmony_ci * with pxa250. We're protocol-compatible, if the host-side drivers 1218c2ecf20Sopenharmony_ci * use the endpoint descriptors. bcdDevice (version) is nonzero, so 1228c2ecf20Sopenharmony_ci * drivers that need to hard-wire endpoint numbers have a hook. 1238c2ecf20Sopenharmony_ci * 1248c2ecf20Sopenharmony_ci * The protocol is a minimal subset of CDC Ether, which works on any bulk 1258c2ecf20Sopenharmony_ci * hardware that's not deeply broken ... even on hardware that can't talk 1268c2ecf20Sopenharmony_ci * RNDIS (like SA-1100, with no interrupt endpoint, or anything that 1278c2ecf20Sopenharmony_ci * doesn't handle control-OUT). 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_ci#define SIMPLE_VENDOR_NUM 0x049f 1308c2ecf20Sopenharmony_ci#define SIMPLE_PRODUCT_NUM 0x505a 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci/* For hardware that can talk RNDIS and either of the above protocols, 1338c2ecf20Sopenharmony_ci * use this ID ... the windows INF files will know it. Unless it's 1348c2ecf20Sopenharmony_ci * used with CDC Ethernet, Linux 2.4 hosts will need updates to choose 1358c2ecf20Sopenharmony_ci * the non-RNDIS configuration. 1368c2ecf20Sopenharmony_ci */ 1378c2ecf20Sopenharmony_ci#define RNDIS_VENDOR_NUM 0x0525 /* NetChip */ 1388c2ecf20Sopenharmony_ci#define RNDIS_PRODUCT_NUM 0xa4a2 /* Ethernet/RNDIS Gadget */ 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci/* For EEM gadgets */ 1418c2ecf20Sopenharmony_ci#define EEM_VENDOR_NUM 0x1d6b /* Linux Foundation */ 1428c2ecf20Sopenharmony_ci#define EEM_PRODUCT_NUM 0x0102 /* EEM Gadget */ 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic struct usb_device_descriptor device_desc = { 1478c2ecf20Sopenharmony_ci .bLength = sizeof device_desc, 1488c2ecf20Sopenharmony_ci .bDescriptorType = USB_DT_DEVICE, 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* .bcdUSB = DYNAMIC */ 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci .bDeviceClass = USB_CLASS_COMM, 1538c2ecf20Sopenharmony_ci .bDeviceSubClass = 0, 1548c2ecf20Sopenharmony_ci .bDeviceProtocol = 0, 1558c2ecf20Sopenharmony_ci /* .bMaxPacketSize0 = f(hardware) */ 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* Vendor and product id defaults change according to what configs 1588c2ecf20Sopenharmony_ci * we support. (As does bNumConfigurations.) These values can 1598c2ecf20Sopenharmony_ci * also be overridden by module parameters. 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_ci .idVendor = cpu_to_le16 (CDC_VENDOR_NUM), 1628c2ecf20Sopenharmony_ci .idProduct = cpu_to_le16 (CDC_PRODUCT_NUM), 1638c2ecf20Sopenharmony_ci /* .bcdDevice = f(hardware) */ 1648c2ecf20Sopenharmony_ci /* .iManufacturer = DYNAMIC */ 1658c2ecf20Sopenharmony_ci /* .iProduct = DYNAMIC */ 1668c2ecf20Sopenharmony_ci /* NO SERIAL NUMBER */ 1678c2ecf20Sopenharmony_ci .bNumConfigurations = 1, 1688c2ecf20Sopenharmony_ci}; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic const struct usb_descriptor_header *otg_desc[2]; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic struct usb_string strings_dev[] = { 1738c2ecf20Sopenharmony_ci [USB_GADGET_MANUFACTURER_IDX].s = "", 1748c2ecf20Sopenharmony_ci [USB_GADGET_PRODUCT_IDX].s = PREFIX DRIVER_DESC, 1758c2ecf20Sopenharmony_ci [USB_GADGET_SERIAL_IDX].s = "", 1768c2ecf20Sopenharmony_ci { } /* end of list */ 1778c2ecf20Sopenharmony_ci}; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic struct usb_gadget_strings stringtab_dev = { 1808c2ecf20Sopenharmony_ci .language = 0x0409, /* en-us */ 1818c2ecf20Sopenharmony_ci .strings = strings_dev, 1828c2ecf20Sopenharmony_ci}; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic struct usb_gadget_strings *dev_strings[] = { 1858c2ecf20Sopenharmony_ci &stringtab_dev, 1868c2ecf20Sopenharmony_ci NULL, 1878c2ecf20Sopenharmony_ci}; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic struct usb_function_instance *fi_ecm; 1908c2ecf20Sopenharmony_cistatic struct usb_function *f_ecm; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic struct usb_function_instance *fi_eem; 1938c2ecf20Sopenharmony_cistatic struct usb_function *f_eem; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic struct usb_function_instance *fi_geth; 1968c2ecf20Sopenharmony_cistatic struct usb_function *f_geth; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic struct usb_function_instance *fi_rndis; 1998c2ecf20Sopenharmony_cistatic struct usb_function *f_rndis; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci/* 2048c2ecf20Sopenharmony_ci * We may not have an RNDIS configuration, but if we do it needs to be 2058c2ecf20Sopenharmony_ci * the first one present. That's to make Microsoft's drivers happy, 2068c2ecf20Sopenharmony_ci * and to follow DOCSIS 1.0 (cable modem standard). 2078c2ecf20Sopenharmony_ci */ 2088c2ecf20Sopenharmony_cistatic int rndis_do_config(struct usb_configuration *c) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci int status; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* FIXME alloc iConfiguration string, set it in c->strings */ 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (gadget_is_otg(c->cdev->gadget)) { 2158c2ecf20Sopenharmony_ci c->descriptors = otg_desc; 2168c2ecf20Sopenharmony_ci c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci f_rndis = usb_get_function(fi_rndis); 2208c2ecf20Sopenharmony_ci if (IS_ERR(f_rndis)) 2218c2ecf20Sopenharmony_ci return PTR_ERR(f_rndis); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci status = usb_add_function(c, f_rndis); 2248c2ecf20Sopenharmony_ci if (status < 0) 2258c2ecf20Sopenharmony_ci usb_put_function(f_rndis); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci return status; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic struct usb_configuration rndis_config_driver = { 2318c2ecf20Sopenharmony_ci .label = "RNDIS", 2328c2ecf20Sopenharmony_ci .bConfigurationValue = 2, 2338c2ecf20Sopenharmony_ci /* .iConfiguration = DYNAMIC */ 2348c2ecf20Sopenharmony_ci .bmAttributes = USB_CONFIG_ATT_SELFPOWER, 2358c2ecf20Sopenharmony_ci}; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_ETH_EEM 2408c2ecf20Sopenharmony_cistatic bool use_eem = 1; 2418c2ecf20Sopenharmony_ci#else 2428c2ecf20Sopenharmony_cistatic bool use_eem; 2438c2ecf20Sopenharmony_ci#endif 2448c2ecf20Sopenharmony_cimodule_param(use_eem, bool, 0); 2458c2ecf20Sopenharmony_ciMODULE_PARM_DESC(use_eem, "use CDC EEM mode"); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci/* 2488c2ecf20Sopenharmony_ci * We _always_ have an ECM, CDC Subset, or EEM configuration. 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_cistatic int eth_do_config(struct usb_configuration *c) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci int status = 0; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* FIXME alloc iConfiguration string, set it in c->strings */ 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (gadget_is_otg(c->cdev->gadget)) { 2578c2ecf20Sopenharmony_ci c->descriptors = otg_desc; 2588c2ecf20Sopenharmony_ci c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (use_eem) { 2628c2ecf20Sopenharmony_ci f_eem = usb_get_function(fi_eem); 2638c2ecf20Sopenharmony_ci if (IS_ERR(f_eem)) 2648c2ecf20Sopenharmony_ci return PTR_ERR(f_eem); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci status = usb_add_function(c, f_eem); 2678c2ecf20Sopenharmony_ci if (status < 0) 2688c2ecf20Sopenharmony_ci usb_put_function(f_eem); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci return status; 2718c2ecf20Sopenharmony_ci } else if (can_support_ecm(c->cdev->gadget)) { 2728c2ecf20Sopenharmony_ci f_ecm = usb_get_function(fi_ecm); 2738c2ecf20Sopenharmony_ci if (IS_ERR(f_ecm)) 2748c2ecf20Sopenharmony_ci return PTR_ERR(f_ecm); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci status = usb_add_function(c, f_ecm); 2778c2ecf20Sopenharmony_ci if (status < 0) 2788c2ecf20Sopenharmony_ci usb_put_function(f_ecm); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci return status; 2818c2ecf20Sopenharmony_ci } else { 2828c2ecf20Sopenharmony_ci f_geth = usb_get_function(fi_geth); 2838c2ecf20Sopenharmony_ci if (IS_ERR(f_geth)) 2848c2ecf20Sopenharmony_ci return PTR_ERR(f_geth); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci status = usb_add_function(c, f_geth); 2878c2ecf20Sopenharmony_ci if (status < 0) 2888c2ecf20Sopenharmony_ci usb_put_function(f_geth); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci return status; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic struct usb_configuration eth_config_driver = { 2968c2ecf20Sopenharmony_ci /* .label = f(hardware) */ 2978c2ecf20Sopenharmony_ci .bConfigurationValue = 1, 2988c2ecf20Sopenharmony_ci /* .iConfiguration = DYNAMIC */ 2998c2ecf20Sopenharmony_ci .bmAttributes = USB_CONFIG_ATT_SELFPOWER, 3008c2ecf20Sopenharmony_ci}; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic int eth_bind(struct usb_composite_dev *cdev) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci struct usb_gadget *gadget = cdev->gadget; 3078c2ecf20Sopenharmony_ci struct f_eem_opts *eem_opts = NULL; 3088c2ecf20Sopenharmony_ci struct f_ecm_opts *ecm_opts = NULL; 3098c2ecf20Sopenharmony_ci struct f_gether_opts *geth_opts = NULL; 3108c2ecf20Sopenharmony_ci struct net_device *net; 3118c2ecf20Sopenharmony_ci int status; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* set up main config label and device descriptor */ 3148c2ecf20Sopenharmony_ci if (use_eem) { 3158c2ecf20Sopenharmony_ci /* EEM */ 3168c2ecf20Sopenharmony_ci fi_eem = usb_get_function_instance("eem"); 3178c2ecf20Sopenharmony_ci if (IS_ERR(fi_eem)) 3188c2ecf20Sopenharmony_ci return PTR_ERR(fi_eem); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci eem_opts = container_of(fi_eem, struct f_eem_opts, func_inst); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci net = eem_opts->net; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci eth_config_driver.label = "CDC Ethernet (EEM)"; 3258c2ecf20Sopenharmony_ci device_desc.idVendor = cpu_to_le16(EEM_VENDOR_NUM); 3268c2ecf20Sopenharmony_ci device_desc.idProduct = cpu_to_le16(EEM_PRODUCT_NUM); 3278c2ecf20Sopenharmony_ci } else if (can_support_ecm(gadget)) { 3288c2ecf20Sopenharmony_ci /* ECM */ 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci fi_ecm = usb_get_function_instance("ecm"); 3318c2ecf20Sopenharmony_ci if (IS_ERR(fi_ecm)) 3328c2ecf20Sopenharmony_ci return PTR_ERR(fi_ecm); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci net = ecm_opts->net; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci eth_config_driver.label = "CDC Ethernet (ECM)"; 3398c2ecf20Sopenharmony_ci } else { 3408c2ecf20Sopenharmony_ci /* CDC Subset */ 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci fi_geth = usb_get_function_instance("geth"); 3438c2ecf20Sopenharmony_ci if (IS_ERR(fi_geth)) 3448c2ecf20Sopenharmony_ci return PTR_ERR(fi_geth); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci geth_opts = container_of(fi_geth, struct f_gether_opts, 3478c2ecf20Sopenharmony_ci func_inst); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci net = geth_opts->net; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci eth_config_driver.label = "CDC Subset/SAFE"; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci device_desc.idVendor = cpu_to_le16(SIMPLE_VENDOR_NUM); 3548c2ecf20Sopenharmony_ci device_desc.idProduct = cpu_to_le16(SIMPLE_PRODUCT_NUM); 3558c2ecf20Sopenharmony_ci if (!has_rndis()) 3568c2ecf20Sopenharmony_ci device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 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 3658c2ecf20Sopenharmony_ci if (has_rndis()) { 3668c2ecf20Sopenharmony_ci /* RNDIS plus ECM-or-Subset */ 3678c2ecf20Sopenharmony_ci gether_set_gadget(net, cdev->gadget); 3688c2ecf20Sopenharmony_ci status = gether_register_netdev(net); 3698c2ecf20Sopenharmony_ci if (status) 3708c2ecf20Sopenharmony_ci goto fail; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (use_eem) 3738c2ecf20Sopenharmony_ci eem_opts->bound = true; 3748c2ecf20Sopenharmony_ci else if (can_support_ecm(gadget)) 3758c2ecf20Sopenharmony_ci ecm_opts->bound = true; 3768c2ecf20Sopenharmony_ci else 3778c2ecf20Sopenharmony_ci geth_opts->bound = true; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci fi_rndis = usb_get_function_instance("rndis"); 3808c2ecf20Sopenharmony_ci if (IS_ERR(fi_rndis)) { 3818c2ecf20Sopenharmony_ci status = PTR_ERR(fi_rndis); 3828c2ecf20Sopenharmony_ci goto fail; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci rndis_borrow_net(fi_rndis, net); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci device_desc.idVendor = cpu_to_le16(RNDIS_VENDOR_NUM); 3888c2ecf20Sopenharmony_ci device_desc.idProduct = cpu_to_le16(RNDIS_PRODUCT_NUM); 3898c2ecf20Sopenharmony_ci device_desc.bNumConfigurations = 2; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci /* Allocate string descriptor numbers ... note that string 3938c2ecf20Sopenharmony_ci * contents can be overridden by the composite_dev glue. 3948c2ecf20Sopenharmony_ci */ 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci status = usb_string_ids_tab(cdev, strings_dev); 3978c2ecf20Sopenharmony_ci if (status < 0) 3988c2ecf20Sopenharmony_ci goto fail1; 3998c2ecf20Sopenharmony_ci device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; 4008c2ecf20Sopenharmony_ci device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (gadget_is_otg(gadget) && !otg_desc[0]) { 4038c2ecf20Sopenharmony_ci struct usb_descriptor_header *usb_desc; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci usb_desc = usb_otg_descriptor_alloc(gadget); 4068c2ecf20Sopenharmony_ci if (!usb_desc) { 4078c2ecf20Sopenharmony_ci status = -ENOMEM; 4088c2ecf20Sopenharmony_ci goto fail1; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci usb_otg_descriptor_init(gadget, usb_desc); 4118c2ecf20Sopenharmony_ci otg_desc[0] = usb_desc; 4128c2ecf20Sopenharmony_ci otg_desc[1] = NULL; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* register our configuration(s); RNDIS first, if it's used */ 4168c2ecf20Sopenharmony_ci if (has_rndis()) { 4178c2ecf20Sopenharmony_ci status = usb_add_config(cdev, &rndis_config_driver, 4188c2ecf20Sopenharmony_ci rndis_do_config); 4198c2ecf20Sopenharmony_ci if (status < 0) 4208c2ecf20Sopenharmony_ci goto fail2; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci status = usb_add_config(cdev, ð_config_driver, eth_do_config); 4248c2ecf20Sopenharmony_ci if (status < 0) 4258c2ecf20Sopenharmony_ci goto fail2; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci usb_composite_overwrite_options(cdev, &coverwrite); 4288c2ecf20Sopenharmony_ci dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n", 4298c2ecf20Sopenharmony_ci DRIVER_DESC); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci return 0; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cifail2: 4348c2ecf20Sopenharmony_ci kfree(otg_desc[0]); 4358c2ecf20Sopenharmony_ci otg_desc[0] = NULL; 4368c2ecf20Sopenharmony_cifail1: 4378c2ecf20Sopenharmony_ci if (has_rndis()) 4388c2ecf20Sopenharmony_ci usb_put_function_instance(fi_rndis); 4398c2ecf20Sopenharmony_cifail: 4408c2ecf20Sopenharmony_ci if (use_eem) 4418c2ecf20Sopenharmony_ci usb_put_function_instance(fi_eem); 4428c2ecf20Sopenharmony_ci else if (can_support_ecm(gadget)) 4438c2ecf20Sopenharmony_ci usb_put_function_instance(fi_ecm); 4448c2ecf20Sopenharmony_ci else 4458c2ecf20Sopenharmony_ci usb_put_function_instance(fi_geth); 4468c2ecf20Sopenharmony_ci return status; 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic int eth_unbind(struct usb_composite_dev *cdev) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci if (has_rndis()) { 4528c2ecf20Sopenharmony_ci usb_put_function(f_rndis); 4538c2ecf20Sopenharmony_ci usb_put_function_instance(fi_rndis); 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci if (use_eem) { 4568c2ecf20Sopenharmony_ci usb_put_function(f_eem); 4578c2ecf20Sopenharmony_ci usb_put_function_instance(fi_eem); 4588c2ecf20Sopenharmony_ci } else if (can_support_ecm(cdev->gadget)) { 4598c2ecf20Sopenharmony_ci usb_put_function(f_ecm); 4608c2ecf20Sopenharmony_ci usb_put_function_instance(fi_ecm); 4618c2ecf20Sopenharmony_ci } else { 4628c2ecf20Sopenharmony_ci usb_put_function(f_geth); 4638c2ecf20Sopenharmony_ci usb_put_function_instance(fi_geth); 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci kfree(otg_desc[0]); 4668c2ecf20Sopenharmony_ci otg_desc[0] = NULL; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci return 0; 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic struct usb_composite_driver eth_driver = { 4728c2ecf20Sopenharmony_ci .name = "g_ether", 4738c2ecf20Sopenharmony_ci .dev = &device_desc, 4748c2ecf20Sopenharmony_ci .strings = dev_strings, 4758c2ecf20Sopenharmony_ci .max_speed = USB_SPEED_SUPER, 4768c2ecf20Sopenharmony_ci .bind = eth_bind, 4778c2ecf20Sopenharmony_ci .unbind = eth_unbind, 4788c2ecf20Sopenharmony_ci}; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cimodule_usb_composite_driver(eth_driver); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(PREFIX DRIVER_DESC); 4838c2ecf20Sopenharmony_ciMODULE_AUTHOR("David Brownell, Benedikt Spanger"); 4848c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 485