162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * f_subset.c -- "CDC Subset" Ethernet link function driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2003-2005,2008 David Brownell 662306a36Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/device.h> 1362306a36Sopenharmony_ci#include <linux/etherdevice.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "u_ether.h" 1662306a36Sopenharmony_ci#include "u_ether_configfs.h" 1762306a36Sopenharmony_ci#include "u_gether.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * This function packages a simple "CDC Subset" Ethernet port with no real 2162306a36Sopenharmony_ci * control mechanisms; just raw data transfer over two bulk endpoints. 2262306a36Sopenharmony_ci * The data transfer model is exactly that of CDC Ethernet, which is 2362306a36Sopenharmony_ci * why we call it the "CDC Subset". 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * Because it's not standardized, this has some interoperability issues. 2662306a36Sopenharmony_ci * They mostly relate to driver binding, since the data transfer model is 2762306a36Sopenharmony_ci * so simple (CDC Ethernet). The original versions of this protocol used 2862306a36Sopenharmony_ci * specific product/vendor IDs: byteswapped IDs for Digital Equipment's 2962306a36Sopenharmony_ci * SA-1100 "Itsy" board, which could run Linux 2.4 kernels and supported 3062306a36Sopenharmony_ci * daughtercards with USB peripheral connectors. (It was used more often 3162306a36Sopenharmony_ci * with other boards, using the Itsy identifiers.) Linux hosts recognized 3262306a36Sopenharmony_ci * this with CONFIG_USB_ARMLINUX; these devices have only one configuration 3362306a36Sopenharmony_ci * and one interface. 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * At some point, MCCI defined a (nonconformant) CDC MDLM variant called 3662306a36Sopenharmony_ci * "SAFE", which happens to have a mode which is identical to the "CDC 3762306a36Sopenharmony_ci * Subset" in terms of data transfer and lack of control model. This was 3862306a36Sopenharmony_ci * adopted by later Sharp Zaurus models, and by some other software which 3962306a36Sopenharmony_ci * Linux hosts recognize with CONFIG_USB_NET_ZAURUS. 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * Because Microsoft's RNDIS drivers are far from robust, we added a few 4262306a36Sopenharmony_ci * descriptors to the CDC Subset code, making this code look like a SAFE 4362306a36Sopenharmony_ci * implementation. This lets you use MCCI's host side MS-Windows drivers 4462306a36Sopenharmony_ci * if you get fed up with RNDIS. It also makes it easier for composite 4562306a36Sopenharmony_ci * drivers to work, since they can use class based binding instead of 4662306a36Sopenharmony_ci * caring about specific product and vendor IDs. 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistruct f_gether { 5062306a36Sopenharmony_ci struct gether port; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci char ethaddr[14]; 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic inline struct f_gether *func_to_geth(struct usb_function *f) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci return container_of(f, struct f_gether, port.func); 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* 6362306a36Sopenharmony_ci * "Simple" CDC-subset option is a simple vendor-neutral model that most 6462306a36Sopenharmony_ci * full speed controllers can handle: one interface, two bulk endpoints. 6562306a36Sopenharmony_ci * To assist host side drivers, we fancy it up a bit, and add descriptors so 6662306a36Sopenharmony_ci * some host side drivers will understand it as a "SAFE" variant. 6762306a36Sopenharmony_ci * 6862306a36Sopenharmony_ci * "SAFE" loosely follows CDC WMC MDLM, violating the spec in various ways. 6962306a36Sopenharmony_ci * Data endpoints live in the control interface, there's no data interface. 7062306a36Sopenharmony_ci * And it's not used to talk to a cell phone radio. 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* interface descriptor: */ 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic struct usb_interface_descriptor subset_data_intf = { 7662306a36Sopenharmony_ci .bLength = sizeof subset_data_intf, 7762306a36Sopenharmony_ci .bDescriptorType = USB_DT_INTERFACE, 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* .bInterfaceNumber = DYNAMIC */ 8062306a36Sopenharmony_ci .bAlternateSetting = 0, 8162306a36Sopenharmony_ci .bNumEndpoints = 2, 8262306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_COMM, 8362306a36Sopenharmony_ci .bInterfaceSubClass = USB_CDC_SUBCLASS_MDLM, 8462306a36Sopenharmony_ci .bInterfaceProtocol = 0, 8562306a36Sopenharmony_ci /* .iInterface = DYNAMIC */ 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic struct usb_cdc_header_desc mdlm_header_desc = { 8962306a36Sopenharmony_ci .bLength = sizeof mdlm_header_desc, 9062306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 9162306a36Sopenharmony_ci .bDescriptorSubType = USB_CDC_HEADER_TYPE, 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci .bcdCDC = cpu_to_le16(0x0110), 9462306a36Sopenharmony_ci}; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic struct usb_cdc_mdlm_desc mdlm_desc = { 9762306a36Sopenharmony_ci .bLength = sizeof mdlm_desc, 9862306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 9962306a36Sopenharmony_ci .bDescriptorSubType = USB_CDC_MDLM_TYPE, 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci .bcdVersion = cpu_to_le16(0x0100), 10262306a36Sopenharmony_ci .bGUID = { 10362306a36Sopenharmony_ci 0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6, 10462306a36Sopenharmony_ci 0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f, 10562306a36Sopenharmony_ci }, 10662306a36Sopenharmony_ci}; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/* since "usb_cdc_mdlm_detail_desc" is a variable length structure, we 10962306a36Sopenharmony_ci * can't really use its struct. All we do here is say that we're using 11062306a36Sopenharmony_ci * the submode of "SAFE" which directly matches the CDC Subset. 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_cistatic u8 mdlm_detail_desc[] = { 11362306a36Sopenharmony_ci 6, 11462306a36Sopenharmony_ci USB_DT_CS_INTERFACE, 11562306a36Sopenharmony_ci USB_CDC_MDLM_DETAIL_TYPE, 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci 0, /* "SAFE" */ 11862306a36Sopenharmony_ci 0, /* network control capabilities (none) */ 11962306a36Sopenharmony_ci 0, /* network data capabilities ("raw" encapsulation) */ 12062306a36Sopenharmony_ci}; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic struct usb_cdc_ether_desc ether_desc = { 12362306a36Sopenharmony_ci .bLength = sizeof ether_desc, 12462306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 12562306a36Sopenharmony_ci .bDescriptorSubType = USB_CDC_ETHERNET_TYPE, 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* this descriptor actually adds value, surprise! */ 12862306a36Sopenharmony_ci /* .iMACAddress = DYNAMIC */ 12962306a36Sopenharmony_ci .bmEthernetStatistics = cpu_to_le32(0), /* no statistics */ 13062306a36Sopenharmony_ci .wMaxSegmentSize = cpu_to_le16(ETH_FRAME_LEN), 13162306a36Sopenharmony_ci .wNumberMCFilters = cpu_to_le16(0), 13262306a36Sopenharmony_ci .bNumberPowerFilters = 0, 13362306a36Sopenharmony_ci}; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* full speed support: */ 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic struct usb_endpoint_descriptor fs_subset_in_desc = { 13862306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 13962306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_IN, 14262306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 14362306a36Sopenharmony_ci}; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic struct usb_endpoint_descriptor fs_subset_out_desc = { 14662306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 14762306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_OUT, 15062306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 15162306a36Sopenharmony_ci}; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic struct usb_descriptor_header *fs_eth_function[] = { 15462306a36Sopenharmony_ci (struct usb_descriptor_header *) &subset_data_intf, 15562306a36Sopenharmony_ci (struct usb_descriptor_header *) &mdlm_header_desc, 15662306a36Sopenharmony_ci (struct usb_descriptor_header *) &mdlm_desc, 15762306a36Sopenharmony_ci (struct usb_descriptor_header *) &mdlm_detail_desc, 15862306a36Sopenharmony_ci (struct usb_descriptor_header *) ðer_desc, 15962306a36Sopenharmony_ci (struct usb_descriptor_header *) &fs_subset_in_desc, 16062306a36Sopenharmony_ci (struct usb_descriptor_header *) &fs_subset_out_desc, 16162306a36Sopenharmony_ci NULL, 16262306a36Sopenharmony_ci}; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci/* high speed support: */ 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic struct usb_endpoint_descriptor hs_subset_in_desc = { 16762306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 16862306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 17162306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(512), 17262306a36Sopenharmony_ci}; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic struct usb_endpoint_descriptor hs_subset_out_desc = { 17562306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 17662306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 17962306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(512), 18062306a36Sopenharmony_ci}; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic struct usb_descriptor_header *hs_eth_function[] = { 18362306a36Sopenharmony_ci (struct usb_descriptor_header *) &subset_data_intf, 18462306a36Sopenharmony_ci (struct usb_descriptor_header *) &mdlm_header_desc, 18562306a36Sopenharmony_ci (struct usb_descriptor_header *) &mdlm_desc, 18662306a36Sopenharmony_ci (struct usb_descriptor_header *) &mdlm_detail_desc, 18762306a36Sopenharmony_ci (struct usb_descriptor_header *) ðer_desc, 18862306a36Sopenharmony_ci (struct usb_descriptor_header *) &hs_subset_in_desc, 18962306a36Sopenharmony_ci (struct usb_descriptor_header *) &hs_subset_out_desc, 19062306a36Sopenharmony_ci NULL, 19162306a36Sopenharmony_ci}; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci/* super speed support: */ 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic struct usb_endpoint_descriptor ss_subset_in_desc = { 19662306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 19762306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 20062306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(1024), 20162306a36Sopenharmony_ci}; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic struct usb_endpoint_descriptor ss_subset_out_desc = { 20462306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 20562306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 20862306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(1024), 20962306a36Sopenharmony_ci}; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic struct usb_ss_ep_comp_descriptor ss_subset_bulk_comp_desc = { 21262306a36Sopenharmony_ci .bLength = sizeof ss_subset_bulk_comp_desc, 21362306a36Sopenharmony_ci .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* the following 2 values can be tweaked if necessary */ 21662306a36Sopenharmony_ci /* .bMaxBurst = 0, */ 21762306a36Sopenharmony_ci /* .bmAttributes = 0, */ 21862306a36Sopenharmony_ci}; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic struct usb_descriptor_header *ss_eth_function[] = { 22162306a36Sopenharmony_ci (struct usb_descriptor_header *) &subset_data_intf, 22262306a36Sopenharmony_ci (struct usb_descriptor_header *) &mdlm_header_desc, 22362306a36Sopenharmony_ci (struct usb_descriptor_header *) &mdlm_desc, 22462306a36Sopenharmony_ci (struct usb_descriptor_header *) &mdlm_detail_desc, 22562306a36Sopenharmony_ci (struct usb_descriptor_header *) ðer_desc, 22662306a36Sopenharmony_ci (struct usb_descriptor_header *) &ss_subset_in_desc, 22762306a36Sopenharmony_ci (struct usb_descriptor_header *) &ss_subset_bulk_comp_desc, 22862306a36Sopenharmony_ci (struct usb_descriptor_header *) &ss_subset_out_desc, 22962306a36Sopenharmony_ci (struct usb_descriptor_header *) &ss_subset_bulk_comp_desc, 23062306a36Sopenharmony_ci NULL, 23162306a36Sopenharmony_ci}; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci/* string descriptors: */ 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic struct usb_string geth_string_defs[] = { 23662306a36Sopenharmony_ci [0].s = "CDC Ethernet Subset/SAFE", 23762306a36Sopenharmony_ci [1].s = "", 23862306a36Sopenharmony_ci { } /* end of list */ 23962306a36Sopenharmony_ci}; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic struct usb_gadget_strings geth_string_table = { 24262306a36Sopenharmony_ci .language = 0x0409, /* en-us */ 24362306a36Sopenharmony_ci .strings = geth_string_defs, 24462306a36Sopenharmony_ci}; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic struct usb_gadget_strings *geth_strings[] = { 24762306a36Sopenharmony_ci &geth_string_table, 24862306a36Sopenharmony_ci NULL, 24962306a36Sopenharmony_ci}; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic int geth_set_alt(struct usb_function *f, unsigned intf, unsigned alt) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct f_gether *geth = func_to_geth(f); 25662306a36Sopenharmony_ci struct usb_composite_dev *cdev = f->config->cdev; 25762306a36Sopenharmony_ci struct net_device *net; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci /* we know alt == 0, so this is an activation or a reset */ 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (geth->port.in_ep->enabled) { 26262306a36Sopenharmony_ci DBG(cdev, "reset cdc subset\n"); 26362306a36Sopenharmony_ci gether_disconnect(&geth->port); 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci DBG(cdev, "init + activate cdc subset\n"); 26762306a36Sopenharmony_ci if (config_ep_by_speed(cdev->gadget, f, geth->port.in_ep) || 26862306a36Sopenharmony_ci config_ep_by_speed(cdev->gadget, f, geth->port.out_ep)) { 26962306a36Sopenharmony_ci geth->port.in_ep->desc = NULL; 27062306a36Sopenharmony_ci geth->port.out_ep->desc = NULL; 27162306a36Sopenharmony_ci return -EINVAL; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci net = gether_connect(&geth->port); 27562306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(net); 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic void geth_disable(struct usb_function *f) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci struct f_gether *geth = func_to_geth(f); 28162306a36Sopenharmony_ci struct usb_composite_dev *cdev = f->config->cdev; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci DBG(cdev, "net deactivated\n"); 28462306a36Sopenharmony_ci gether_disconnect(&geth->port); 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci/* serial function driver setup/binding */ 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic int 29262306a36Sopenharmony_cigeth_bind(struct usb_configuration *c, struct usb_function *f) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci struct usb_composite_dev *cdev = c->cdev; 29562306a36Sopenharmony_ci struct f_gether *geth = func_to_geth(f); 29662306a36Sopenharmony_ci struct usb_string *us; 29762306a36Sopenharmony_ci int status; 29862306a36Sopenharmony_ci struct usb_ep *ep; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci struct f_gether_opts *gether_opts; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci gether_opts = container_of(f->fi, struct f_gether_opts, func_inst); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci /* 30562306a36Sopenharmony_ci * in drivers/usb/gadget/configfs.c:configfs_composite_bind() 30662306a36Sopenharmony_ci * configurations are bound in sequence with list_for_each_entry, 30762306a36Sopenharmony_ci * in each configuration its functions are bound in sequence 30862306a36Sopenharmony_ci * with list_for_each_entry, so we assume no race condition 30962306a36Sopenharmony_ci * with regard to gether_opts->bound access 31062306a36Sopenharmony_ci */ 31162306a36Sopenharmony_ci if (!gether_opts->bound) { 31262306a36Sopenharmony_ci mutex_lock(&gether_opts->lock); 31362306a36Sopenharmony_ci gether_set_gadget(gether_opts->net, cdev->gadget); 31462306a36Sopenharmony_ci status = gether_register_netdev(gether_opts->net); 31562306a36Sopenharmony_ci mutex_unlock(&gether_opts->lock); 31662306a36Sopenharmony_ci if (status) 31762306a36Sopenharmony_ci return status; 31862306a36Sopenharmony_ci gether_opts->bound = true; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci us = usb_gstrings_attach(cdev, geth_strings, 32262306a36Sopenharmony_ci ARRAY_SIZE(geth_string_defs)); 32362306a36Sopenharmony_ci if (IS_ERR(us)) 32462306a36Sopenharmony_ci return PTR_ERR(us); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci subset_data_intf.iInterface = us[0].id; 32762306a36Sopenharmony_ci ether_desc.iMACAddress = us[1].id; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci /* allocate instance-specific interface IDs */ 33062306a36Sopenharmony_ci status = usb_interface_id(c, f); 33162306a36Sopenharmony_ci if (status < 0) 33262306a36Sopenharmony_ci goto fail; 33362306a36Sopenharmony_ci subset_data_intf.bInterfaceNumber = status; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci status = -ENODEV; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* allocate instance-specific endpoints */ 33862306a36Sopenharmony_ci ep = usb_ep_autoconfig(cdev->gadget, &fs_subset_in_desc); 33962306a36Sopenharmony_ci if (!ep) 34062306a36Sopenharmony_ci goto fail; 34162306a36Sopenharmony_ci geth->port.in_ep = ep; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci ep = usb_ep_autoconfig(cdev->gadget, &fs_subset_out_desc); 34462306a36Sopenharmony_ci if (!ep) 34562306a36Sopenharmony_ci goto fail; 34662306a36Sopenharmony_ci geth->port.out_ep = ep; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* support all relevant hardware speeds... we expect that when 34962306a36Sopenharmony_ci * hardware is dual speed, all bulk-capable endpoints work at 35062306a36Sopenharmony_ci * both speeds 35162306a36Sopenharmony_ci */ 35262306a36Sopenharmony_ci hs_subset_in_desc.bEndpointAddress = fs_subset_in_desc.bEndpointAddress; 35362306a36Sopenharmony_ci hs_subset_out_desc.bEndpointAddress = 35462306a36Sopenharmony_ci fs_subset_out_desc.bEndpointAddress; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci ss_subset_in_desc.bEndpointAddress = fs_subset_in_desc.bEndpointAddress; 35762306a36Sopenharmony_ci ss_subset_out_desc.bEndpointAddress = 35862306a36Sopenharmony_ci fs_subset_out_desc.bEndpointAddress; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci status = usb_assign_descriptors(f, fs_eth_function, hs_eth_function, 36162306a36Sopenharmony_ci ss_eth_function, ss_eth_function); 36262306a36Sopenharmony_ci if (status) 36362306a36Sopenharmony_ci goto fail; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci /* NOTE: all that is done without knowing or caring about 36662306a36Sopenharmony_ci * the network link ... which is unavailable to this code 36762306a36Sopenharmony_ci * until we're activated via set_alt(). 36862306a36Sopenharmony_ci */ 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci DBG(cdev, "CDC Subset: IN/%s OUT/%s\n", 37162306a36Sopenharmony_ci geth->port.in_ep->name, geth->port.out_ep->name); 37262306a36Sopenharmony_ci return 0; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cifail: 37562306a36Sopenharmony_ci ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci return status; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic inline struct f_gether_opts *to_f_gether_opts(struct config_item *item) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci return container_of(to_config_group(item), struct f_gether_opts, 38362306a36Sopenharmony_ci func_inst.group); 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci/* f_gether_item_ops */ 38762306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM(gether); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci/* f_gether_opts_dev_addr */ 39062306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(gether); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci/* f_gether_opts_host_addr */ 39362306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(gether); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci/* f_gether_opts_qmult */ 39662306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(gether); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci/* f_gether_opts_ifname */ 39962306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(gether); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic struct configfs_attribute *gether_attrs[] = { 40262306a36Sopenharmony_ci &gether_opts_attr_dev_addr, 40362306a36Sopenharmony_ci &gether_opts_attr_host_addr, 40462306a36Sopenharmony_ci &gether_opts_attr_qmult, 40562306a36Sopenharmony_ci &gether_opts_attr_ifname, 40662306a36Sopenharmony_ci NULL, 40762306a36Sopenharmony_ci}; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic const struct config_item_type gether_func_type = { 41062306a36Sopenharmony_ci .ct_item_ops = &gether_item_ops, 41162306a36Sopenharmony_ci .ct_attrs = gether_attrs, 41262306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 41362306a36Sopenharmony_ci}; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic void geth_free_inst(struct usb_function_instance *f) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct f_gether_opts *opts; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci opts = container_of(f, struct f_gether_opts, func_inst); 42062306a36Sopenharmony_ci if (opts->bound) 42162306a36Sopenharmony_ci gether_cleanup(netdev_priv(opts->net)); 42262306a36Sopenharmony_ci else 42362306a36Sopenharmony_ci free_netdev(opts->net); 42462306a36Sopenharmony_ci kfree(opts); 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic struct usb_function_instance *geth_alloc_inst(void) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci struct f_gether_opts *opts; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci opts = kzalloc(sizeof(*opts), GFP_KERNEL); 43262306a36Sopenharmony_ci if (!opts) 43362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 43462306a36Sopenharmony_ci mutex_init(&opts->lock); 43562306a36Sopenharmony_ci opts->func_inst.free_func_inst = geth_free_inst; 43662306a36Sopenharmony_ci opts->net = gether_setup_default(); 43762306a36Sopenharmony_ci if (IS_ERR(opts->net)) { 43862306a36Sopenharmony_ci struct net_device *net = opts->net; 43962306a36Sopenharmony_ci kfree(opts); 44062306a36Sopenharmony_ci return ERR_CAST(net); 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci config_group_init_type_name(&opts->func_inst.group, "", 44462306a36Sopenharmony_ci &gether_func_type); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci return &opts->func_inst; 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic void geth_free(struct usb_function *f) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci struct f_gether *eth; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci eth = func_to_geth(f); 45462306a36Sopenharmony_ci kfree(eth); 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic void geth_unbind(struct usb_configuration *c, struct usb_function *f) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci geth_string_defs[0].id = 0; 46062306a36Sopenharmony_ci usb_free_all_descriptors(f); 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic struct usb_function *geth_alloc(struct usb_function_instance *fi) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci struct f_gether *geth; 46662306a36Sopenharmony_ci struct f_gether_opts *opts; 46762306a36Sopenharmony_ci int status; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci /* allocate and initialize one new instance */ 47062306a36Sopenharmony_ci geth = kzalloc(sizeof(*geth), GFP_KERNEL); 47162306a36Sopenharmony_ci if (!geth) 47262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci opts = container_of(fi, struct f_gether_opts, func_inst); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci mutex_lock(&opts->lock); 47762306a36Sopenharmony_ci opts->refcnt++; 47862306a36Sopenharmony_ci /* export host's Ethernet address in CDC format */ 47962306a36Sopenharmony_ci status = gether_get_host_addr_cdc(opts->net, geth->ethaddr, 48062306a36Sopenharmony_ci sizeof(geth->ethaddr)); 48162306a36Sopenharmony_ci if (status < 12) { 48262306a36Sopenharmony_ci kfree(geth); 48362306a36Sopenharmony_ci mutex_unlock(&opts->lock); 48462306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci geth_string_defs[1].s = geth->ethaddr; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci geth->port.ioport = netdev_priv(opts->net); 48962306a36Sopenharmony_ci mutex_unlock(&opts->lock); 49062306a36Sopenharmony_ci geth->port.cdc_filter = DEFAULT_FILTER; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci geth->port.func.name = "cdc_subset"; 49362306a36Sopenharmony_ci geth->port.func.bind = geth_bind; 49462306a36Sopenharmony_ci geth->port.func.unbind = geth_unbind; 49562306a36Sopenharmony_ci geth->port.func.set_alt = geth_set_alt; 49662306a36Sopenharmony_ci geth->port.func.disable = geth_disable; 49762306a36Sopenharmony_ci geth->port.func.free_func = geth_free; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci return &geth->port.func; 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ciDECLARE_USB_FUNCTION_INIT(geth, geth_alloc_inst, geth_alloc); 50362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 50462306a36Sopenharmony_ciMODULE_AUTHOR("David Brownell"); 505