162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * f_rndis.c -- RNDIS link function driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2003-2005,2008 David Brownell 662306a36Sopenharmony_ci * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger 762306a36Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation 862306a36Sopenharmony_ci * Copyright (C) 2009 Samsung Electronics 962306a36Sopenharmony_ci * Author: Michal Nazarewicz (mina86@mina86.com) 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci/* #define VERBOSE_DEBUG */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/device.h> 1862306a36Sopenharmony_ci#include <linux/etherdevice.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/atomic.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "u_ether.h" 2362306a36Sopenharmony_ci#include "u_ether_configfs.h" 2462306a36Sopenharmony_ci#include "u_rndis.h" 2562306a36Sopenharmony_ci#include "rndis.h" 2662306a36Sopenharmony_ci#include "configfs.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* 2962306a36Sopenharmony_ci * This function is an RNDIS Ethernet port -- a Microsoft protocol that's 3062306a36Sopenharmony_ci * been promoted instead of the standard CDC Ethernet. The published RNDIS 3162306a36Sopenharmony_ci * spec is ambiguous, incomplete, and needlessly complex. Variants such as 3262306a36Sopenharmony_ci * ActiveSync have even worse status in terms of specification. 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci * In short: it's a protocol controlled by (and for) Microsoft, not for an 3562306a36Sopenharmony_ci * Open ecosystem or markets. Linux supports it *only* because Microsoft 3662306a36Sopenharmony_ci * doesn't support the CDC Ethernet standard. 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * The RNDIS data transfer model is complex, with multiple Ethernet packets 3962306a36Sopenharmony_ci * per USB message, and out of band data. The control model is built around 4062306a36Sopenharmony_ci * what's essentially an "RNDIS RPC" protocol. It's all wrapped in a CDC ACM 4162306a36Sopenharmony_ci * (modem, not Ethernet) veneer, with those ACM descriptors being entirely 4262306a36Sopenharmony_ci * useless (they're ignored). RNDIS expects to be the only function in its 4362306a36Sopenharmony_ci * configuration, so it's no real help if you need composite devices; and 4462306a36Sopenharmony_ci * it expects to be the first configuration too. 4562306a36Sopenharmony_ci * 4662306a36Sopenharmony_ci * There is a single technical advantage of RNDIS over CDC Ethernet, if you 4762306a36Sopenharmony_ci * discount the fluff that its RPC can be made to deliver: it doesn't need 4862306a36Sopenharmony_ci * a NOP altsetting for the data interface. That lets it work on some of the 4962306a36Sopenharmony_ci * "so smart it's stupid" hardware which takes over configuration changes 5062306a36Sopenharmony_ci * from the software, and adds restrictions like "no altsettings". 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * Unfortunately MSFT's RNDIS drivers are buggy. They hang or oops, and 5362306a36Sopenharmony_ci * have all sorts of contrary-to-specification oddities that can prevent 5462306a36Sopenharmony_ci * them from working sanely. Since bugfixes (or accurate specs, letting 5562306a36Sopenharmony_ci * Linux work around those bugs) are unlikely to ever come from MSFT, you 5662306a36Sopenharmony_ci * may want to avoid using RNDIS on purely operational grounds. 5762306a36Sopenharmony_ci * 5862306a36Sopenharmony_ci * Omissions from the RNDIS 1.0 specification include: 5962306a36Sopenharmony_ci * 6062306a36Sopenharmony_ci * - Power management ... references data that's scattered around lots 6162306a36Sopenharmony_ci * of other documentation, which is incorrect/incomplete there too. 6262306a36Sopenharmony_ci * 6362306a36Sopenharmony_ci * - There are various undocumented protocol requirements, like the need 6462306a36Sopenharmony_ci * to send garbage in some control-OUT messages. 6562306a36Sopenharmony_ci * 6662306a36Sopenharmony_ci * - MS-Windows drivers sometimes emit undocumented requests. 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistruct f_rndis { 7062306a36Sopenharmony_ci struct gether port; 7162306a36Sopenharmony_ci u8 ctrl_id, data_id; 7262306a36Sopenharmony_ci u8 ethaddr[ETH_ALEN]; 7362306a36Sopenharmony_ci u32 vendorID; 7462306a36Sopenharmony_ci const char *manufacturer; 7562306a36Sopenharmony_ci struct rndis_params *params; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci struct usb_ep *notify; 7862306a36Sopenharmony_ci struct usb_request *notify_req; 7962306a36Sopenharmony_ci atomic_t notify_count; 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic inline struct f_rndis *func_to_rndis(struct usb_function *f) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci return container_of(f, struct f_rndis, port.func); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/* 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci#define RNDIS_STATUS_INTERVAL_MS 32 9362306a36Sopenharmony_ci#define STATUS_BYTECOUNT 8 /* 8 bytes data */ 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/* interface descriptor: */ 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic struct usb_interface_descriptor rndis_control_intf = { 9962306a36Sopenharmony_ci .bLength = sizeof rndis_control_intf, 10062306a36Sopenharmony_ci .bDescriptorType = USB_DT_INTERFACE, 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* .bInterfaceNumber = DYNAMIC */ 10362306a36Sopenharmony_ci /* status endpoint is optional; this could be patched later */ 10462306a36Sopenharmony_ci .bNumEndpoints = 1, 10562306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_COMM, 10662306a36Sopenharmony_ci .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, 10762306a36Sopenharmony_ci .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR, 10862306a36Sopenharmony_ci /* .iInterface = DYNAMIC */ 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic struct usb_cdc_header_desc header_desc = { 11262306a36Sopenharmony_ci .bLength = sizeof header_desc, 11362306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 11462306a36Sopenharmony_ci .bDescriptorSubType = USB_CDC_HEADER_TYPE, 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci .bcdCDC = cpu_to_le16(0x0110), 11762306a36Sopenharmony_ci}; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor = { 12062306a36Sopenharmony_ci .bLength = sizeof call_mgmt_descriptor, 12162306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 12262306a36Sopenharmony_ci .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci .bmCapabilities = 0x00, 12562306a36Sopenharmony_ci .bDataInterface = 0x01, 12662306a36Sopenharmony_ci}; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic struct usb_cdc_acm_descriptor rndis_acm_descriptor = { 12962306a36Sopenharmony_ci .bLength = sizeof rndis_acm_descriptor, 13062306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 13162306a36Sopenharmony_ci .bDescriptorSubType = USB_CDC_ACM_TYPE, 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci .bmCapabilities = 0x00, 13462306a36Sopenharmony_ci}; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic struct usb_cdc_union_desc rndis_union_desc = { 13762306a36Sopenharmony_ci .bLength = sizeof(rndis_union_desc), 13862306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 13962306a36Sopenharmony_ci .bDescriptorSubType = USB_CDC_UNION_TYPE, 14062306a36Sopenharmony_ci /* .bMasterInterface0 = DYNAMIC */ 14162306a36Sopenharmony_ci /* .bSlaveInterface0 = DYNAMIC */ 14262306a36Sopenharmony_ci}; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/* the data interface has two bulk endpoints */ 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic struct usb_interface_descriptor rndis_data_intf = { 14762306a36Sopenharmony_ci .bLength = sizeof rndis_data_intf, 14862306a36Sopenharmony_ci .bDescriptorType = USB_DT_INTERFACE, 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* .bInterfaceNumber = DYNAMIC */ 15162306a36Sopenharmony_ci .bNumEndpoints = 2, 15262306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_CDC_DATA, 15362306a36Sopenharmony_ci .bInterfaceSubClass = 0, 15462306a36Sopenharmony_ci .bInterfaceProtocol = 0, 15562306a36Sopenharmony_ci /* .iInterface = DYNAMIC */ 15662306a36Sopenharmony_ci}; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic struct usb_interface_assoc_descriptor 16062306a36Sopenharmony_cirndis_iad_descriptor = { 16162306a36Sopenharmony_ci .bLength = sizeof rndis_iad_descriptor, 16262306a36Sopenharmony_ci .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci .bFirstInterface = 0, /* XXX, hardcoded */ 16562306a36Sopenharmony_ci .bInterfaceCount = 2, // control + data 16662306a36Sopenharmony_ci .bFunctionClass = USB_CLASS_COMM, 16762306a36Sopenharmony_ci .bFunctionSubClass = USB_CDC_SUBCLASS_ETHERNET, 16862306a36Sopenharmony_ci .bFunctionProtocol = USB_CDC_PROTO_NONE, 16962306a36Sopenharmony_ci /* .iFunction = DYNAMIC */ 17062306a36Sopenharmony_ci}; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci/* full speed support: */ 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic struct usb_endpoint_descriptor fs_notify_desc = { 17562306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 17662306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_IN, 17962306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_INT, 18062306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT), 18162306a36Sopenharmony_ci .bInterval = RNDIS_STATUS_INTERVAL_MS, 18262306a36Sopenharmony_ci}; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic struct usb_endpoint_descriptor fs_in_desc = { 18562306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 18662306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_IN, 18962306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 19062306a36Sopenharmony_ci}; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic struct usb_endpoint_descriptor fs_out_desc = { 19362306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 19462306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_OUT, 19762306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 19862306a36Sopenharmony_ci}; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic struct usb_descriptor_header *eth_fs_function[] = { 20162306a36Sopenharmony_ci (struct usb_descriptor_header *) &rndis_iad_descriptor, 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* control interface matches ACM, not Ethernet */ 20462306a36Sopenharmony_ci (struct usb_descriptor_header *) &rndis_control_intf, 20562306a36Sopenharmony_ci (struct usb_descriptor_header *) &header_desc, 20662306a36Sopenharmony_ci (struct usb_descriptor_header *) &call_mgmt_descriptor, 20762306a36Sopenharmony_ci (struct usb_descriptor_header *) &rndis_acm_descriptor, 20862306a36Sopenharmony_ci (struct usb_descriptor_header *) &rndis_union_desc, 20962306a36Sopenharmony_ci (struct usb_descriptor_header *) &fs_notify_desc, 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* data interface has no altsetting */ 21262306a36Sopenharmony_ci (struct usb_descriptor_header *) &rndis_data_intf, 21362306a36Sopenharmony_ci (struct usb_descriptor_header *) &fs_in_desc, 21462306a36Sopenharmony_ci (struct usb_descriptor_header *) &fs_out_desc, 21562306a36Sopenharmony_ci NULL, 21662306a36Sopenharmony_ci}; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci/* high speed support: */ 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic struct usb_endpoint_descriptor hs_notify_desc = { 22162306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 22262306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_IN, 22562306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_INT, 22662306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT), 22762306a36Sopenharmony_ci .bInterval = USB_MS_TO_HS_INTERVAL(RNDIS_STATUS_INTERVAL_MS) 22862306a36Sopenharmony_ci}; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic struct usb_endpoint_descriptor hs_in_desc = { 23162306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 23262306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_IN, 23562306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 23662306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(512), 23762306a36Sopenharmony_ci}; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic struct usb_endpoint_descriptor hs_out_desc = { 24062306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 24162306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_OUT, 24462306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 24562306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(512), 24662306a36Sopenharmony_ci}; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic struct usb_descriptor_header *eth_hs_function[] = { 24962306a36Sopenharmony_ci (struct usb_descriptor_header *) &rndis_iad_descriptor, 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* control interface matches ACM, not Ethernet */ 25262306a36Sopenharmony_ci (struct usb_descriptor_header *) &rndis_control_intf, 25362306a36Sopenharmony_ci (struct usb_descriptor_header *) &header_desc, 25462306a36Sopenharmony_ci (struct usb_descriptor_header *) &call_mgmt_descriptor, 25562306a36Sopenharmony_ci (struct usb_descriptor_header *) &rndis_acm_descriptor, 25662306a36Sopenharmony_ci (struct usb_descriptor_header *) &rndis_union_desc, 25762306a36Sopenharmony_ci (struct usb_descriptor_header *) &hs_notify_desc, 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci /* data interface has no altsetting */ 26062306a36Sopenharmony_ci (struct usb_descriptor_header *) &rndis_data_intf, 26162306a36Sopenharmony_ci (struct usb_descriptor_header *) &hs_in_desc, 26262306a36Sopenharmony_ci (struct usb_descriptor_header *) &hs_out_desc, 26362306a36Sopenharmony_ci NULL, 26462306a36Sopenharmony_ci}; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci/* super speed support: */ 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic struct usb_endpoint_descriptor ss_notify_desc = { 26962306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 27062306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_IN, 27362306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_INT, 27462306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT), 27562306a36Sopenharmony_ci .bInterval = USB_MS_TO_HS_INTERVAL(RNDIS_STATUS_INTERVAL_MS) 27662306a36Sopenharmony_ci}; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic struct usb_ss_ep_comp_descriptor ss_intr_comp_desc = { 27962306a36Sopenharmony_ci .bLength = sizeof ss_intr_comp_desc, 28062306a36Sopenharmony_ci .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* the following 3 values can be tweaked if necessary */ 28362306a36Sopenharmony_ci /* .bMaxBurst = 0, */ 28462306a36Sopenharmony_ci /* .bmAttributes = 0, */ 28562306a36Sopenharmony_ci .wBytesPerInterval = cpu_to_le16(STATUS_BYTECOUNT), 28662306a36Sopenharmony_ci}; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic struct usb_endpoint_descriptor ss_in_desc = { 28962306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 29062306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_IN, 29362306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 29462306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(1024), 29562306a36Sopenharmony_ci}; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic struct usb_endpoint_descriptor ss_out_desc = { 29862306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 29962306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_OUT, 30262306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 30362306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(1024), 30462306a36Sopenharmony_ci}; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic struct usb_ss_ep_comp_descriptor ss_bulk_comp_desc = { 30762306a36Sopenharmony_ci .bLength = sizeof ss_bulk_comp_desc, 30862306a36Sopenharmony_ci .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* the following 2 values can be tweaked if necessary */ 31162306a36Sopenharmony_ci /* .bMaxBurst = 0, */ 31262306a36Sopenharmony_ci /* .bmAttributes = 0, */ 31362306a36Sopenharmony_ci}; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic struct usb_descriptor_header *eth_ss_function[] = { 31662306a36Sopenharmony_ci (struct usb_descriptor_header *) &rndis_iad_descriptor, 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* control interface matches ACM, not Ethernet */ 31962306a36Sopenharmony_ci (struct usb_descriptor_header *) &rndis_control_intf, 32062306a36Sopenharmony_ci (struct usb_descriptor_header *) &header_desc, 32162306a36Sopenharmony_ci (struct usb_descriptor_header *) &call_mgmt_descriptor, 32262306a36Sopenharmony_ci (struct usb_descriptor_header *) &rndis_acm_descriptor, 32362306a36Sopenharmony_ci (struct usb_descriptor_header *) &rndis_union_desc, 32462306a36Sopenharmony_ci (struct usb_descriptor_header *) &ss_notify_desc, 32562306a36Sopenharmony_ci (struct usb_descriptor_header *) &ss_intr_comp_desc, 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* data interface has no altsetting */ 32862306a36Sopenharmony_ci (struct usb_descriptor_header *) &rndis_data_intf, 32962306a36Sopenharmony_ci (struct usb_descriptor_header *) &ss_in_desc, 33062306a36Sopenharmony_ci (struct usb_descriptor_header *) &ss_bulk_comp_desc, 33162306a36Sopenharmony_ci (struct usb_descriptor_header *) &ss_out_desc, 33262306a36Sopenharmony_ci (struct usb_descriptor_header *) &ss_bulk_comp_desc, 33362306a36Sopenharmony_ci NULL, 33462306a36Sopenharmony_ci}; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci/* string descriptors: */ 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic struct usb_string rndis_string_defs[] = { 33962306a36Sopenharmony_ci [0].s = "RNDIS Communications Control", 34062306a36Sopenharmony_ci [1].s = "RNDIS Ethernet Data", 34162306a36Sopenharmony_ci [2].s = "RNDIS", 34262306a36Sopenharmony_ci { } /* end of list */ 34362306a36Sopenharmony_ci}; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic struct usb_gadget_strings rndis_string_table = { 34662306a36Sopenharmony_ci .language = 0x0409, /* en-us */ 34762306a36Sopenharmony_ci .strings = rndis_string_defs, 34862306a36Sopenharmony_ci}; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic struct usb_gadget_strings *rndis_strings[] = { 35162306a36Sopenharmony_ci &rndis_string_table, 35262306a36Sopenharmony_ci NULL, 35362306a36Sopenharmony_ci}; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic struct sk_buff *rndis_add_header(struct gether *port, 35862306a36Sopenharmony_ci struct sk_buff *skb) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct sk_buff *skb2; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (!skb) 36362306a36Sopenharmony_ci return NULL; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci skb2 = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type)); 36662306a36Sopenharmony_ci rndis_add_hdr(skb2); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci dev_kfree_skb(skb); 36962306a36Sopenharmony_ci return skb2; 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic void rndis_response_available(void *_rndis) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci struct f_rndis *rndis = _rndis; 37562306a36Sopenharmony_ci struct usb_request *req = rndis->notify_req; 37662306a36Sopenharmony_ci struct usb_composite_dev *cdev = rndis->port.func.config->cdev; 37762306a36Sopenharmony_ci __le32 *data = req->buf; 37862306a36Sopenharmony_ci int status; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (atomic_inc_return(&rndis->notify_count) != 1) 38162306a36Sopenharmony_ci return; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci /* Send RNDIS RESPONSE_AVAILABLE notification; a 38462306a36Sopenharmony_ci * USB_CDC_NOTIFY_RESPONSE_AVAILABLE "should" work too 38562306a36Sopenharmony_ci * 38662306a36Sopenharmony_ci * This is the only notification defined by RNDIS. 38762306a36Sopenharmony_ci */ 38862306a36Sopenharmony_ci data[0] = cpu_to_le32(1); 38962306a36Sopenharmony_ci data[1] = cpu_to_le32(0); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC); 39262306a36Sopenharmony_ci if (status) { 39362306a36Sopenharmony_ci atomic_dec(&rndis->notify_count); 39462306a36Sopenharmony_ci DBG(cdev, "notify/0 --> %d\n", status); 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic void rndis_response_complete(struct usb_ep *ep, struct usb_request *req) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci struct f_rndis *rndis = req->context; 40162306a36Sopenharmony_ci struct usb_composite_dev *cdev = rndis->port.func.config->cdev; 40262306a36Sopenharmony_ci int status = req->status; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* after TX: 40562306a36Sopenharmony_ci * - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control) 40662306a36Sopenharmony_ci * - RNDIS_RESPONSE_AVAILABLE (status/irq) 40762306a36Sopenharmony_ci */ 40862306a36Sopenharmony_ci switch (status) { 40962306a36Sopenharmony_ci case -ECONNRESET: 41062306a36Sopenharmony_ci case -ESHUTDOWN: 41162306a36Sopenharmony_ci /* connection gone */ 41262306a36Sopenharmony_ci atomic_set(&rndis->notify_count, 0); 41362306a36Sopenharmony_ci break; 41462306a36Sopenharmony_ci default: 41562306a36Sopenharmony_ci DBG(cdev, "RNDIS %s response error %d, %d/%d\n", 41662306a36Sopenharmony_ci ep->name, status, 41762306a36Sopenharmony_ci req->actual, req->length); 41862306a36Sopenharmony_ci fallthrough; 41962306a36Sopenharmony_ci case 0: 42062306a36Sopenharmony_ci if (ep != rndis->notify) 42162306a36Sopenharmony_ci break; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* handle multiple pending RNDIS_RESPONSE_AVAILABLE 42462306a36Sopenharmony_ci * notifications by resending until we're done 42562306a36Sopenharmony_ci */ 42662306a36Sopenharmony_ci if (atomic_dec_and_test(&rndis->notify_count)) 42762306a36Sopenharmony_ci break; 42862306a36Sopenharmony_ci status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC); 42962306a36Sopenharmony_ci if (status) { 43062306a36Sopenharmony_ci atomic_dec(&rndis->notify_count); 43162306a36Sopenharmony_ci DBG(cdev, "notify/1 --> %d\n", status); 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci break; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic void rndis_command_complete(struct usb_ep *ep, struct usb_request *req) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci struct f_rndis *rndis = req->context; 44062306a36Sopenharmony_ci int status; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */ 44362306a36Sopenharmony_ci// spin_lock(&dev->lock); 44462306a36Sopenharmony_ci status = rndis_msg_parser(rndis->params, (u8 *) req->buf); 44562306a36Sopenharmony_ci if (status < 0) 44662306a36Sopenharmony_ci pr_err("RNDIS command error %d, %d/%d\n", 44762306a36Sopenharmony_ci status, req->actual, req->length); 44862306a36Sopenharmony_ci// spin_unlock(&dev->lock); 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic int 45262306a36Sopenharmony_cirndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci struct f_rndis *rndis = func_to_rndis(f); 45562306a36Sopenharmony_ci struct usb_composite_dev *cdev = f->config->cdev; 45662306a36Sopenharmony_ci struct usb_request *req = cdev->req; 45762306a36Sopenharmony_ci int value = -EOPNOTSUPP; 45862306a36Sopenharmony_ci u16 w_index = le16_to_cpu(ctrl->wIndex); 45962306a36Sopenharmony_ci u16 w_value = le16_to_cpu(ctrl->wValue); 46062306a36Sopenharmony_ci u16 w_length = le16_to_cpu(ctrl->wLength); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* composite driver infrastructure handles everything except 46362306a36Sopenharmony_ci * CDC class messages; interface activation uses set_alt(). 46462306a36Sopenharmony_ci */ 46562306a36Sopenharmony_ci switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* RNDIS uses the CDC command encapsulation mechanism to implement 46862306a36Sopenharmony_ci * an RPC scheme, with much getting/setting of attributes by OID. 46962306a36Sopenharmony_ci */ 47062306a36Sopenharmony_ci case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) 47162306a36Sopenharmony_ci | USB_CDC_SEND_ENCAPSULATED_COMMAND: 47262306a36Sopenharmony_ci if (w_value || w_index != rndis->ctrl_id) 47362306a36Sopenharmony_ci goto invalid; 47462306a36Sopenharmony_ci /* read the request; process it later */ 47562306a36Sopenharmony_ci value = w_length; 47662306a36Sopenharmony_ci req->complete = rndis_command_complete; 47762306a36Sopenharmony_ci req->context = rndis; 47862306a36Sopenharmony_ci /* later, rndis_response_available() sends a notification */ 47962306a36Sopenharmony_ci break; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) 48262306a36Sopenharmony_ci | USB_CDC_GET_ENCAPSULATED_RESPONSE: 48362306a36Sopenharmony_ci if (w_value || w_index != rndis->ctrl_id) 48462306a36Sopenharmony_ci goto invalid; 48562306a36Sopenharmony_ci else { 48662306a36Sopenharmony_ci u8 *buf; 48762306a36Sopenharmony_ci u32 n; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* return the result */ 49062306a36Sopenharmony_ci buf = rndis_get_next_response(rndis->params, &n); 49162306a36Sopenharmony_ci if (buf) { 49262306a36Sopenharmony_ci memcpy(req->buf, buf, n); 49362306a36Sopenharmony_ci req->complete = rndis_response_complete; 49462306a36Sopenharmony_ci req->context = rndis; 49562306a36Sopenharmony_ci rndis_free_response(rndis->params, buf); 49662306a36Sopenharmony_ci value = n; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci /* else stalls ... spec says to avoid that */ 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci break; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci default: 50362306a36Sopenharmony_ciinvalid: 50462306a36Sopenharmony_ci VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", 50562306a36Sopenharmony_ci ctrl->bRequestType, ctrl->bRequest, 50662306a36Sopenharmony_ci w_value, w_index, w_length); 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* respond with data transfer or status phase? */ 51062306a36Sopenharmony_ci if (value >= 0) { 51162306a36Sopenharmony_ci DBG(cdev, "rndis req%02x.%02x v%04x i%04x l%d\n", 51262306a36Sopenharmony_ci ctrl->bRequestType, ctrl->bRequest, 51362306a36Sopenharmony_ci w_value, w_index, w_length); 51462306a36Sopenharmony_ci req->zero = (value < w_length); 51562306a36Sopenharmony_ci req->length = value; 51662306a36Sopenharmony_ci value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); 51762306a36Sopenharmony_ci if (value < 0) 51862306a36Sopenharmony_ci ERROR(cdev, "rndis response on err %d\n", value); 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* device either stalls (value < 0) or reports success */ 52262306a36Sopenharmony_ci return value; 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cistatic int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci struct f_rndis *rndis = func_to_rndis(f); 52962306a36Sopenharmony_ci struct usb_composite_dev *cdev = f->config->cdev; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci /* we know alt == 0 */ 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (intf == rndis->ctrl_id) { 53462306a36Sopenharmony_ci VDBG(cdev, "reset rndis control %d\n", intf); 53562306a36Sopenharmony_ci usb_ep_disable(rndis->notify); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (!rndis->notify->desc) { 53862306a36Sopenharmony_ci VDBG(cdev, "init rndis ctrl %d\n", intf); 53962306a36Sopenharmony_ci if (config_ep_by_speed(cdev->gadget, f, rndis->notify)) 54062306a36Sopenharmony_ci goto fail; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci usb_ep_enable(rndis->notify); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci } else if (intf == rndis->data_id) { 54562306a36Sopenharmony_ci struct net_device *net; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (rndis->port.in_ep->enabled) { 54862306a36Sopenharmony_ci DBG(cdev, "reset rndis\n"); 54962306a36Sopenharmony_ci gether_disconnect(&rndis->port); 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci if (!rndis->port.in_ep->desc || !rndis->port.out_ep->desc) { 55362306a36Sopenharmony_ci DBG(cdev, "init rndis\n"); 55462306a36Sopenharmony_ci if (config_ep_by_speed(cdev->gadget, f, 55562306a36Sopenharmony_ci rndis->port.in_ep) || 55662306a36Sopenharmony_ci config_ep_by_speed(cdev->gadget, f, 55762306a36Sopenharmony_ci rndis->port.out_ep)) { 55862306a36Sopenharmony_ci rndis->port.in_ep->desc = NULL; 55962306a36Sopenharmony_ci rndis->port.out_ep->desc = NULL; 56062306a36Sopenharmony_ci goto fail; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci /* Avoid ZLPs; they can be troublesome. */ 56562306a36Sopenharmony_ci rndis->port.is_zlp_ok = false; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* RNDIS should be in the "RNDIS uninitialized" state, 56862306a36Sopenharmony_ci * either never activated or after rndis_uninit(). 56962306a36Sopenharmony_ci * 57062306a36Sopenharmony_ci * We don't want data to flow here until a nonzero packet 57162306a36Sopenharmony_ci * filter is set, at which point it enters "RNDIS data 57262306a36Sopenharmony_ci * initialized" state ... but we do want the endpoints 57362306a36Sopenharmony_ci * to be activated. It's a strange little state. 57462306a36Sopenharmony_ci * 57562306a36Sopenharmony_ci * REVISIT the RNDIS gadget code has done this wrong for a 57662306a36Sopenharmony_ci * very long time. We need another call to the link layer 57762306a36Sopenharmony_ci * code -- gether_updown(...bool) maybe -- to do it right. 57862306a36Sopenharmony_ci */ 57962306a36Sopenharmony_ci rndis->port.cdc_filter = 0; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci DBG(cdev, "RNDIS RX/TX early activation ... \n"); 58262306a36Sopenharmony_ci net = gether_connect(&rndis->port); 58362306a36Sopenharmony_ci if (IS_ERR(net)) 58462306a36Sopenharmony_ci return PTR_ERR(net); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci rndis_set_param_dev(rndis->params, net, 58762306a36Sopenharmony_ci &rndis->port.cdc_filter); 58862306a36Sopenharmony_ci } else 58962306a36Sopenharmony_ci goto fail; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci return 0; 59262306a36Sopenharmony_cifail: 59362306a36Sopenharmony_ci return -EINVAL; 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic void rndis_disable(struct usb_function *f) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci struct f_rndis *rndis = func_to_rndis(f); 59962306a36Sopenharmony_ci struct usb_composite_dev *cdev = f->config->cdev; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci if (!rndis->notify->enabled) 60262306a36Sopenharmony_ci return; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci DBG(cdev, "rndis deactivated\n"); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci rndis_uninit(rndis->params); 60762306a36Sopenharmony_ci gether_disconnect(&rndis->port); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci usb_ep_disable(rndis->notify); 61062306a36Sopenharmony_ci rndis->notify->desc = NULL; 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci/* 61662306a36Sopenharmony_ci * This isn't quite the same mechanism as CDC Ethernet, since the 61762306a36Sopenharmony_ci * notification scheme passes less data, but the same set of link 61862306a36Sopenharmony_ci * states must be tested. A key difference is that altsettings are 61962306a36Sopenharmony_ci * not used to tell whether the link should send packets or not. 62062306a36Sopenharmony_ci */ 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cistatic void rndis_open(struct gether *geth) 62362306a36Sopenharmony_ci{ 62462306a36Sopenharmony_ci struct f_rndis *rndis = func_to_rndis(&geth->func); 62562306a36Sopenharmony_ci struct usb_composite_dev *cdev = geth->func.config->cdev; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci DBG(cdev, "%s\n", __func__); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, 63062306a36Sopenharmony_ci gether_bitrate(cdev->gadget) / 100); 63162306a36Sopenharmony_ci rndis_signal_connect(rndis->params); 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_cistatic void rndis_close(struct gether *geth) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci struct f_rndis *rndis = func_to_rndis(&geth->func); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci DBG(geth->func.config->cdev, "%s\n", __func__); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, 0); 64162306a36Sopenharmony_ci rndis_signal_disconnect(rndis->params); 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci/* Some controllers can't support RNDIS ... */ 64762306a36Sopenharmony_cistatic inline bool can_support_rndis(struct usb_configuration *c) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci /* everything else is *presumably* fine */ 65062306a36Sopenharmony_ci return true; 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci/* ethernet function driver setup/binding */ 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_cistatic int 65662306a36Sopenharmony_cirndis_bind(struct usb_configuration *c, struct usb_function *f) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci struct usb_composite_dev *cdev = c->cdev; 65962306a36Sopenharmony_ci struct f_rndis *rndis = func_to_rndis(f); 66062306a36Sopenharmony_ci struct usb_string *us; 66162306a36Sopenharmony_ci int status; 66262306a36Sopenharmony_ci struct usb_ep *ep; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci struct f_rndis_opts *rndis_opts; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci if (!can_support_rndis(c)) 66762306a36Sopenharmony_ci return -EINVAL; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci rndis_opts = container_of(f->fi, struct f_rndis_opts, func_inst); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if (cdev->use_os_string) { 67262306a36Sopenharmony_ci f->os_desc_table = kzalloc(sizeof(*f->os_desc_table), 67362306a36Sopenharmony_ci GFP_KERNEL); 67462306a36Sopenharmony_ci if (!f->os_desc_table) 67562306a36Sopenharmony_ci return -ENOMEM; 67662306a36Sopenharmony_ci f->os_desc_n = 1; 67762306a36Sopenharmony_ci f->os_desc_table[0].os_desc = &rndis_opts->rndis_os_desc; 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci rndis_iad_descriptor.bFunctionClass = rndis_opts->class; 68162306a36Sopenharmony_ci rndis_iad_descriptor.bFunctionSubClass = rndis_opts->subclass; 68262306a36Sopenharmony_ci rndis_iad_descriptor.bFunctionProtocol = rndis_opts->protocol; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci /* 68562306a36Sopenharmony_ci * in drivers/usb/gadget/configfs.c:configfs_composite_bind() 68662306a36Sopenharmony_ci * configurations are bound in sequence with list_for_each_entry, 68762306a36Sopenharmony_ci * in each configuration its functions are bound in sequence 68862306a36Sopenharmony_ci * with list_for_each_entry, so we assume no race condition 68962306a36Sopenharmony_ci * with regard to rndis_opts->bound access 69062306a36Sopenharmony_ci */ 69162306a36Sopenharmony_ci if (!rndis_opts->bound) { 69262306a36Sopenharmony_ci gether_set_gadget(rndis_opts->net, cdev->gadget); 69362306a36Sopenharmony_ci status = gether_register_netdev(rndis_opts->net); 69462306a36Sopenharmony_ci if (status) 69562306a36Sopenharmony_ci goto fail; 69662306a36Sopenharmony_ci rndis_opts->bound = true; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci us = usb_gstrings_attach(cdev, rndis_strings, 70062306a36Sopenharmony_ci ARRAY_SIZE(rndis_string_defs)); 70162306a36Sopenharmony_ci if (IS_ERR(us)) { 70262306a36Sopenharmony_ci status = PTR_ERR(us); 70362306a36Sopenharmony_ci goto fail; 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci rndis_control_intf.iInterface = us[0].id; 70662306a36Sopenharmony_ci rndis_data_intf.iInterface = us[1].id; 70762306a36Sopenharmony_ci rndis_iad_descriptor.iFunction = us[2].id; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci /* allocate instance-specific interface IDs */ 71062306a36Sopenharmony_ci status = usb_interface_id(c, f); 71162306a36Sopenharmony_ci if (status < 0) 71262306a36Sopenharmony_ci goto fail; 71362306a36Sopenharmony_ci rndis->ctrl_id = status; 71462306a36Sopenharmony_ci rndis_iad_descriptor.bFirstInterface = status; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci rndis_control_intf.bInterfaceNumber = status; 71762306a36Sopenharmony_ci rndis_union_desc.bMasterInterface0 = status; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci if (cdev->use_os_string) 72062306a36Sopenharmony_ci f->os_desc_table[0].if_id = 72162306a36Sopenharmony_ci rndis_iad_descriptor.bFirstInterface; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci status = usb_interface_id(c, f); 72462306a36Sopenharmony_ci if (status < 0) 72562306a36Sopenharmony_ci goto fail; 72662306a36Sopenharmony_ci rndis->data_id = status; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci rndis_data_intf.bInterfaceNumber = status; 72962306a36Sopenharmony_ci rndis_union_desc.bSlaveInterface0 = status; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci status = -ENODEV; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci /* allocate instance-specific endpoints */ 73462306a36Sopenharmony_ci ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc); 73562306a36Sopenharmony_ci if (!ep) 73662306a36Sopenharmony_ci goto fail; 73762306a36Sopenharmony_ci rndis->port.in_ep = ep; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc); 74062306a36Sopenharmony_ci if (!ep) 74162306a36Sopenharmony_ci goto fail; 74262306a36Sopenharmony_ci rndis->port.out_ep = ep; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci /* NOTE: a status/notification endpoint is, strictly speaking, 74562306a36Sopenharmony_ci * optional. We don't treat it that way though! It's simpler, 74662306a36Sopenharmony_ci * and some newer profiles don't treat it as optional. 74762306a36Sopenharmony_ci */ 74862306a36Sopenharmony_ci ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc); 74962306a36Sopenharmony_ci if (!ep) 75062306a36Sopenharmony_ci goto fail; 75162306a36Sopenharmony_ci rndis->notify = ep; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci status = -ENOMEM; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci /* allocate notification request and buffer */ 75662306a36Sopenharmony_ci rndis->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL); 75762306a36Sopenharmony_ci if (!rndis->notify_req) 75862306a36Sopenharmony_ci goto fail; 75962306a36Sopenharmony_ci rndis->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL); 76062306a36Sopenharmony_ci if (!rndis->notify_req->buf) 76162306a36Sopenharmony_ci goto fail; 76262306a36Sopenharmony_ci rndis->notify_req->length = STATUS_BYTECOUNT; 76362306a36Sopenharmony_ci rndis->notify_req->context = rndis; 76462306a36Sopenharmony_ci rndis->notify_req->complete = rndis_response_complete; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci /* support all relevant hardware speeds... we expect that when 76762306a36Sopenharmony_ci * hardware is dual speed, all bulk-capable endpoints work at 76862306a36Sopenharmony_ci * both speeds 76962306a36Sopenharmony_ci */ 77062306a36Sopenharmony_ci hs_in_desc.bEndpointAddress = fs_in_desc.bEndpointAddress; 77162306a36Sopenharmony_ci hs_out_desc.bEndpointAddress = fs_out_desc.bEndpointAddress; 77262306a36Sopenharmony_ci hs_notify_desc.bEndpointAddress = fs_notify_desc.bEndpointAddress; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci ss_in_desc.bEndpointAddress = fs_in_desc.bEndpointAddress; 77562306a36Sopenharmony_ci ss_out_desc.bEndpointAddress = fs_out_desc.bEndpointAddress; 77662306a36Sopenharmony_ci ss_notify_desc.bEndpointAddress = fs_notify_desc.bEndpointAddress; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci status = usb_assign_descriptors(f, eth_fs_function, eth_hs_function, 77962306a36Sopenharmony_ci eth_ss_function, eth_ss_function); 78062306a36Sopenharmony_ci if (status) 78162306a36Sopenharmony_ci goto fail; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci rndis->port.open = rndis_open; 78462306a36Sopenharmony_ci rndis->port.close = rndis_close; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, 0); 78762306a36Sopenharmony_ci rndis_set_host_mac(rndis->params, rndis->ethaddr); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci if (rndis->manufacturer && rndis->vendorID && 79062306a36Sopenharmony_ci rndis_set_param_vendor(rndis->params, rndis->vendorID, 79162306a36Sopenharmony_ci rndis->manufacturer)) { 79262306a36Sopenharmony_ci status = -EINVAL; 79362306a36Sopenharmony_ci goto fail_free_descs; 79462306a36Sopenharmony_ci } 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci /* NOTE: all that is done without knowing or caring about 79762306a36Sopenharmony_ci * the network link ... which is unavailable to this code 79862306a36Sopenharmony_ci * until we're activated via set_alt(). 79962306a36Sopenharmony_ci */ 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci DBG(cdev, "RNDIS: IN/%s OUT/%s NOTIFY/%s\n", 80262306a36Sopenharmony_ci rndis->port.in_ep->name, rndis->port.out_ep->name, 80362306a36Sopenharmony_ci rndis->notify->name); 80462306a36Sopenharmony_ci return 0; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_cifail_free_descs: 80762306a36Sopenharmony_ci usb_free_all_descriptors(f); 80862306a36Sopenharmony_cifail: 80962306a36Sopenharmony_ci kfree(f->os_desc_table); 81062306a36Sopenharmony_ci f->os_desc_n = 0; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci if (rndis->notify_req) { 81362306a36Sopenharmony_ci kfree(rndis->notify_req->buf); 81462306a36Sopenharmony_ci usb_ep_free_request(rndis->notify, rndis->notify_req); 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci return status; 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_civoid rndis_borrow_net(struct usb_function_instance *f, struct net_device *net) 82362306a36Sopenharmony_ci{ 82462306a36Sopenharmony_ci struct f_rndis_opts *opts; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci opts = container_of(f, struct f_rndis_opts, func_inst); 82762306a36Sopenharmony_ci if (opts->bound) 82862306a36Sopenharmony_ci gether_cleanup(netdev_priv(opts->net)); 82962306a36Sopenharmony_ci else 83062306a36Sopenharmony_ci free_netdev(opts->net); 83162306a36Sopenharmony_ci opts->borrowed_net = opts->bound = true; 83262306a36Sopenharmony_ci opts->net = net; 83362306a36Sopenharmony_ci} 83462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rndis_borrow_net); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_cistatic inline struct f_rndis_opts *to_f_rndis_opts(struct config_item *item) 83762306a36Sopenharmony_ci{ 83862306a36Sopenharmony_ci return container_of(to_config_group(item), struct f_rndis_opts, 83962306a36Sopenharmony_ci func_inst.group); 84062306a36Sopenharmony_ci} 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci/* f_rndis_item_ops */ 84362306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM(rndis); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci/* f_rndis_opts_dev_addr */ 84662306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(rndis); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci/* f_rndis_opts_host_addr */ 84962306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(rndis); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci/* f_rndis_opts_qmult */ 85262306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(rndis); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci/* f_rndis_opts_ifname */ 85562306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(rndis); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci/* f_rndis_opts_class */ 85862306a36Sopenharmony_ciUSB_ETHER_CONFIGFS_ITEM_ATTR_U8_RW(rndis, class); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci/* f_rndis_opts_subclass */ 86162306a36Sopenharmony_ciUSB_ETHER_CONFIGFS_ITEM_ATTR_U8_RW(rndis, subclass); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci/* f_rndis_opts_protocol */ 86462306a36Sopenharmony_ciUSB_ETHER_CONFIGFS_ITEM_ATTR_U8_RW(rndis, protocol); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_cistatic struct configfs_attribute *rndis_attrs[] = { 86762306a36Sopenharmony_ci &rndis_opts_attr_dev_addr, 86862306a36Sopenharmony_ci &rndis_opts_attr_host_addr, 86962306a36Sopenharmony_ci &rndis_opts_attr_qmult, 87062306a36Sopenharmony_ci &rndis_opts_attr_ifname, 87162306a36Sopenharmony_ci &rndis_opts_attr_class, 87262306a36Sopenharmony_ci &rndis_opts_attr_subclass, 87362306a36Sopenharmony_ci &rndis_opts_attr_protocol, 87462306a36Sopenharmony_ci NULL, 87562306a36Sopenharmony_ci}; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_cistatic const struct config_item_type rndis_func_type = { 87862306a36Sopenharmony_ci .ct_item_ops = &rndis_item_ops, 87962306a36Sopenharmony_ci .ct_attrs = rndis_attrs, 88062306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 88162306a36Sopenharmony_ci}; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_cistatic void rndis_free_inst(struct usb_function_instance *f) 88462306a36Sopenharmony_ci{ 88562306a36Sopenharmony_ci struct f_rndis_opts *opts; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci opts = container_of(f, struct f_rndis_opts, func_inst); 88862306a36Sopenharmony_ci if (!opts->borrowed_net) { 88962306a36Sopenharmony_ci if (opts->bound) 89062306a36Sopenharmony_ci gether_cleanup(netdev_priv(opts->net)); 89162306a36Sopenharmony_ci else 89262306a36Sopenharmony_ci free_netdev(opts->net); 89362306a36Sopenharmony_ci } 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci kfree(opts->rndis_interf_group); /* single VLA chunk */ 89662306a36Sopenharmony_ci kfree(opts); 89762306a36Sopenharmony_ci} 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_cistatic struct usb_function_instance *rndis_alloc_inst(void) 90062306a36Sopenharmony_ci{ 90162306a36Sopenharmony_ci struct f_rndis_opts *opts; 90262306a36Sopenharmony_ci struct usb_os_desc *descs[1]; 90362306a36Sopenharmony_ci char *names[1]; 90462306a36Sopenharmony_ci struct config_group *rndis_interf_group; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci opts = kzalloc(sizeof(*opts), GFP_KERNEL); 90762306a36Sopenharmony_ci if (!opts) 90862306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 90962306a36Sopenharmony_ci opts->rndis_os_desc.ext_compat_id = opts->rndis_ext_compat_id; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci mutex_init(&opts->lock); 91262306a36Sopenharmony_ci opts->func_inst.free_func_inst = rndis_free_inst; 91362306a36Sopenharmony_ci opts->net = gether_setup_default(); 91462306a36Sopenharmony_ci if (IS_ERR(opts->net)) { 91562306a36Sopenharmony_ci struct net_device *net = opts->net; 91662306a36Sopenharmony_ci kfree(opts); 91762306a36Sopenharmony_ci return ERR_CAST(net); 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci INIT_LIST_HEAD(&opts->rndis_os_desc.ext_prop); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci opts->class = rndis_iad_descriptor.bFunctionClass; 92262306a36Sopenharmony_ci opts->subclass = rndis_iad_descriptor.bFunctionSubClass; 92362306a36Sopenharmony_ci opts->protocol = rndis_iad_descriptor.bFunctionProtocol; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci descs[0] = &opts->rndis_os_desc; 92662306a36Sopenharmony_ci names[0] = "rndis"; 92762306a36Sopenharmony_ci config_group_init_type_name(&opts->func_inst.group, "", 92862306a36Sopenharmony_ci &rndis_func_type); 92962306a36Sopenharmony_ci rndis_interf_group = 93062306a36Sopenharmony_ci usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs, 93162306a36Sopenharmony_ci names, THIS_MODULE); 93262306a36Sopenharmony_ci if (IS_ERR(rndis_interf_group)) { 93362306a36Sopenharmony_ci rndis_free_inst(&opts->func_inst); 93462306a36Sopenharmony_ci return ERR_CAST(rndis_interf_group); 93562306a36Sopenharmony_ci } 93662306a36Sopenharmony_ci opts->rndis_interf_group = rndis_interf_group; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci return &opts->func_inst; 93962306a36Sopenharmony_ci} 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_cistatic void rndis_free(struct usb_function *f) 94262306a36Sopenharmony_ci{ 94362306a36Sopenharmony_ci struct f_rndis *rndis; 94462306a36Sopenharmony_ci struct f_rndis_opts *opts; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci rndis = func_to_rndis(f); 94762306a36Sopenharmony_ci rndis_deregister(rndis->params); 94862306a36Sopenharmony_ci opts = container_of(f->fi, struct f_rndis_opts, func_inst); 94962306a36Sopenharmony_ci kfree(rndis); 95062306a36Sopenharmony_ci mutex_lock(&opts->lock); 95162306a36Sopenharmony_ci opts->refcnt--; 95262306a36Sopenharmony_ci mutex_unlock(&opts->lock); 95362306a36Sopenharmony_ci} 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_cistatic void rndis_unbind(struct usb_configuration *c, struct usb_function *f) 95662306a36Sopenharmony_ci{ 95762306a36Sopenharmony_ci struct f_rndis *rndis = func_to_rndis(f); 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci kfree(f->os_desc_table); 96062306a36Sopenharmony_ci f->os_desc_n = 0; 96162306a36Sopenharmony_ci usb_free_all_descriptors(f); 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci kfree(rndis->notify_req->buf); 96462306a36Sopenharmony_ci usb_ep_free_request(rndis->notify, rndis->notify_req); 96562306a36Sopenharmony_ci} 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_cistatic struct usb_function *rndis_alloc(struct usb_function_instance *fi) 96862306a36Sopenharmony_ci{ 96962306a36Sopenharmony_ci struct f_rndis *rndis; 97062306a36Sopenharmony_ci struct f_rndis_opts *opts; 97162306a36Sopenharmony_ci struct rndis_params *params; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci /* allocate and initialize one new instance */ 97462306a36Sopenharmony_ci rndis = kzalloc(sizeof(*rndis), GFP_KERNEL); 97562306a36Sopenharmony_ci if (!rndis) 97662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci opts = container_of(fi, struct f_rndis_opts, func_inst); 97962306a36Sopenharmony_ci mutex_lock(&opts->lock); 98062306a36Sopenharmony_ci opts->refcnt++; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci gether_get_host_addr_u8(opts->net, rndis->ethaddr); 98362306a36Sopenharmony_ci rndis->vendorID = opts->vendor_id; 98462306a36Sopenharmony_ci rndis->manufacturer = opts->manufacturer; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci rndis->port.ioport = netdev_priv(opts->net); 98762306a36Sopenharmony_ci mutex_unlock(&opts->lock); 98862306a36Sopenharmony_ci /* RNDIS activates when the host changes this filter */ 98962306a36Sopenharmony_ci rndis->port.cdc_filter = 0; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci /* RNDIS has special (and complex) framing */ 99262306a36Sopenharmony_ci rndis->port.header_len = sizeof(struct rndis_packet_msg_type); 99362306a36Sopenharmony_ci rndis->port.wrap = rndis_add_header; 99462306a36Sopenharmony_ci rndis->port.unwrap = rndis_rm_hdr; 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci rndis->port.func.name = "rndis"; 99762306a36Sopenharmony_ci /* descriptors are per-instance copies */ 99862306a36Sopenharmony_ci rndis->port.func.bind = rndis_bind; 99962306a36Sopenharmony_ci rndis->port.func.unbind = rndis_unbind; 100062306a36Sopenharmony_ci rndis->port.func.set_alt = rndis_set_alt; 100162306a36Sopenharmony_ci rndis->port.func.setup = rndis_setup; 100262306a36Sopenharmony_ci rndis->port.func.disable = rndis_disable; 100362306a36Sopenharmony_ci rndis->port.func.free_func = rndis_free; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci params = rndis_register(rndis_response_available, rndis); 100662306a36Sopenharmony_ci if (IS_ERR(params)) { 100762306a36Sopenharmony_ci kfree(rndis); 100862306a36Sopenharmony_ci return ERR_CAST(params); 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci rndis->params = params; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci return &rndis->port.func; 101362306a36Sopenharmony_ci} 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ciDECLARE_USB_FUNCTION_INIT(rndis, rndis_alloc_inst, rndis_alloc); 101662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 101762306a36Sopenharmony_ciMODULE_AUTHOR("David Brownell"); 1018