162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * f_ecm.c -- USB CDC Ethernet (ECM) 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/* #define VERBOSE_DEBUG */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/device.h> 1562306a36Sopenharmony_ci#include <linux/etherdevice.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "u_ether.h" 1862306a36Sopenharmony_ci#include "u_ether_configfs.h" 1962306a36Sopenharmony_ci#include "u_ecm.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* 2362306a36Sopenharmony_ci * This function is a "CDC Ethernet Networking Control Model" (CDC ECM) 2462306a36Sopenharmony_ci * Ethernet link. The data transfer model is simple (packets sent and 2562306a36Sopenharmony_ci * received over bulk endpoints using normal short packet termination), 2662306a36Sopenharmony_ci * and the control model exposes various data and optional notifications. 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * ECM is well standardized and (except for Microsoft) supported by most 2962306a36Sopenharmony_ci * operating systems with USB host support. It's the preferred interop 3062306a36Sopenharmony_ci * solution for Ethernet over USB, at least for firmware based solutions. 3162306a36Sopenharmony_ci * (Hardware solutions tend to be more minimalist.) A newer and simpler 3262306a36Sopenharmony_ci * "Ethernet Emulation Model" (CDC EEM) hasn't yet caught on. 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci * Note that ECM requires the use of "alternate settings" for its data 3562306a36Sopenharmony_ci * interface. This means that the set_alt() method has real work to do, 3662306a36Sopenharmony_ci * and also means that a get_alt() method is required. 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cienum ecm_notify_state { 4162306a36Sopenharmony_ci ECM_NOTIFY_NONE, /* don't notify */ 4262306a36Sopenharmony_ci ECM_NOTIFY_CONNECT, /* issue CONNECT next */ 4362306a36Sopenharmony_ci ECM_NOTIFY_SPEED, /* issue SPEED_CHANGE next */ 4462306a36Sopenharmony_ci}; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistruct f_ecm { 4762306a36Sopenharmony_ci struct gether port; 4862306a36Sopenharmony_ci u8 ctrl_id, data_id; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci char ethaddr[14]; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci struct usb_ep *notify; 5362306a36Sopenharmony_ci struct usb_request *notify_req; 5462306a36Sopenharmony_ci u8 notify_state; 5562306a36Sopenharmony_ci atomic_t notify_count; 5662306a36Sopenharmony_ci bool is_open; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci /* FIXME is_open needs some irq-ish locking 5962306a36Sopenharmony_ci * ... possibly the same as port.ioport 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic inline struct f_ecm *func_to_ecm(struct usb_function *f) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci return container_of(f, struct f_ecm, port.func); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* 7162306a36Sopenharmony_ci * Include the status endpoint if we can, even though it's optional. 7262306a36Sopenharmony_ci * 7362306a36Sopenharmony_ci * Use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one 7462306a36Sopenharmony_ci * packet, to simplify cancellation; and a big transfer interval, to 7562306a36Sopenharmony_ci * waste less bandwidth. 7662306a36Sopenharmony_ci * 7762306a36Sopenharmony_ci * Some drivers (like Linux 2.4 cdc-ether!) "need" it to exist even 7862306a36Sopenharmony_ci * if they ignore the connect/disconnect notifications that real aether 7962306a36Sopenharmony_ci * can provide. More advanced cdc configurations might want to support 8062306a36Sopenharmony_ci * encapsulated commands (vendor-specific, using control-OUT). 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci#define ECM_STATUS_INTERVAL_MS 32 8462306a36Sopenharmony_ci#define ECM_STATUS_BYTECOUNT 16 /* 8 byte header + data */ 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/* interface descriptor: */ 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic struct usb_interface_assoc_descriptor 9062306a36Sopenharmony_ciecm_iad_descriptor = { 9162306a36Sopenharmony_ci .bLength = sizeof ecm_iad_descriptor, 9262306a36Sopenharmony_ci .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* .bFirstInterface = DYNAMIC, */ 9562306a36Sopenharmony_ci .bInterfaceCount = 2, /* control + data */ 9662306a36Sopenharmony_ci .bFunctionClass = USB_CLASS_COMM, 9762306a36Sopenharmony_ci .bFunctionSubClass = USB_CDC_SUBCLASS_ETHERNET, 9862306a36Sopenharmony_ci .bFunctionProtocol = USB_CDC_PROTO_NONE, 9962306a36Sopenharmony_ci /* .iFunction = DYNAMIC */ 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic struct usb_interface_descriptor ecm_control_intf = { 10462306a36Sopenharmony_ci .bLength = sizeof ecm_control_intf, 10562306a36Sopenharmony_ci .bDescriptorType = USB_DT_INTERFACE, 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* .bInterfaceNumber = DYNAMIC */ 10862306a36Sopenharmony_ci /* status endpoint is optional; this could be patched later */ 10962306a36Sopenharmony_ci .bNumEndpoints = 1, 11062306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_COMM, 11162306a36Sopenharmony_ci .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, 11262306a36Sopenharmony_ci .bInterfaceProtocol = USB_CDC_PROTO_NONE, 11362306a36Sopenharmony_ci /* .iInterface = DYNAMIC */ 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic struct usb_cdc_header_desc ecm_header_desc = { 11762306a36Sopenharmony_ci .bLength = sizeof ecm_header_desc, 11862306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 11962306a36Sopenharmony_ci .bDescriptorSubType = USB_CDC_HEADER_TYPE, 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci .bcdCDC = cpu_to_le16(0x0110), 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic struct usb_cdc_union_desc ecm_union_desc = { 12562306a36Sopenharmony_ci .bLength = sizeof(ecm_union_desc), 12662306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 12762306a36Sopenharmony_ci .bDescriptorSubType = USB_CDC_UNION_TYPE, 12862306a36Sopenharmony_ci /* .bMasterInterface0 = DYNAMIC */ 12962306a36Sopenharmony_ci /* .bSlaveInterface0 = DYNAMIC */ 13062306a36Sopenharmony_ci}; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic struct usb_cdc_ether_desc ecm_desc = { 13362306a36Sopenharmony_ci .bLength = sizeof ecm_desc, 13462306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 13562306a36Sopenharmony_ci .bDescriptorSubType = USB_CDC_ETHERNET_TYPE, 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* this descriptor actually adds value, surprise! */ 13862306a36Sopenharmony_ci /* .iMACAddress = DYNAMIC */ 13962306a36Sopenharmony_ci .bmEthernetStatistics = cpu_to_le32(0), /* no statistics */ 14062306a36Sopenharmony_ci .wMaxSegmentSize = cpu_to_le16(ETH_FRAME_LEN), 14162306a36Sopenharmony_ci .wNumberMCFilters = cpu_to_le16(0), 14262306a36Sopenharmony_ci .bNumberPowerFilters = 0, 14362306a36Sopenharmony_ci}; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci/* the default data interface has no endpoints ... */ 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic struct usb_interface_descriptor ecm_data_nop_intf = { 14862306a36Sopenharmony_ci .bLength = sizeof ecm_data_nop_intf, 14962306a36Sopenharmony_ci .bDescriptorType = USB_DT_INTERFACE, 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci .bInterfaceNumber = 1, 15262306a36Sopenharmony_ci .bAlternateSetting = 0, 15362306a36Sopenharmony_ci .bNumEndpoints = 0, 15462306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_CDC_DATA, 15562306a36Sopenharmony_ci .bInterfaceSubClass = 0, 15662306a36Sopenharmony_ci .bInterfaceProtocol = 0, 15762306a36Sopenharmony_ci /* .iInterface = DYNAMIC */ 15862306a36Sopenharmony_ci}; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci/* ... but the "real" data interface has two bulk endpoints */ 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic struct usb_interface_descriptor ecm_data_intf = { 16362306a36Sopenharmony_ci .bLength = sizeof ecm_data_intf, 16462306a36Sopenharmony_ci .bDescriptorType = USB_DT_INTERFACE, 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci .bInterfaceNumber = 1, 16762306a36Sopenharmony_ci .bAlternateSetting = 1, 16862306a36Sopenharmony_ci .bNumEndpoints = 2, 16962306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_CDC_DATA, 17062306a36Sopenharmony_ci .bInterfaceSubClass = 0, 17162306a36Sopenharmony_ci .bInterfaceProtocol = 0, 17262306a36Sopenharmony_ci /* .iInterface = DYNAMIC */ 17362306a36Sopenharmony_ci}; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci/* full speed support: */ 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic struct usb_endpoint_descriptor fs_ecm_notify_desc = { 17862306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 17962306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_IN, 18262306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_INT, 18362306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(ECM_STATUS_BYTECOUNT), 18462306a36Sopenharmony_ci .bInterval = ECM_STATUS_INTERVAL_MS, 18562306a36Sopenharmony_ci}; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic struct usb_endpoint_descriptor fs_ecm_in_desc = { 18862306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 18962306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_IN, 19262306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 19362306a36Sopenharmony_ci}; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic struct usb_endpoint_descriptor fs_ecm_out_desc = { 19662306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 19762306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_OUT, 20062306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 20162306a36Sopenharmony_ci}; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic struct usb_descriptor_header *ecm_fs_function[] = { 20462306a36Sopenharmony_ci /* CDC ECM control descriptors */ 20562306a36Sopenharmony_ci (struct usb_descriptor_header *) &ecm_iad_descriptor, 20662306a36Sopenharmony_ci (struct usb_descriptor_header *) &ecm_control_intf, 20762306a36Sopenharmony_ci (struct usb_descriptor_header *) &ecm_header_desc, 20862306a36Sopenharmony_ci (struct usb_descriptor_header *) &ecm_union_desc, 20962306a36Sopenharmony_ci (struct usb_descriptor_header *) &ecm_desc, 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* NOTE: status endpoint might need to be removed */ 21262306a36Sopenharmony_ci (struct usb_descriptor_header *) &fs_ecm_notify_desc, 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* data interface, altsettings 0 and 1 */ 21562306a36Sopenharmony_ci (struct usb_descriptor_header *) &ecm_data_nop_intf, 21662306a36Sopenharmony_ci (struct usb_descriptor_header *) &ecm_data_intf, 21762306a36Sopenharmony_ci (struct usb_descriptor_header *) &fs_ecm_in_desc, 21862306a36Sopenharmony_ci (struct usb_descriptor_header *) &fs_ecm_out_desc, 21962306a36Sopenharmony_ci NULL, 22062306a36Sopenharmony_ci}; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci/* high speed support: */ 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic struct usb_endpoint_descriptor hs_ecm_notify_desc = { 22562306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 22662306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_IN, 22962306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_INT, 23062306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(ECM_STATUS_BYTECOUNT), 23162306a36Sopenharmony_ci .bInterval = USB_MS_TO_HS_INTERVAL(ECM_STATUS_INTERVAL_MS), 23262306a36Sopenharmony_ci}; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic struct usb_endpoint_descriptor hs_ecm_in_desc = { 23562306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 23662306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_IN, 23962306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 24062306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(512), 24162306a36Sopenharmony_ci}; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic struct usb_endpoint_descriptor hs_ecm_out_desc = { 24462306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 24562306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_OUT, 24862306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 24962306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(512), 25062306a36Sopenharmony_ci}; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic struct usb_descriptor_header *ecm_hs_function[] = { 25362306a36Sopenharmony_ci /* CDC ECM control descriptors */ 25462306a36Sopenharmony_ci (struct usb_descriptor_header *) &ecm_iad_descriptor, 25562306a36Sopenharmony_ci (struct usb_descriptor_header *) &ecm_control_intf, 25662306a36Sopenharmony_ci (struct usb_descriptor_header *) &ecm_header_desc, 25762306a36Sopenharmony_ci (struct usb_descriptor_header *) &ecm_union_desc, 25862306a36Sopenharmony_ci (struct usb_descriptor_header *) &ecm_desc, 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* NOTE: status endpoint might need to be removed */ 26162306a36Sopenharmony_ci (struct usb_descriptor_header *) &hs_ecm_notify_desc, 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* data interface, altsettings 0 and 1 */ 26462306a36Sopenharmony_ci (struct usb_descriptor_header *) &ecm_data_nop_intf, 26562306a36Sopenharmony_ci (struct usb_descriptor_header *) &ecm_data_intf, 26662306a36Sopenharmony_ci (struct usb_descriptor_header *) &hs_ecm_in_desc, 26762306a36Sopenharmony_ci (struct usb_descriptor_header *) &hs_ecm_out_desc, 26862306a36Sopenharmony_ci NULL, 26962306a36Sopenharmony_ci}; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci/* super speed support: */ 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic struct usb_endpoint_descriptor ss_ecm_notify_desc = { 27462306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 27562306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_IN, 27862306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_INT, 27962306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(ECM_STATUS_BYTECOUNT), 28062306a36Sopenharmony_ci .bInterval = USB_MS_TO_HS_INTERVAL(ECM_STATUS_INTERVAL_MS), 28162306a36Sopenharmony_ci}; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic struct usb_ss_ep_comp_descriptor ss_ecm_intr_comp_desc = { 28462306a36Sopenharmony_ci .bLength = sizeof ss_ecm_intr_comp_desc, 28562306a36Sopenharmony_ci .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* the following 3 values can be tweaked if necessary */ 28862306a36Sopenharmony_ci /* .bMaxBurst = 0, */ 28962306a36Sopenharmony_ci /* .bmAttributes = 0, */ 29062306a36Sopenharmony_ci .wBytesPerInterval = cpu_to_le16(ECM_STATUS_BYTECOUNT), 29162306a36Sopenharmony_ci}; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic struct usb_endpoint_descriptor ss_ecm_in_desc = { 29462306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 29562306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_IN, 29862306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 29962306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(1024), 30062306a36Sopenharmony_ci}; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic struct usb_endpoint_descriptor ss_ecm_out_desc = { 30362306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 30462306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_OUT, 30762306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 30862306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(1024), 30962306a36Sopenharmony_ci}; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic struct usb_ss_ep_comp_descriptor ss_ecm_bulk_comp_desc = { 31262306a36Sopenharmony_ci .bLength = sizeof ss_ecm_bulk_comp_desc, 31362306a36Sopenharmony_ci .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* the following 2 values can be tweaked if necessary */ 31662306a36Sopenharmony_ci /* .bMaxBurst = 0, */ 31762306a36Sopenharmony_ci /* .bmAttributes = 0, */ 31862306a36Sopenharmony_ci}; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic struct usb_descriptor_header *ecm_ss_function[] = { 32162306a36Sopenharmony_ci /* CDC ECM control descriptors */ 32262306a36Sopenharmony_ci (struct usb_descriptor_header *) &ecm_iad_descriptor, 32362306a36Sopenharmony_ci (struct usb_descriptor_header *) &ecm_control_intf, 32462306a36Sopenharmony_ci (struct usb_descriptor_header *) &ecm_header_desc, 32562306a36Sopenharmony_ci (struct usb_descriptor_header *) &ecm_union_desc, 32662306a36Sopenharmony_ci (struct usb_descriptor_header *) &ecm_desc, 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* NOTE: status endpoint might need to be removed */ 32962306a36Sopenharmony_ci (struct usb_descriptor_header *) &ss_ecm_notify_desc, 33062306a36Sopenharmony_ci (struct usb_descriptor_header *) &ss_ecm_intr_comp_desc, 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci /* data interface, altsettings 0 and 1 */ 33362306a36Sopenharmony_ci (struct usb_descriptor_header *) &ecm_data_nop_intf, 33462306a36Sopenharmony_ci (struct usb_descriptor_header *) &ecm_data_intf, 33562306a36Sopenharmony_ci (struct usb_descriptor_header *) &ss_ecm_in_desc, 33662306a36Sopenharmony_ci (struct usb_descriptor_header *) &ss_ecm_bulk_comp_desc, 33762306a36Sopenharmony_ci (struct usb_descriptor_header *) &ss_ecm_out_desc, 33862306a36Sopenharmony_ci (struct usb_descriptor_header *) &ss_ecm_bulk_comp_desc, 33962306a36Sopenharmony_ci NULL, 34062306a36Sopenharmony_ci}; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci/* string descriptors: */ 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic struct usb_string ecm_string_defs[] = { 34562306a36Sopenharmony_ci [0].s = "CDC Ethernet Control Model (ECM)", 34662306a36Sopenharmony_ci [1].s = "", 34762306a36Sopenharmony_ci [2].s = "CDC Ethernet Data", 34862306a36Sopenharmony_ci [3].s = "CDC ECM", 34962306a36Sopenharmony_ci { } /* end of list */ 35062306a36Sopenharmony_ci}; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic struct usb_gadget_strings ecm_string_table = { 35362306a36Sopenharmony_ci .language = 0x0409, /* en-us */ 35462306a36Sopenharmony_ci .strings = ecm_string_defs, 35562306a36Sopenharmony_ci}; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic struct usb_gadget_strings *ecm_strings[] = { 35862306a36Sopenharmony_ci &ecm_string_table, 35962306a36Sopenharmony_ci NULL, 36062306a36Sopenharmony_ci}; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cistatic void ecm_do_notify(struct f_ecm *ecm) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci struct usb_request *req = ecm->notify_req; 36762306a36Sopenharmony_ci struct usb_cdc_notification *event; 36862306a36Sopenharmony_ci struct usb_composite_dev *cdev = ecm->port.func.config->cdev; 36962306a36Sopenharmony_ci __le32 *data; 37062306a36Sopenharmony_ci int status; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci /* notification already in flight? */ 37362306a36Sopenharmony_ci if (atomic_read(&ecm->notify_count)) 37462306a36Sopenharmony_ci return; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci event = req->buf; 37762306a36Sopenharmony_ci switch (ecm->notify_state) { 37862306a36Sopenharmony_ci case ECM_NOTIFY_NONE: 37962306a36Sopenharmony_ci return; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci case ECM_NOTIFY_CONNECT: 38262306a36Sopenharmony_ci event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION; 38362306a36Sopenharmony_ci if (ecm->is_open) 38462306a36Sopenharmony_ci event->wValue = cpu_to_le16(1); 38562306a36Sopenharmony_ci else 38662306a36Sopenharmony_ci event->wValue = cpu_to_le16(0); 38762306a36Sopenharmony_ci event->wLength = 0; 38862306a36Sopenharmony_ci req->length = sizeof *event; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci DBG(cdev, "notify connect %s\n", 39162306a36Sopenharmony_ci ecm->is_open ? "true" : "false"); 39262306a36Sopenharmony_ci ecm->notify_state = ECM_NOTIFY_SPEED; 39362306a36Sopenharmony_ci break; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci case ECM_NOTIFY_SPEED: 39662306a36Sopenharmony_ci event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE; 39762306a36Sopenharmony_ci event->wValue = cpu_to_le16(0); 39862306a36Sopenharmony_ci event->wLength = cpu_to_le16(8); 39962306a36Sopenharmony_ci req->length = ECM_STATUS_BYTECOUNT; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci /* SPEED_CHANGE data is up/down speeds in bits/sec */ 40262306a36Sopenharmony_ci data = req->buf + sizeof *event; 40362306a36Sopenharmony_ci data[0] = cpu_to_le32(gether_bitrate(cdev->gadget)); 40462306a36Sopenharmony_ci data[1] = data[0]; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci DBG(cdev, "notify speed %d\n", gether_bitrate(cdev->gadget)); 40762306a36Sopenharmony_ci ecm->notify_state = ECM_NOTIFY_NONE; 40862306a36Sopenharmony_ci break; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci event->bmRequestType = 0xA1; 41162306a36Sopenharmony_ci event->wIndex = cpu_to_le16(ecm->ctrl_id); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci atomic_inc(&ecm->notify_count); 41462306a36Sopenharmony_ci status = usb_ep_queue(ecm->notify, req, GFP_ATOMIC); 41562306a36Sopenharmony_ci if (status < 0) { 41662306a36Sopenharmony_ci atomic_dec(&ecm->notify_count); 41762306a36Sopenharmony_ci DBG(cdev, "notify --> %d\n", status); 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic void ecm_notify(struct f_ecm *ecm) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci /* NOTE on most versions of Linux, host side cdc-ethernet 42462306a36Sopenharmony_ci * won't listen for notifications until its netdevice opens. 42562306a36Sopenharmony_ci * The first notification then sits in the FIFO for a long 42662306a36Sopenharmony_ci * time, and the second one is queued. 42762306a36Sopenharmony_ci */ 42862306a36Sopenharmony_ci ecm->notify_state = ECM_NOTIFY_CONNECT; 42962306a36Sopenharmony_ci ecm_do_notify(ecm); 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic void ecm_notify_complete(struct usb_ep *ep, struct usb_request *req) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci struct f_ecm *ecm = req->context; 43562306a36Sopenharmony_ci struct usb_composite_dev *cdev = ecm->port.func.config->cdev; 43662306a36Sopenharmony_ci struct usb_cdc_notification *event = req->buf; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci switch (req->status) { 43962306a36Sopenharmony_ci case 0: 44062306a36Sopenharmony_ci /* no fault */ 44162306a36Sopenharmony_ci atomic_dec(&ecm->notify_count); 44262306a36Sopenharmony_ci break; 44362306a36Sopenharmony_ci case -ECONNRESET: 44462306a36Sopenharmony_ci case -ESHUTDOWN: 44562306a36Sopenharmony_ci atomic_set(&ecm->notify_count, 0); 44662306a36Sopenharmony_ci ecm->notify_state = ECM_NOTIFY_NONE; 44762306a36Sopenharmony_ci break; 44862306a36Sopenharmony_ci default: 44962306a36Sopenharmony_ci DBG(cdev, "event %02x --> %d\n", 45062306a36Sopenharmony_ci event->bNotificationType, req->status); 45162306a36Sopenharmony_ci atomic_dec(&ecm->notify_count); 45262306a36Sopenharmony_ci break; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci ecm_do_notify(ecm); 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic int ecm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci struct f_ecm *ecm = func_to_ecm(f); 46062306a36Sopenharmony_ci struct usb_composite_dev *cdev = f->config->cdev; 46162306a36Sopenharmony_ci struct usb_request *req = cdev->req; 46262306a36Sopenharmony_ci int value = -EOPNOTSUPP; 46362306a36Sopenharmony_ci u16 w_index = le16_to_cpu(ctrl->wIndex); 46462306a36Sopenharmony_ci u16 w_value = le16_to_cpu(ctrl->wValue); 46562306a36Sopenharmony_ci u16 w_length = le16_to_cpu(ctrl->wLength); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* composite driver infrastructure handles everything except 46862306a36Sopenharmony_ci * CDC class messages; interface activation uses set_alt(). 46962306a36Sopenharmony_ci */ 47062306a36Sopenharmony_ci switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { 47162306a36Sopenharmony_ci case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) 47262306a36Sopenharmony_ci | USB_CDC_SET_ETHERNET_PACKET_FILTER: 47362306a36Sopenharmony_ci /* see 6.2.30: no data, wIndex = interface, 47462306a36Sopenharmony_ci * wValue = packet filter bitmap 47562306a36Sopenharmony_ci */ 47662306a36Sopenharmony_ci if (w_length != 0 || w_index != ecm->ctrl_id) 47762306a36Sopenharmony_ci goto invalid; 47862306a36Sopenharmony_ci DBG(cdev, "packet filter %02x\n", w_value); 47962306a36Sopenharmony_ci /* REVISIT locking of cdc_filter. This assumes the UDC 48062306a36Sopenharmony_ci * driver won't have a concurrent packet TX irq running on 48162306a36Sopenharmony_ci * another CPU; or that if it does, this write is atomic... 48262306a36Sopenharmony_ci */ 48362306a36Sopenharmony_ci ecm->port.cdc_filter = w_value; 48462306a36Sopenharmony_ci value = 0; 48562306a36Sopenharmony_ci break; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci /* and optionally: 48862306a36Sopenharmony_ci * case USB_CDC_SEND_ENCAPSULATED_COMMAND: 48962306a36Sopenharmony_ci * case USB_CDC_GET_ENCAPSULATED_RESPONSE: 49062306a36Sopenharmony_ci * case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS: 49162306a36Sopenharmony_ci * case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER: 49262306a36Sopenharmony_ci * case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER: 49362306a36Sopenharmony_ci * case USB_CDC_GET_ETHERNET_STATISTIC: 49462306a36Sopenharmony_ci */ 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci default: 49762306a36Sopenharmony_ciinvalid: 49862306a36Sopenharmony_ci DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", 49962306a36Sopenharmony_ci ctrl->bRequestType, ctrl->bRequest, 50062306a36Sopenharmony_ci w_value, w_index, w_length); 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci /* respond with data transfer or status phase? */ 50462306a36Sopenharmony_ci if (value >= 0) { 50562306a36Sopenharmony_ci DBG(cdev, "ecm req%02x.%02x v%04x i%04x l%d\n", 50662306a36Sopenharmony_ci ctrl->bRequestType, ctrl->bRequest, 50762306a36Sopenharmony_ci w_value, w_index, w_length); 50862306a36Sopenharmony_ci req->zero = 0; 50962306a36Sopenharmony_ci req->length = value; 51062306a36Sopenharmony_ci value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); 51162306a36Sopenharmony_ci if (value < 0) 51262306a36Sopenharmony_ci ERROR(cdev, "ecm req %02x.%02x response err %d\n", 51362306a36Sopenharmony_ci ctrl->bRequestType, ctrl->bRequest, 51462306a36Sopenharmony_ci value); 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci /* device either stalls (value < 0) or reports success */ 51862306a36Sopenharmony_ci return value; 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic int ecm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci struct f_ecm *ecm = func_to_ecm(f); 52562306a36Sopenharmony_ci struct usb_composite_dev *cdev = f->config->cdev; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* Control interface has only altsetting 0 */ 52862306a36Sopenharmony_ci if (intf == ecm->ctrl_id) { 52962306a36Sopenharmony_ci if (alt != 0) 53062306a36Sopenharmony_ci goto fail; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci VDBG(cdev, "reset ecm control %d\n", intf); 53362306a36Sopenharmony_ci usb_ep_disable(ecm->notify); 53462306a36Sopenharmony_ci if (!(ecm->notify->desc)) { 53562306a36Sopenharmony_ci VDBG(cdev, "init ecm ctrl %d\n", intf); 53662306a36Sopenharmony_ci if (config_ep_by_speed(cdev->gadget, f, ecm->notify)) 53762306a36Sopenharmony_ci goto fail; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci usb_ep_enable(ecm->notify); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* Data interface has two altsettings, 0 and 1 */ 54262306a36Sopenharmony_ci } else if (intf == ecm->data_id) { 54362306a36Sopenharmony_ci if (alt > 1) 54462306a36Sopenharmony_ci goto fail; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (ecm->port.in_ep->enabled) { 54762306a36Sopenharmony_ci DBG(cdev, "reset ecm\n"); 54862306a36Sopenharmony_ci gether_disconnect(&ecm->port); 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci if (!ecm->port.in_ep->desc || 55262306a36Sopenharmony_ci !ecm->port.out_ep->desc) { 55362306a36Sopenharmony_ci DBG(cdev, "init ecm\n"); 55462306a36Sopenharmony_ci if (config_ep_by_speed(cdev->gadget, f, 55562306a36Sopenharmony_ci ecm->port.in_ep) || 55662306a36Sopenharmony_ci config_ep_by_speed(cdev->gadget, f, 55762306a36Sopenharmony_ci ecm->port.out_ep)) { 55862306a36Sopenharmony_ci ecm->port.in_ep->desc = NULL; 55962306a36Sopenharmony_ci ecm->port.out_ep->desc = NULL; 56062306a36Sopenharmony_ci goto fail; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci /* CDC Ethernet only sends data in non-default altsettings. 56562306a36Sopenharmony_ci * Changing altsettings resets filters, statistics, etc. 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_ci if (alt == 1) { 56862306a36Sopenharmony_ci struct net_device *net; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci /* Enable zlps by default for ECM conformance; 57162306a36Sopenharmony_ci * override for musb_hdrc (avoids txdma ovhead). 57262306a36Sopenharmony_ci */ 57362306a36Sopenharmony_ci ecm->port.is_zlp_ok = 57462306a36Sopenharmony_ci gadget_is_zlp_supported(cdev->gadget); 57562306a36Sopenharmony_ci ecm->port.cdc_filter = DEFAULT_FILTER; 57662306a36Sopenharmony_ci DBG(cdev, "activate ecm\n"); 57762306a36Sopenharmony_ci net = gether_connect(&ecm->port); 57862306a36Sopenharmony_ci if (IS_ERR(net)) 57962306a36Sopenharmony_ci return PTR_ERR(net); 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci /* NOTE this can be a minor disagreement with the ECM spec, 58362306a36Sopenharmony_ci * which says speed notifications will "always" follow 58462306a36Sopenharmony_ci * connection notifications. But we allow one connect to 58562306a36Sopenharmony_ci * follow another (if the first is in flight), and instead 58662306a36Sopenharmony_ci * just guarantee that a speed notification is always sent. 58762306a36Sopenharmony_ci */ 58862306a36Sopenharmony_ci ecm_notify(ecm); 58962306a36Sopenharmony_ci } else 59062306a36Sopenharmony_ci goto fail; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci return 0; 59362306a36Sopenharmony_cifail: 59462306a36Sopenharmony_ci return -EINVAL; 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci/* Because the data interface supports multiple altsettings, 59862306a36Sopenharmony_ci * this ECM function *MUST* implement a get_alt() method. 59962306a36Sopenharmony_ci */ 60062306a36Sopenharmony_cistatic int ecm_get_alt(struct usb_function *f, unsigned intf) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci struct f_ecm *ecm = func_to_ecm(f); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci if (intf == ecm->ctrl_id) 60562306a36Sopenharmony_ci return 0; 60662306a36Sopenharmony_ci return ecm->port.in_ep->enabled ? 1 : 0; 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic void ecm_disable(struct usb_function *f) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci struct f_ecm *ecm = func_to_ecm(f); 61262306a36Sopenharmony_ci struct usb_composite_dev *cdev = f->config->cdev; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci DBG(cdev, "ecm deactivated\n"); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci if (ecm->port.in_ep->enabled) { 61762306a36Sopenharmony_ci gether_disconnect(&ecm->port); 61862306a36Sopenharmony_ci } else { 61962306a36Sopenharmony_ci ecm->port.in_ep->desc = NULL; 62062306a36Sopenharmony_ci ecm->port.out_ep->desc = NULL; 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci usb_ep_disable(ecm->notify); 62462306a36Sopenharmony_ci ecm->notify->desc = NULL; 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci/* 63062306a36Sopenharmony_ci * Callbacks let us notify the host about connect/disconnect when the 63162306a36Sopenharmony_ci * net device is opened or closed. 63262306a36Sopenharmony_ci * 63362306a36Sopenharmony_ci * For testing, note that link states on this side include both opened 63462306a36Sopenharmony_ci * and closed variants of: 63562306a36Sopenharmony_ci * 63662306a36Sopenharmony_ci * - disconnected/unconfigured 63762306a36Sopenharmony_ci * - configured but inactive (data alt 0) 63862306a36Sopenharmony_ci * - configured and active (data alt 1) 63962306a36Sopenharmony_ci * 64062306a36Sopenharmony_ci * Each needs to be tested with unplug, rmmod, SET_CONFIGURATION, and 64162306a36Sopenharmony_ci * SET_INTERFACE (altsetting). Remember also that "configured" doesn't 64262306a36Sopenharmony_ci * imply the host is actually polling the notification endpoint, and 64362306a36Sopenharmony_ci * likewise that "active" doesn't imply it's actually using the data 64462306a36Sopenharmony_ci * endpoints for traffic. 64562306a36Sopenharmony_ci */ 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic void ecm_open(struct gether *geth) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci struct f_ecm *ecm = func_to_ecm(&geth->func); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci DBG(ecm->port.func.config->cdev, "%s\n", __func__); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci ecm->is_open = true; 65462306a36Sopenharmony_ci ecm_notify(ecm); 65562306a36Sopenharmony_ci} 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_cistatic void ecm_close(struct gether *geth) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci struct f_ecm *ecm = func_to_ecm(&geth->func); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci DBG(ecm->port.func.config->cdev, "%s\n", __func__); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci ecm->is_open = false; 66462306a36Sopenharmony_ci ecm_notify(ecm); 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci/* ethernet function driver setup/binding */ 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_cistatic int 67262306a36Sopenharmony_ciecm_bind(struct usb_configuration *c, struct usb_function *f) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci struct usb_composite_dev *cdev = c->cdev; 67562306a36Sopenharmony_ci struct f_ecm *ecm = func_to_ecm(f); 67662306a36Sopenharmony_ci struct usb_string *us; 67762306a36Sopenharmony_ci int status = 0; 67862306a36Sopenharmony_ci struct usb_ep *ep; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci struct f_ecm_opts *ecm_opts; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (!can_support_ecm(cdev->gadget)) 68362306a36Sopenharmony_ci return -EINVAL; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci ecm_opts = container_of(f->fi, struct f_ecm_opts, func_inst); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci mutex_lock(&ecm_opts->lock); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci gether_set_gadget(ecm_opts->net, cdev->gadget); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci if (!ecm_opts->bound) { 69262306a36Sopenharmony_ci status = gether_register_netdev(ecm_opts->net); 69362306a36Sopenharmony_ci ecm_opts->bound = true; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci mutex_unlock(&ecm_opts->lock); 69762306a36Sopenharmony_ci if (status) 69862306a36Sopenharmony_ci return status; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci ecm_string_defs[1].s = ecm->ethaddr; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci us = usb_gstrings_attach(cdev, ecm_strings, 70362306a36Sopenharmony_ci ARRAY_SIZE(ecm_string_defs)); 70462306a36Sopenharmony_ci if (IS_ERR(us)) 70562306a36Sopenharmony_ci return PTR_ERR(us); 70662306a36Sopenharmony_ci ecm_control_intf.iInterface = us[0].id; 70762306a36Sopenharmony_ci ecm_data_intf.iInterface = us[2].id; 70862306a36Sopenharmony_ci ecm_desc.iMACAddress = us[1].id; 70962306a36Sopenharmony_ci ecm_iad_descriptor.iFunction = us[3].id; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* allocate instance-specific interface IDs */ 71262306a36Sopenharmony_ci status = usb_interface_id(c, f); 71362306a36Sopenharmony_ci if (status < 0) 71462306a36Sopenharmony_ci goto fail; 71562306a36Sopenharmony_ci ecm->ctrl_id = status; 71662306a36Sopenharmony_ci ecm_iad_descriptor.bFirstInterface = status; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci ecm_control_intf.bInterfaceNumber = status; 71962306a36Sopenharmony_ci ecm_union_desc.bMasterInterface0 = status; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci status = usb_interface_id(c, f); 72262306a36Sopenharmony_ci if (status < 0) 72362306a36Sopenharmony_ci goto fail; 72462306a36Sopenharmony_ci ecm->data_id = status; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci ecm_data_nop_intf.bInterfaceNumber = status; 72762306a36Sopenharmony_ci ecm_data_intf.bInterfaceNumber = status; 72862306a36Sopenharmony_ci ecm_union_desc.bSlaveInterface0 = status; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci status = -ENODEV; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci /* allocate instance-specific endpoints */ 73362306a36Sopenharmony_ci ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_in_desc); 73462306a36Sopenharmony_ci if (!ep) 73562306a36Sopenharmony_ci goto fail; 73662306a36Sopenharmony_ci ecm->port.in_ep = ep; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_out_desc); 73962306a36Sopenharmony_ci if (!ep) 74062306a36Sopenharmony_ci goto fail; 74162306a36Sopenharmony_ci ecm->port.out_ep = ep; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci /* NOTE: a status/notification endpoint is *OPTIONAL* but we 74462306a36Sopenharmony_ci * don't treat it that way. It's simpler, and some newer CDC 74562306a36Sopenharmony_ci * profiles (wireless handsets) no longer treat it as optional. 74662306a36Sopenharmony_ci */ 74762306a36Sopenharmony_ci ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_notify_desc); 74862306a36Sopenharmony_ci if (!ep) 74962306a36Sopenharmony_ci goto fail; 75062306a36Sopenharmony_ci ecm->notify = ep; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci status = -ENOMEM; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci /* allocate notification request and buffer */ 75562306a36Sopenharmony_ci ecm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL); 75662306a36Sopenharmony_ci if (!ecm->notify_req) 75762306a36Sopenharmony_ci goto fail; 75862306a36Sopenharmony_ci ecm->notify_req->buf = kmalloc(ECM_STATUS_BYTECOUNT, GFP_KERNEL); 75962306a36Sopenharmony_ci if (!ecm->notify_req->buf) 76062306a36Sopenharmony_ci goto fail; 76162306a36Sopenharmony_ci ecm->notify_req->context = ecm; 76262306a36Sopenharmony_ci ecm->notify_req->complete = ecm_notify_complete; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci /* support all relevant hardware speeds... we expect that when 76562306a36Sopenharmony_ci * hardware is dual speed, all bulk-capable endpoints work at 76662306a36Sopenharmony_ci * both speeds 76762306a36Sopenharmony_ci */ 76862306a36Sopenharmony_ci hs_ecm_in_desc.bEndpointAddress = fs_ecm_in_desc.bEndpointAddress; 76962306a36Sopenharmony_ci hs_ecm_out_desc.bEndpointAddress = fs_ecm_out_desc.bEndpointAddress; 77062306a36Sopenharmony_ci hs_ecm_notify_desc.bEndpointAddress = 77162306a36Sopenharmony_ci fs_ecm_notify_desc.bEndpointAddress; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci ss_ecm_in_desc.bEndpointAddress = fs_ecm_in_desc.bEndpointAddress; 77462306a36Sopenharmony_ci ss_ecm_out_desc.bEndpointAddress = fs_ecm_out_desc.bEndpointAddress; 77562306a36Sopenharmony_ci ss_ecm_notify_desc.bEndpointAddress = 77662306a36Sopenharmony_ci fs_ecm_notify_desc.bEndpointAddress; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci status = usb_assign_descriptors(f, ecm_fs_function, ecm_hs_function, 77962306a36Sopenharmony_ci ecm_ss_function, ecm_ss_function); 78062306a36Sopenharmony_ci if (status) 78162306a36Sopenharmony_ci goto fail; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci /* NOTE: all that is done without knowing or caring about 78462306a36Sopenharmony_ci * the network link ... which is unavailable to this code 78562306a36Sopenharmony_ci * until we're activated via set_alt(). 78662306a36Sopenharmony_ci */ 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci ecm->port.open = ecm_open; 78962306a36Sopenharmony_ci ecm->port.close = ecm_close; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci DBG(cdev, "CDC Ethernet: IN/%s OUT/%s NOTIFY/%s\n", 79262306a36Sopenharmony_ci ecm->port.in_ep->name, ecm->port.out_ep->name, 79362306a36Sopenharmony_ci ecm->notify->name); 79462306a36Sopenharmony_ci return 0; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_cifail: 79762306a36Sopenharmony_ci if (ecm->notify_req) { 79862306a36Sopenharmony_ci kfree(ecm->notify_req->buf); 79962306a36Sopenharmony_ci usb_ep_free_request(ecm->notify, ecm->notify_req); 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci return status; 80562306a36Sopenharmony_ci} 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_cistatic inline struct f_ecm_opts *to_f_ecm_opts(struct config_item *item) 80862306a36Sopenharmony_ci{ 80962306a36Sopenharmony_ci return container_of(to_config_group(item), struct f_ecm_opts, 81062306a36Sopenharmony_ci func_inst.group); 81162306a36Sopenharmony_ci} 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci/* f_ecm_item_ops */ 81462306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM(ecm); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci/* f_ecm_opts_dev_addr */ 81762306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ecm); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci/* f_ecm_opts_host_addr */ 82062306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ecm); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci/* f_ecm_opts_qmult */ 82362306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ecm); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci/* f_ecm_opts_ifname */ 82662306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ecm); 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_cistatic struct configfs_attribute *ecm_attrs[] = { 82962306a36Sopenharmony_ci &ecm_opts_attr_dev_addr, 83062306a36Sopenharmony_ci &ecm_opts_attr_host_addr, 83162306a36Sopenharmony_ci &ecm_opts_attr_qmult, 83262306a36Sopenharmony_ci &ecm_opts_attr_ifname, 83362306a36Sopenharmony_ci NULL, 83462306a36Sopenharmony_ci}; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_cistatic const struct config_item_type ecm_func_type = { 83762306a36Sopenharmony_ci .ct_item_ops = &ecm_item_ops, 83862306a36Sopenharmony_ci .ct_attrs = ecm_attrs, 83962306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 84062306a36Sopenharmony_ci}; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_cistatic void ecm_free_inst(struct usb_function_instance *f) 84362306a36Sopenharmony_ci{ 84462306a36Sopenharmony_ci struct f_ecm_opts *opts; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci opts = container_of(f, struct f_ecm_opts, func_inst); 84762306a36Sopenharmony_ci if (opts->bound) 84862306a36Sopenharmony_ci gether_cleanup(netdev_priv(opts->net)); 84962306a36Sopenharmony_ci else 85062306a36Sopenharmony_ci free_netdev(opts->net); 85162306a36Sopenharmony_ci kfree(opts); 85262306a36Sopenharmony_ci} 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_cistatic struct usb_function_instance *ecm_alloc_inst(void) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci struct f_ecm_opts *opts; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci opts = kzalloc(sizeof(*opts), GFP_KERNEL); 85962306a36Sopenharmony_ci if (!opts) 86062306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 86162306a36Sopenharmony_ci mutex_init(&opts->lock); 86262306a36Sopenharmony_ci opts->func_inst.free_func_inst = ecm_free_inst; 86362306a36Sopenharmony_ci opts->net = gether_setup_default(); 86462306a36Sopenharmony_ci if (IS_ERR(opts->net)) { 86562306a36Sopenharmony_ci struct net_device *net = opts->net; 86662306a36Sopenharmony_ci kfree(opts); 86762306a36Sopenharmony_ci return ERR_CAST(net); 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci config_group_init_type_name(&opts->func_inst.group, "", &ecm_func_type); 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci return &opts->func_inst; 87362306a36Sopenharmony_ci} 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cistatic void ecm_suspend(struct usb_function *f) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci struct f_ecm *ecm = func_to_ecm(f); 87862306a36Sopenharmony_ci struct usb_composite_dev *cdev = ecm->port.func.config->cdev; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci DBG(cdev, "ECM Suspend\n"); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci gether_suspend(&ecm->port); 88362306a36Sopenharmony_ci} 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_cistatic void ecm_resume(struct usb_function *f) 88662306a36Sopenharmony_ci{ 88762306a36Sopenharmony_ci struct f_ecm *ecm = func_to_ecm(f); 88862306a36Sopenharmony_ci struct usb_composite_dev *cdev = ecm->port.func.config->cdev; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci DBG(cdev, "ECM Resume\n"); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci gether_resume(&ecm->port); 89362306a36Sopenharmony_ci} 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_cistatic void ecm_free(struct usb_function *f) 89662306a36Sopenharmony_ci{ 89762306a36Sopenharmony_ci struct f_ecm *ecm; 89862306a36Sopenharmony_ci struct f_ecm_opts *opts; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci ecm = func_to_ecm(f); 90162306a36Sopenharmony_ci opts = container_of(f->fi, struct f_ecm_opts, func_inst); 90262306a36Sopenharmony_ci kfree(ecm); 90362306a36Sopenharmony_ci mutex_lock(&opts->lock); 90462306a36Sopenharmony_ci opts->refcnt--; 90562306a36Sopenharmony_ci mutex_unlock(&opts->lock); 90662306a36Sopenharmony_ci} 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_cistatic void ecm_unbind(struct usb_configuration *c, struct usb_function *f) 90962306a36Sopenharmony_ci{ 91062306a36Sopenharmony_ci struct f_ecm *ecm = func_to_ecm(f); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci DBG(c->cdev, "ecm unbind\n"); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci usb_free_all_descriptors(f); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci if (atomic_read(&ecm->notify_count)) { 91762306a36Sopenharmony_ci usb_ep_dequeue(ecm->notify, ecm->notify_req); 91862306a36Sopenharmony_ci atomic_set(&ecm->notify_count, 0); 91962306a36Sopenharmony_ci } 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci kfree(ecm->notify_req->buf); 92262306a36Sopenharmony_ci usb_ep_free_request(ecm->notify, ecm->notify_req); 92362306a36Sopenharmony_ci} 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_cistatic struct usb_function *ecm_alloc(struct usb_function_instance *fi) 92662306a36Sopenharmony_ci{ 92762306a36Sopenharmony_ci struct f_ecm *ecm; 92862306a36Sopenharmony_ci struct f_ecm_opts *opts; 92962306a36Sopenharmony_ci int status; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci /* allocate and initialize one new instance */ 93262306a36Sopenharmony_ci ecm = kzalloc(sizeof(*ecm), GFP_KERNEL); 93362306a36Sopenharmony_ci if (!ecm) 93462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci opts = container_of(fi, struct f_ecm_opts, func_inst); 93762306a36Sopenharmony_ci mutex_lock(&opts->lock); 93862306a36Sopenharmony_ci opts->refcnt++; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci /* export host's Ethernet address in CDC format */ 94162306a36Sopenharmony_ci status = gether_get_host_addr_cdc(opts->net, ecm->ethaddr, 94262306a36Sopenharmony_ci sizeof(ecm->ethaddr)); 94362306a36Sopenharmony_ci if (status < 12) { 94462306a36Sopenharmony_ci kfree(ecm); 94562306a36Sopenharmony_ci mutex_unlock(&opts->lock); 94662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci ecm->port.ioport = netdev_priv(opts->net); 95062306a36Sopenharmony_ci mutex_unlock(&opts->lock); 95162306a36Sopenharmony_ci ecm->port.cdc_filter = DEFAULT_FILTER; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci ecm->port.func.name = "cdc_ethernet"; 95462306a36Sopenharmony_ci /* descriptors are per-instance copies */ 95562306a36Sopenharmony_ci ecm->port.func.bind = ecm_bind; 95662306a36Sopenharmony_ci ecm->port.func.unbind = ecm_unbind; 95762306a36Sopenharmony_ci ecm->port.func.set_alt = ecm_set_alt; 95862306a36Sopenharmony_ci ecm->port.func.get_alt = ecm_get_alt; 95962306a36Sopenharmony_ci ecm->port.func.setup = ecm_setup; 96062306a36Sopenharmony_ci ecm->port.func.disable = ecm_disable; 96162306a36Sopenharmony_ci ecm->port.func.free_func = ecm_free; 96262306a36Sopenharmony_ci ecm->port.func.suspend = ecm_suspend; 96362306a36Sopenharmony_ci ecm->port.func.resume = ecm_resume; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci return &ecm->port.func; 96662306a36Sopenharmony_ci} 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ciDECLARE_USB_FUNCTION_INIT(ecm, ecm_alloc_inst, ecm_alloc); 96962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 97062306a36Sopenharmony_ciMODULE_AUTHOR("David Brownell"); 971