1812 lines · 47.8 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0+/* * f_ncm.c -- USB CDC Network (NCM) link function driver * * Copyright (C) 2010 Nokia Corporation * Contact: Yauheni Kaliuta <yauheni.kaliuta@nokia.com> * * The driver borrows from f_ecm.c which is: * * Copyright (C) 2003-2005,2008 David Brownell * Copyright (C) 2008 Nokia Corporation */ #include <linux/cleanup.h>#include <linux/kernel.h>#include <linux/interrupt.h>#include <linux/module.h>#include <linux/device.h>#include <linux/etherdevice.h>#include <linux/crc32.h>#include <linux/string_choices.h> #include <linux/usb/cdc.h>#include <linux/usb/gadget.h> #include "u_ether.h"#include "u_ether_configfs.h"#include "u_ncm.h"#include "configfs.h" /* * This function is a "CDC Network Control Model" (CDC NCM) Ethernet link. * NCM is intended to be used with high-speed network attachments. * * Note that NCM requires the use of "alternate settings" for its data * interface.  This means that the set_alt() method has real work to do, * and also means that a get_alt() method is required. */ /* to trigger crc/non-crc ndp signature */ #define NCM_NDP_HDR_CRC		0x01000000 enum ncm_notify_state {	NCM_NOTIFY_NONE,		/* don't notify */	NCM_NOTIFY_CONNECT,		/* issue CONNECT next */	NCM_NOTIFY_SPEED,		/* issue SPEED_CHANGE next */}; struct f_ncm {	struct gether			port;	u8				ctrl_id, data_id; 	char				ethaddr[14]; 	struct usb_ep			*notify;	struct usb_request		*notify_req;	u8				notify_state;	atomic_t			notify_count;	bool				is_open; 	const struct ndp_parser_opts	*parser_opts;	bool				is_crc;	u32				ndp_sign; 	/*	 * for notification, it is accessed from both	 * callback and ethernet open/close	 */	spinlock_t			lock; 	struct net_device		*netdev; 	/* For multi-frame NDP TX */	struct sk_buff			*skb_tx_data;	struct sk_buff			*skb_tx_ndp;	u16				ndp_dgram_count;	struct hrtimer			task_timer;}; static inline struct f_ncm *func_to_ncm(struct usb_function *f){	return container_of(f, struct f_ncm, port.func);} /*-------------------------------------------------------------------------*/ /* * We cannot group frames so use just the minimal size which ok to put * one max-size ethernet frame. * If the host can group frames, allow it to do that, 16K is selected, * because it's used by default by the current linux host driver */#define NTB_DEFAULT_IN_SIZE	16384#define NTB_OUT_SIZE		16384 /* Allocation for storing the NDP, 32 should suffice for a * 16k packet. This allows a maximum of 32 * 507 Byte packets to * be transmitted in a single 16kB skb, though when sending full size * packets this limit will be plenty. * Smaller packets are not likely to be trying to maximize the * throughput and will be mstly sending smaller infrequent frames. */#define TX_MAX_NUM_DPE		32 /* Delay for the transmit to wait before sending an unfilled NTB frame. */#define TX_TIMEOUT_NSECS	300000 /* * Although max mtu as dictated by u_ether is 15412 bytes, setting * max_segment_size to 15426 would not be efficient. If user chooses segment * size to be (>= 8192), then we can't aggregate more than one buffer in each * NTB (assuming each packet coming from network layer is >= 8192 bytes) as ep * maxpacket limit is 16384. So let max_segment_size be limited to 8000 to allow * at least 2 packets to be aggregated reducing wastage of NTB buffer space */#define MAX_DATAGRAM_SIZE	8000 #define FORMATS_SUPPORTED	(USB_CDC_NCM_NTB16_SUPPORTED |	\				 USB_CDC_NCM_NTB32_SUPPORTED) static struct usb_cdc_ncm_ntb_parameters ntb_parameters = {	.wLength = cpu_to_le16(sizeof(ntb_parameters)),	.bmNtbFormatsSupported = cpu_to_le16(FORMATS_SUPPORTED),	.dwNtbInMaxSize = cpu_to_le32(NTB_DEFAULT_IN_SIZE),	.wNdpInDivisor = cpu_to_le16(4),	.wNdpInPayloadRemainder = cpu_to_le16(0),	.wNdpInAlignment = cpu_to_le16(4), 	.dwNtbOutMaxSize = cpu_to_le32(NTB_OUT_SIZE),	.wNdpOutDivisor = cpu_to_le16(4),	.wNdpOutPayloadRemainder = cpu_to_le16(0),	.wNdpOutAlignment = cpu_to_le16(4),}; /* * Use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one * packet, to simplify cancellation; and a big transfer interval, to * waste less bandwidth. */ #define NCM_STATUS_INTERVAL_MS		32#define NCM_STATUS_BYTECOUNT		16	/* 8 byte header + data */ static struct usb_interface_assoc_descriptor ncm_iad_desc = {	.bLength =		sizeof ncm_iad_desc,	.bDescriptorType =	USB_DT_INTERFACE_ASSOCIATION, 	/* .bFirstInterface =	DYNAMIC, */	.bInterfaceCount =	2,	/* control + data */	.bFunctionClass =	USB_CLASS_COMM,	.bFunctionSubClass =	USB_CDC_SUBCLASS_NCM,	.bFunctionProtocol =	USB_CDC_PROTO_NONE,	/* .iFunction =		DYNAMIC */}; /* interface descriptor: */ static struct usb_interface_descriptor ncm_control_intf = {	.bLength =		sizeof ncm_control_intf,	.bDescriptorType =	USB_DT_INTERFACE, 	/* .bInterfaceNumber = DYNAMIC */	.bNumEndpoints =	1,	.bInterfaceClass =	USB_CLASS_COMM,	.bInterfaceSubClass =	USB_CDC_SUBCLASS_NCM,	.bInterfaceProtocol =	USB_CDC_PROTO_NONE,	/* .iInterface = DYNAMIC */}; static struct usb_cdc_header_desc ncm_header_desc = {	.bLength =		sizeof ncm_header_desc,	.bDescriptorType =	USB_DT_CS_INTERFACE,	.bDescriptorSubType =	USB_CDC_HEADER_TYPE, 	.bcdCDC =		cpu_to_le16(0x0110),}; static struct usb_cdc_union_desc ncm_union_desc = {	.bLength =		sizeof(ncm_union_desc),	.bDescriptorType =	USB_DT_CS_INTERFACE,	.bDescriptorSubType =	USB_CDC_UNION_TYPE,	/* .bMasterInterface0 =	DYNAMIC */	/* .bSlaveInterface0 =	DYNAMIC */}; static struct usb_cdc_ether_desc ecm_desc = {	.bLength =		sizeof ecm_desc,	.bDescriptorType =	USB_DT_CS_INTERFACE,	.bDescriptorSubType =	USB_CDC_ETHERNET_TYPE, 	/* this descriptor actually adds value, surprise! */	/* .iMACAddress = DYNAMIC */	.bmEthernetStatistics =	cpu_to_le32(0), /* no statistics */	.wNumberMCFilters =	cpu_to_le16(0),	.bNumberPowerFilters =	0,}; #define NCAPS	(USB_CDC_NCM_NCAP_ETH_FILTER | USB_CDC_NCM_NCAP_CRC_MODE) static struct usb_cdc_ncm_desc ncm_desc = {	.bLength =		sizeof ncm_desc,	.bDescriptorType =	USB_DT_CS_INTERFACE,	.bDescriptorSubType =	USB_CDC_NCM_TYPE, 	.bcdNcmVersion =	cpu_to_le16(0x0100),	/* can process SetEthernetPacketFilter */	.bmNetworkCapabilities = NCAPS,}; /* the default data interface has no endpoints ... */ static struct usb_interface_descriptor ncm_data_nop_intf = {	.bLength =		sizeof ncm_data_nop_intf,	.bDescriptorType =	USB_DT_INTERFACE, 	.bInterfaceNumber =	1,	.bAlternateSetting =	0,	.bNumEndpoints =	0,	.bInterfaceClass =	USB_CLASS_CDC_DATA,	.bInterfaceSubClass =	0,	.bInterfaceProtocol =	USB_CDC_NCM_PROTO_NTB,	/* .iInterface = DYNAMIC */}; /* ... but the "real" data interface has two bulk endpoints */ static struct usb_interface_descriptor ncm_data_intf = {	.bLength =		sizeof ncm_data_intf,	.bDescriptorType =	USB_DT_INTERFACE, 	.bInterfaceNumber =	1,	.bAlternateSetting =	1,	.bNumEndpoints =	2,	.bInterfaceClass =	USB_CLASS_CDC_DATA,	.bInterfaceSubClass =	0,	.bInterfaceProtocol =	USB_CDC_NCM_PROTO_NTB,	/* .iInterface = DYNAMIC */}; /* full speed support: */ static struct usb_endpoint_descriptor fs_ncm_notify_desc = {	.bLength =		USB_DT_ENDPOINT_SIZE,	.bDescriptorType =	USB_DT_ENDPOINT, 	.bEndpointAddress =	USB_DIR_IN,	.bmAttributes =		USB_ENDPOINT_XFER_INT,	.wMaxPacketSize =	cpu_to_le16(NCM_STATUS_BYTECOUNT),	.bInterval =		NCM_STATUS_INTERVAL_MS,}; static struct usb_endpoint_descriptor fs_ncm_in_desc = {	.bLength =		USB_DT_ENDPOINT_SIZE,	.bDescriptorType =	USB_DT_ENDPOINT, 	.bEndpointAddress =	USB_DIR_IN,	.bmAttributes =		USB_ENDPOINT_XFER_BULK,}; static struct usb_endpoint_descriptor fs_ncm_out_desc = {	.bLength =		USB_DT_ENDPOINT_SIZE,	.bDescriptorType =	USB_DT_ENDPOINT, 	.bEndpointAddress =	USB_DIR_OUT,	.bmAttributes =		USB_ENDPOINT_XFER_BULK,}; static struct usb_descriptor_header *ncm_fs_function[] = {	(struct usb_descriptor_header *) &ncm_iad_desc,	/* CDC NCM control descriptors */	(struct usb_descriptor_header *) &ncm_control_intf,	(struct usb_descriptor_header *) &ncm_header_desc,	(struct usb_descriptor_header *) &ncm_union_desc,	(struct usb_descriptor_header *) &ecm_desc,	(struct usb_descriptor_header *) &ncm_desc,	(struct usb_descriptor_header *) &fs_ncm_notify_desc,	/* data interface, altsettings 0 and 1 */	(struct usb_descriptor_header *) &ncm_data_nop_intf,	(struct usb_descriptor_header *) &ncm_data_intf,	(struct usb_descriptor_header *) &fs_ncm_in_desc,	(struct usb_descriptor_header *) &fs_ncm_out_desc,	NULL,}; /* high speed support: */ static struct usb_endpoint_descriptor hs_ncm_notify_desc = {	.bLength =		USB_DT_ENDPOINT_SIZE,	.bDescriptorType =	USB_DT_ENDPOINT, 	.bEndpointAddress =	USB_DIR_IN,	.bmAttributes =		USB_ENDPOINT_XFER_INT,	.wMaxPacketSize =	cpu_to_le16(NCM_STATUS_BYTECOUNT),	.bInterval =		USB_MS_TO_HS_INTERVAL(NCM_STATUS_INTERVAL_MS),};static struct usb_endpoint_descriptor hs_ncm_in_desc = {	.bLength =		USB_DT_ENDPOINT_SIZE,	.bDescriptorType =	USB_DT_ENDPOINT, 	.bEndpointAddress =	USB_DIR_IN,	.bmAttributes =		USB_ENDPOINT_XFER_BULK,	.wMaxPacketSize =	cpu_to_le16(512),}; static struct usb_endpoint_descriptor hs_ncm_out_desc = {	.bLength =		USB_DT_ENDPOINT_SIZE,	.bDescriptorType =	USB_DT_ENDPOINT, 	.bEndpointAddress =	USB_DIR_OUT,	.bmAttributes =		USB_ENDPOINT_XFER_BULK,	.wMaxPacketSize =	cpu_to_le16(512),}; static struct usb_descriptor_header *ncm_hs_function[] = {	(struct usb_descriptor_header *) &ncm_iad_desc,	/* CDC NCM control descriptors */	(struct usb_descriptor_header *) &ncm_control_intf,	(struct usb_descriptor_header *) &ncm_header_desc,	(struct usb_descriptor_header *) &ncm_union_desc,	(struct usb_descriptor_header *) &ecm_desc,	(struct usb_descriptor_header *) &ncm_desc,	(struct usb_descriptor_header *) &hs_ncm_notify_desc,	/* data interface, altsettings 0 and 1 */	(struct usb_descriptor_header *) &ncm_data_nop_intf,	(struct usb_descriptor_header *) &ncm_data_intf,	(struct usb_descriptor_header *) &hs_ncm_in_desc,	(struct usb_descriptor_header *) &hs_ncm_out_desc,	NULL,};  /* super speed support: */ static struct usb_endpoint_descriptor ss_ncm_notify_desc = {	.bLength =		USB_DT_ENDPOINT_SIZE,	.bDescriptorType =	USB_DT_ENDPOINT, 	.bEndpointAddress =	USB_DIR_IN,	.bmAttributes =		USB_ENDPOINT_XFER_INT,	.wMaxPacketSize =	cpu_to_le16(NCM_STATUS_BYTECOUNT),	.bInterval =		USB_MS_TO_HS_INTERVAL(NCM_STATUS_INTERVAL_MS)}; static struct usb_ss_ep_comp_descriptor ss_ncm_notify_comp_desc = {	.bLength =		sizeof(ss_ncm_notify_comp_desc),	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP, 	/* the following 3 values can be tweaked if necessary */	/* .bMaxBurst =		0, */	/* .bmAttributes =	0, */	.wBytesPerInterval =	cpu_to_le16(NCM_STATUS_BYTECOUNT),}; static struct usb_endpoint_descriptor ss_ncm_in_desc = {	.bLength =		USB_DT_ENDPOINT_SIZE,	.bDescriptorType =	USB_DT_ENDPOINT, 	.bEndpointAddress =	USB_DIR_IN,	.bmAttributes =		USB_ENDPOINT_XFER_BULK,	.wMaxPacketSize =	cpu_to_le16(1024),}; static struct usb_endpoint_descriptor ss_ncm_out_desc = {	.bLength =		USB_DT_ENDPOINT_SIZE,	.bDescriptorType =	USB_DT_ENDPOINT, 	.bEndpointAddress =	USB_DIR_OUT,	.bmAttributes =		USB_ENDPOINT_XFER_BULK,	.wMaxPacketSize =	cpu_to_le16(1024),}; static struct usb_ss_ep_comp_descriptor ss_ncm_bulk_comp_desc = {	.bLength =		sizeof(ss_ncm_bulk_comp_desc),	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP, 	/* the following 2 values can be tweaked if necessary */	.bMaxBurst =		15,	/* .bmAttributes =	0, */}; static struct usb_descriptor_header *ncm_ss_function[] = {	(struct usb_descriptor_header *) &ncm_iad_desc,	/* CDC NCM control descriptors */	(struct usb_descriptor_header *) &ncm_control_intf,	(struct usb_descriptor_header *) &ncm_header_desc,	(struct usb_descriptor_header *) &ncm_union_desc,	(struct usb_descriptor_header *) &ecm_desc,	(struct usb_descriptor_header *) &ncm_desc,	(struct usb_descriptor_header *) &ss_ncm_notify_desc,	(struct usb_descriptor_header *) &ss_ncm_notify_comp_desc,	/* data interface, altsettings 0 and 1 */	(struct usb_descriptor_header *) &ncm_data_nop_intf,	(struct usb_descriptor_header *) &ncm_data_intf,	(struct usb_descriptor_header *) &ss_ncm_in_desc,	(struct usb_descriptor_header *) &ss_ncm_bulk_comp_desc,	(struct usb_descriptor_header *) &ss_ncm_out_desc,	(struct usb_descriptor_header *) &ss_ncm_bulk_comp_desc,	NULL,}; /* string descriptors: */ #define STRING_CTRL_IDX	0#define STRING_MAC_IDX	1#define STRING_DATA_IDX	2#define STRING_IAD_IDX	3 static struct usb_string ncm_string_defs[] = {	[STRING_CTRL_IDX].s = "CDC Network Control Model (NCM)",	[STRING_MAC_IDX].s = "",	[STRING_DATA_IDX].s = "CDC Network Data",	[STRING_IAD_IDX].s = "CDC NCM",	{  } /* end of list */}; static struct usb_gadget_strings ncm_string_table = {	.language =		0x0409,	/* en-us */	.strings =		ncm_string_defs,}; static struct usb_gadget_strings *ncm_strings[] = {	&ncm_string_table,	NULL,}; /* * Here are options for NCM Datagram Pointer table (NDP) parser. * There are 2 different formats: NDP16 and NDP32 in the spec (ch. 3), * in NDP16 offsets and sizes fields are 1 16bit word wide, * in NDP32 -- 2 16bit words wide. Also signatures are different. * To make the parser code the same, put the differences in the structure, * and switch pointers to the structures when the format is changed. */ struct ndp_parser_opts {	u32		nth_sign;	u32		ndp_sign;	unsigned	nth_size;	unsigned	ndp_size;	unsigned	dpe_size;	unsigned	ndplen_align;	/* sizes in u16 units */	unsigned	dgram_item_len; /* index or length */	unsigned	block_length;	unsigned	ndp_index;	unsigned	reserved1;	unsigned	reserved2;	unsigned	next_ndp_index;}; static const struct ndp_parser_opts ndp16_opts = {	.nth_sign = USB_CDC_NCM_NTH16_SIGN,	.ndp_sign = USB_CDC_NCM_NDP16_NOCRC_SIGN,	.nth_size = sizeof(struct usb_cdc_ncm_nth16),	.ndp_size = sizeof(struct usb_cdc_ncm_ndp16),	.dpe_size = sizeof(struct usb_cdc_ncm_dpe16),	.ndplen_align = 4,	.dgram_item_len = 1,	.block_length = 1,	.ndp_index = 1,	.reserved1 = 0,	.reserved2 = 0,	.next_ndp_index = 1,}; static const struct ndp_parser_opts ndp32_opts = {	.nth_sign = USB_CDC_NCM_NTH32_SIGN,	.ndp_sign = USB_CDC_NCM_NDP32_NOCRC_SIGN,	.nth_size = sizeof(struct usb_cdc_ncm_nth32),	.ndp_size = sizeof(struct usb_cdc_ncm_ndp32),	.dpe_size = sizeof(struct usb_cdc_ncm_dpe32),	.ndplen_align = 8,	.dgram_item_len = 2,	.block_length = 2,	.ndp_index = 2,	.reserved1 = 1,	.reserved2 = 2,	.next_ndp_index = 2,}; static inline void put_ncm(__le16 **p, unsigned size, unsigned val){	switch (size) {	case 1:		put_unaligned_le16((u16)val, *p);		break;	case 2:		put_unaligned_le32((u32)val, *p); 		break;	default:		BUG();	} 	*p += size;} static inline unsigned get_ncm(__le16 **p, unsigned size){

Lines 1–500 of 1812.