xref: /linux/drivers/usb/gadget/function/f_ncm.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
200a2430fSAndrzej Pietrasiewicz /*
300a2430fSAndrzej Pietrasiewicz  * f_ncm.c -- USB CDC Network (NCM) link function driver
400a2430fSAndrzej Pietrasiewicz  *
500a2430fSAndrzej Pietrasiewicz  * Copyright (C) 2010 Nokia Corporation
600a2430fSAndrzej Pietrasiewicz  * Contact: Yauheni Kaliuta <yauheni.kaliuta@nokia.com>
700a2430fSAndrzej Pietrasiewicz  *
800a2430fSAndrzej Pietrasiewicz  * The driver borrows from f_ecm.c which is:
900a2430fSAndrzej Pietrasiewicz  *
1000a2430fSAndrzej Pietrasiewicz  * Copyright (C) 2003-2005,2008 David Brownell
1100a2430fSAndrzej Pietrasiewicz  * Copyright (C) 2008 Nokia Corporation
1200a2430fSAndrzej Pietrasiewicz  */
1300a2430fSAndrzej Pietrasiewicz 
1400a2430fSAndrzej Pietrasiewicz #include <linux/kernel.h>
15282ccf6eSFlorian Westphal #include <linux/interrupt.h>
1600a2430fSAndrzej Pietrasiewicz #include <linux/module.h>
1700a2430fSAndrzej Pietrasiewicz #include <linux/device.h>
1800a2430fSAndrzej Pietrasiewicz #include <linux/etherdevice.h>
1900a2430fSAndrzej Pietrasiewicz #include <linux/crc32.h>
2000a2430fSAndrzej Pietrasiewicz 
2100a2430fSAndrzej Pietrasiewicz #include <linux/usb/cdc.h>
2200a2430fSAndrzej Pietrasiewicz 
2300a2430fSAndrzej Pietrasiewicz #include "u_ether.h"
2400a2430fSAndrzej Pietrasiewicz #include "u_ether_configfs.h"
2500a2430fSAndrzej Pietrasiewicz #include "u_ncm.h"
2679340929SRomain Izard #include "configfs.h"
2700a2430fSAndrzej Pietrasiewicz 
2800a2430fSAndrzej Pietrasiewicz /*
2900a2430fSAndrzej Pietrasiewicz  * This function is a "CDC Network Control Model" (CDC NCM) Ethernet link.
3000a2430fSAndrzej Pietrasiewicz  * NCM is intended to be used with high-speed network attachments.
3100a2430fSAndrzej Pietrasiewicz  *
3200a2430fSAndrzej Pietrasiewicz  * Note that NCM requires the use of "alternate settings" for its data
3300a2430fSAndrzej Pietrasiewicz  * interface.  This means that the set_alt() method has real work to do,
3400a2430fSAndrzej Pietrasiewicz  * and also means that a get_alt() method is required.
3500a2430fSAndrzej Pietrasiewicz  */
3600a2430fSAndrzej Pietrasiewicz 
3700a2430fSAndrzej Pietrasiewicz /* to trigger crc/non-crc ndp signature */
3800a2430fSAndrzej Pietrasiewicz 
3900a2430fSAndrzej Pietrasiewicz #define NCM_NDP_HDR_CRC		0x01000000
4000a2430fSAndrzej Pietrasiewicz 
4100a2430fSAndrzej Pietrasiewicz enum ncm_notify_state {
4200a2430fSAndrzej Pietrasiewicz 	NCM_NOTIFY_NONE,		/* don't notify */
4300a2430fSAndrzej Pietrasiewicz 	NCM_NOTIFY_CONNECT,		/* issue CONNECT next */
4400a2430fSAndrzej Pietrasiewicz 	NCM_NOTIFY_SPEED,		/* issue SPEED_CHANGE next */
4500a2430fSAndrzej Pietrasiewicz };
4600a2430fSAndrzej Pietrasiewicz 
4700a2430fSAndrzej Pietrasiewicz struct f_ncm {
4800a2430fSAndrzej Pietrasiewicz 	struct gether			port;
4900a2430fSAndrzej Pietrasiewicz 	u8				ctrl_id, data_id;
5000a2430fSAndrzej Pietrasiewicz 
5100a2430fSAndrzej Pietrasiewicz 	char				ethaddr[14];
5200a2430fSAndrzej Pietrasiewicz 
5300a2430fSAndrzej Pietrasiewicz 	struct usb_ep			*notify;
5400a2430fSAndrzej Pietrasiewicz 	struct usb_request		*notify_req;
5500a2430fSAndrzej Pietrasiewicz 	u8				notify_state;
565b24c28cSBryan O'Donoghue 	atomic_t			notify_count;
5700a2430fSAndrzej Pietrasiewicz 	bool				is_open;
5800a2430fSAndrzej Pietrasiewicz 
5900a2430fSAndrzej Pietrasiewicz 	const struct ndp_parser_opts	*parser_opts;
6000a2430fSAndrzej Pietrasiewicz 	bool				is_crc;
6100a2430fSAndrzej Pietrasiewicz 	u32				ndp_sign;
6200a2430fSAndrzej Pietrasiewicz 
6300a2430fSAndrzej Pietrasiewicz 	/*
6400a2430fSAndrzej Pietrasiewicz 	 * for notification, it is accessed from both
6500a2430fSAndrzej Pietrasiewicz 	 * callback and ethernet open/close
6600a2430fSAndrzej Pietrasiewicz 	 */
6700a2430fSAndrzej Pietrasiewicz 	spinlock_t			lock;
6800a2430fSAndrzej Pietrasiewicz 
6900a2430fSAndrzej Pietrasiewicz 	struct net_device		*netdev;
7000a2430fSAndrzej Pietrasiewicz 
7100a2430fSAndrzej Pietrasiewicz 	/* For multi-frame NDP TX */
7200a2430fSAndrzej Pietrasiewicz 	struct sk_buff			*skb_tx_data;
7300a2430fSAndrzej Pietrasiewicz 	struct sk_buff			*skb_tx_ndp;
7400a2430fSAndrzej Pietrasiewicz 	u16				ndp_dgram_count;
7500a2430fSAndrzej Pietrasiewicz 	struct hrtimer			task_timer;
7600a2430fSAndrzej Pietrasiewicz };
7700a2430fSAndrzej Pietrasiewicz 
func_to_ncm(struct usb_function * f)7800a2430fSAndrzej Pietrasiewicz static inline struct f_ncm *func_to_ncm(struct usb_function *f)
7900a2430fSAndrzej Pietrasiewicz {
8000a2430fSAndrzej Pietrasiewicz 	return container_of(f, struct f_ncm, port.func);
8100a2430fSAndrzej Pietrasiewicz }
8200a2430fSAndrzej Pietrasiewicz 
8300a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
8400a2430fSAndrzej Pietrasiewicz 
8500a2430fSAndrzej Pietrasiewicz /*
8600a2430fSAndrzej Pietrasiewicz  * We cannot group frames so use just the minimal size which ok to put
8700a2430fSAndrzej Pietrasiewicz  * one max-size ethernet frame.
8800a2430fSAndrzej Pietrasiewicz  * If the host can group frames, allow it to do that, 16K is selected,
8900a2430fSAndrzej Pietrasiewicz  * because it's used by default by the current linux host driver
9000a2430fSAndrzej Pietrasiewicz  */
9100a2430fSAndrzej Pietrasiewicz #define NTB_DEFAULT_IN_SIZE	16384
9200a2430fSAndrzej Pietrasiewicz #define NTB_OUT_SIZE		16384
9300a2430fSAndrzej Pietrasiewicz 
9400a2430fSAndrzej Pietrasiewicz /* Allocation for storing the NDP, 32 should suffice for a
9500a2430fSAndrzej Pietrasiewicz  * 16k packet. This allows a maximum of 32 * 507 Byte packets to
9600a2430fSAndrzej Pietrasiewicz  * be transmitted in a single 16kB skb, though when sending full size
9700a2430fSAndrzej Pietrasiewicz  * packets this limit will be plenty.
9800a2430fSAndrzej Pietrasiewicz  * Smaller packets are not likely to be trying to maximize the
9900a2430fSAndrzej Pietrasiewicz  * throughput and will be mstly sending smaller infrequent frames.
10000a2430fSAndrzej Pietrasiewicz  */
10100a2430fSAndrzej Pietrasiewicz #define TX_MAX_NUM_DPE		32
10200a2430fSAndrzej Pietrasiewicz 
10300a2430fSAndrzej Pietrasiewicz /* Delay for the transmit to wait before sending an unfilled NTB frame. */
10400a2430fSAndrzej Pietrasiewicz #define TX_TIMEOUT_NSECS	300000
10500a2430fSAndrzej Pietrasiewicz 
1061900daeeSKrishna Kurapati /*
1071900daeeSKrishna Kurapati  * Although max mtu as dictated by u_ether is 15412 bytes, setting
1081900daeeSKrishna Kurapati  * max_segment_size to 15426 would not be efficient. If user chooses segment
1091900daeeSKrishna Kurapati  * size to be (>= 8192), then we can't aggregate more than one buffer in each
1101900daeeSKrishna Kurapati  * NTB (assuming each packet coming from network layer is >= 8192 bytes) as ep
1111900daeeSKrishna Kurapati  * maxpacket limit is 16384. So let max_segment_size be limited to 8000 to allow
1121900daeeSKrishna Kurapati  * at least 2 packets to be aggregated reducing wastage of NTB buffer space
1131900daeeSKrishna Kurapati  */
1141900daeeSKrishna Kurapati #define MAX_DATAGRAM_SIZE	8000
1151900daeeSKrishna Kurapati 
11600a2430fSAndrzej Pietrasiewicz #define FORMATS_SUPPORTED	(USB_CDC_NCM_NTB16_SUPPORTED |	\
11700a2430fSAndrzej Pietrasiewicz 				 USB_CDC_NCM_NTB32_SUPPORTED)
11800a2430fSAndrzej Pietrasiewicz 
11900a2430fSAndrzej Pietrasiewicz static struct usb_cdc_ncm_ntb_parameters ntb_parameters = {
12000a2430fSAndrzej Pietrasiewicz 	.wLength = cpu_to_le16(sizeof(ntb_parameters)),
12100a2430fSAndrzej Pietrasiewicz 	.bmNtbFormatsSupported = cpu_to_le16(FORMATS_SUPPORTED),
12200a2430fSAndrzej Pietrasiewicz 	.dwNtbInMaxSize = cpu_to_le32(NTB_DEFAULT_IN_SIZE),
12300a2430fSAndrzej Pietrasiewicz 	.wNdpInDivisor = cpu_to_le16(4),
12400a2430fSAndrzej Pietrasiewicz 	.wNdpInPayloadRemainder = cpu_to_le16(0),
12500a2430fSAndrzej Pietrasiewicz 	.wNdpInAlignment = cpu_to_le16(4),
12600a2430fSAndrzej Pietrasiewicz 
12700a2430fSAndrzej Pietrasiewicz 	.dwNtbOutMaxSize = cpu_to_le32(NTB_OUT_SIZE),
12800a2430fSAndrzej Pietrasiewicz 	.wNdpOutDivisor = cpu_to_le16(4),
12900a2430fSAndrzej Pietrasiewicz 	.wNdpOutPayloadRemainder = cpu_to_le16(0),
13000a2430fSAndrzej Pietrasiewicz 	.wNdpOutAlignment = cpu_to_le16(4),
13100a2430fSAndrzej Pietrasiewicz };
13200a2430fSAndrzej Pietrasiewicz 
13300a2430fSAndrzej Pietrasiewicz /*
13400a2430fSAndrzej Pietrasiewicz  * Use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one
13500a2430fSAndrzej Pietrasiewicz  * packet, to simplify cancellation; and a big transfer interval, to
13600a2430fSAndrzej Pietrasiewicz  * waste less bandwidth.
13700a2430fSAndrzej Pietrasiewicz  */
13800a2430fSAndrzej Pietrasiewicz 
13900a2430fSAndrzej Pietrasiewicz #define NCM_STATUS_INTERVAL_MS		32
14000a2430fSAndrzej Pietrasiewicz #define NCM_STATUS_BYTECOUNT		16	/* 8 byte header + data */
14100a2430fSAndrzej Pietrasiewicz 
14200a2430fSAndrzej Pietrasiewicz static struct usb_interface_assoc_descriptor ncm_iad_desc = {
14300a2430fSAndrzej Pietrasiewicz 	.bLength =		sizeof ncm_iad_desc,
14400a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_INTERFACE_ASSOCIATION,
14500a2430fSAndrzej Pietrasiewicz 
14600a2430fSAndrzej Pietrasiewicz 	/* .bFirstInterface =	DYNAMIC, */
14700a2430fSAndrzej Pietrasiewicz 	.bInterfaceCount =	2,	/* control + data */
14800a2430fSAndrzej Pietrasiewicz 	.bFunctionClass =	USB_CLASS_COMM,
14900a2430fSAndrzej Pietrasiewicz 	.bFunctionSubClass =	USB_CDC_SUBCLASS_NCM,
15000a2430fSAndrzej Pietrasiewicz 	.bFunctionProtocol =	USB_CDC_PROTO_NONE,
15100a2430fSAndrzej Pietrasiewicz 	/* .iFunction =		DYNAMIC */
15200a2430fSAndrzej Pietrasiewicz };
15300a2430fSAndrzej Pietrasiewicz 
15400a2430fSAndrzej Pietrasiewicz /* interface descriptor: */
15500a2430fSAndrzej Pietrasiewicz 
15600a2430fSAndrzej Pietrasiewicz static struct usb_interface_descriptor ncm_control_intf = {
15700a2430fSAndrzej Pietrasiewicz 	.bLength =		sizeof ncm_control_intf,
15800a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_INTERFACE,
15900a2430fSAndrzej Pietrasiewicz 
16000a2430fSAndrzej Pietrasiewicz 	/* .bInterfaceNumber = DYNAMIC */
16100a2430fSAndrzej Pietrasiewicz 	.bNumEndpoints =	1,
16200a2430fSAndrzej Pietrasiewicz 	.bInterfaceClass =	USB_CLASS_COMM,
16300a2430fSAndrzej Pietrasiewicz 	.bInterfaceSubClass =	USB_CDC_SUBCLASS_NCM,
16400a2430fSAndrzej Pietrasiewicz 	.bInterfaceProtocol =	USB_CDC_PROTO_NONE,
16500a2430fSAndrzej Pietrasiewicz 	/* .iInterface = DYNAMIC */
16600a2430fSAndrzej Pietrasiewicz };
16700a2430fSAndrzej Pietrasiewicz 
16800a2430fSAndrzej Pietrasiewicz static struct usb_cdc_header_desc ncm_header_desc = {
16900a2430fSAndrzej Pietrasiewicz 	.bLength =		sizeof ncm_header_desc,
17000a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_CS_INTERFACE,
17100a2430fSAndrzej Pietrasiewicz 	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
17200a2430fSAndrzej Pietrasiewicz 
17300a2430fSAndrzej Pietrasiewicz 	.bcdCDC =		cpu_to_le16(0x0110),
17400a2430fSAndrzej Pietrasiewicz };
17500a2430fSAndrzej Pietrasiewicz 
17600a2430fSAndrzej Pietrasiewicz static struct usb_cdc_union_desc ncm_union_desc = {
17700a2430fSAndrzej Pietrasiewicz 	.bLength =		sizeof(ncm_union_desc),
17800a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_CS_INTERFACE,
17900a2430fSAndrzej Pietrasiewicz 	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
18000a2430fSAndrzej Pietrasiewicz 	/* .bMasterInterface0 =	DYNAMIC */
18100a2430fSAndrzej Pietrasiewicz 	/* .bSlaveInterface0 =	DYNAMIC */
18200a2430fSAndrzej Pietrasiewicz };
18300a2430fSAndrzej Pietrasiewicz 
18400a2430fSAndrzej Pietrasiewicz static struct usb_cdc_ether_desc ecm_desc = {
18500a2430fSAndrzej Pietrasiewicz 	.bLength =		sizeof ecm_desc,
18600a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_CS_INTERFACE,
18700a2430fSAndrzej Pietrasiewicz 	.bDescriptorSubType =	USB_CDC_ETHERNET_TYPE,
18800a2430fSAndrzej Pietrasiewicz 
18900a2430fSAndrzej Pietrasiewicz 	/* this descriptor actually adds value, surprise! */
19000a2430fSAndrzej Pietrasiewicz 	/* .iMACAddress = DYNAMIC */
19100a2430fSAndrzej Pietrasiewicz 	.bmEthernetStatistics =	cpu_to_le32(0), /* no statistics */
19200a2430fSAndrzej Pietrasiewicz 	.wNumberMCFilters =	cpu_to_le16(0),
19300a2430fSAndrzej Pietrasiewicz 	.bNumberPowerFilters =	0,
19400a2430fSAndrzej Pietrasiewicz };
19500a2430fSAndrzej Pietrasiewicz 
19600a2430fSAndrzej Pietrasiewicz #define NCAPS	(USB_CDC_NCM_NCAP_ETH_FILTER | USB_CDC_NCM_NCAP_CRC_MODE)
19700a2430fSAndrzej Pietrasiewicz 
19800a2430fSAndrzej Pietrasiewicz static struct usb_cdc_ncm_desc ncm_desc = {
19900a2430fSAndrzej Pietrasiewicz 	.bLength =		sizeof ncm_desc,
20000a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_CS_INTERFACE,
20100a2430fSAndrzej Pietrasiewicz 	.bDescriptorSubType =	USB_CDC_NCM_TYPE,
20200a2430fSAndrzej Pietrasiewicz 
20300a2430fSAndrzej Pietrasiewicz 	.bcdNcmVersion =	cpu_to_le16(0x0100),
20400a2430fSAndrzej Pietrasiewicz 	/* can process SetEthernetPacketFilter */
20500a2430fSAndrzej Pietrasiewicz 	.bmNetworkCapabilities = NCAPS,
20600a2430fSAndrzej Pietrasiewicz };
20700a2430fSAndrzej Pietrasiewicz 
20800a2430fSAndrzej Pietrasiewicz /* the default data interface has no endpoints ... */
20900a2430fSAndrzej Pietrasiewicz 
21000a2430fSAndrzej Pietrasiewicz static struct usb_interface_descriptor ncm_data_nop_intf = {
21100a2430fSAndrzej Pietrasiewicz 	.bLength =		sizeof ncm_data_nop_intf,
21200a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_INTERFACE,
21300a2430fSAndrzej Pietrasiewicz 
21400a2430fSAndrzej Pietrasiewicz 	.bInterfaceNumber =	1,
21500a2430fSAndrzej Pietrasiewicz 	.bAlternateSetting =	0,
21600a2430fSAndrzej Pietrasiewicz 	.bNumEndpoints =	0,
21700a2430fSAndrzej Pietrasiewicz 	.bInterfaceClass =	USB_CLASS_CDC_DATA,
21800a2430fSAndrzej Pietrasiewicz 	.bInterfaceSubClass =	0,
21900a2430fSAndrzej Pietrasiewicz 	.bInterfaceProtocol =	USB_CDC_NCM_PROTO_NTB,
22000a2430fSAndrzej Pietrasiewicz 	/* .iInterface = DYNAMIC */
22100a2430fSAndrzej Pietrasiewicz };
22200a2430fSAndrzej Pietrasiewicz 
22300a2430fSAndrzej Pietrasiewicz /* ... but the "real" data interface has two bulk endpoints */
22400a2430fSAndrzej Pietrasiewicz 
22500a2430fSAndrzej Pietrasiewicz static struct usb_interface_descriptor ncm_data_intf = {
22600a2430fSAndrzej Pietrasiewicz 	.bLength =		sizeof ncm_data_intf,
22700a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_INTERFACE,
22800a2430fSAndrzej Pietrasiewicz 
22900a2430fSAndrzej Pietrasiewicz 	.bInterfaceNumber =	1,
23000a2430fSAndrzej Pietrasiewicz 	.bAlternateSetting =	1,
23100a2430fSAndrzej Pietrasiewicz 	.bNumEndpoints =	2,
23200a2430fSAndrzej Pietrasiewicz 	.bInterfaceClass =	USB_CLASS_CDC_DATA,
23300a2430fSAndrzej Pietrasiewicz 	.bInterfaceSubClass =	0,
23400a2430fSAndrzej Pietrasiewicz 	.bInterfaceProtocol =	USB_CDC_NCM_PROTO_NTB,
23500a2430fSAndrzej Pietrasiewicz 	/* .iInterface = DYNAMIC */
23600a2430fSAndrzej Pietrasiewicz };
23700a2430fSAndrzej Pietrasiewicz 
23800a2430fSAndrzej Pietrasiewicz /* full speed support: */
23900a2430fSAndrzej Pietrasiewicz 
24000a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor fs_ncm_notify_desc = {
24100a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
24200a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
24300a2430fSAndrzej Pietrasiewicz 
24400a2430fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_IN,
24500a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_INT,
24600a2430fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(NCM_STATUS_BYTECOUNT),
24700a2430fSAndrzej Pietrasiewicz 	.bInterval =		NCM_STATUS_INTERVAL_MS,
24800a2430fSAndrzej Pietrasiewicz };
24900a2430fSAndrzej Pietrasiewicz 
25000a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor fs_ncm_in_desc = {
25100a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
25200a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
25300a2430fSAndrzej Pietrasiewicz 
25400a2430fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_IN,
25500a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
25600a2430fSAndrzej Pietrasiewicz };
25700a2430fSAndrzej Pietrasiewicz 
25800a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor fs_ncm_out_desc = {
25900a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
26000a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
26100a2430fSAndrzej Pietrasiewicz 
26200a2430fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_OUT,
26300a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
26400a2430fSAndrzej Pietrasiewicz };
26500a2430fSAndrzej Pietrasiewicz 
26600a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *ncm_fs_function[] = {
26700a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ncm_iad_desc,
26800a2430fSAndrzej Pietrasiewicz 	/* CDC NCM control descriptors */
26900a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ncm_control_intf,
27000a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ncm_header_desc,
27100a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ncm_union_desc,
27200a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ecm_desc,
27300a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ncm_desc,
27400a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &fs_ncm_notify_desc,
27500a2430fSAndrzej Pietrasiewicz 	/* data interface, altsettings 0 and 1 */
27600a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ncm_data_nop_intf,
27700a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ncm_data_intf,
27800a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &fs_ncm_in_desc,
27900a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &fs_ncm_out_desc,
28000a2430fSAndrzej Pietrasiewicz 	NULL,
28100a2430fSAndrzej Pietrasiewicz };
28200a2430fSAndrzej Pietrasiewicz 
28300a2430fSAndrzej Pietrasiewicz /* high speed support: */
28400a2430fSAndrzej Pietrasiewicz 
28500a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hs_ncm_notify_desc = {
28600a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
28700a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
28800a2430fSAndrzej Pietrasiewicz 
28900a2430fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_IN,
29000a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_INT,
29100a2430fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(NCM_STATUS_BYTECOUNT),
29200a2430fSAndrzej Pietrasiewicz 	.bInterval =		USB_MS_TO_HS_INTERVAL(NCM_STATUS_INTERVAL_MS),
29300a2430fSAndrzej Pietrasiewicz };
29400a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hs_ncm_in_desc = {
29500a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
29600a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
29700a2430fSAndrzej Pietrasiewicz 
29800a2430fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_IN,
29900a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
30000a2430fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(512),
30100a2430fSAndrzej Pietrasiewicz };
30200a2430fSAndrzej Pietrasiewicz 
30300a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hs_ncm_out_desc = {
30400a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
30500a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
30600a2430fSAndrzej Pietrasiewicz 
30700a2430fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_OUT,
30800a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
30900a2430fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(512),
31000a2430fSAndrzej Pietrasiewicz };
31100a2430fSAndrzej Pietrasiewicz 
31200a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *ncm_hs_function[] = {
31300a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ncm_iad_desc,
31400a2430fSAndrzej Pietrasiewicz 	/* CDC NCM control descriptors */
31500a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ncm_control_intf,
31600a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ncm_header_desc,
31700a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ncm_union_desc,
31800a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ecm_desc,
31900a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ncm_desc,
32000a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &hs_ncm_notify_desc,
32100a2430fSAndrzej Pietrasiewicz 	/* data interface, altsettings 0 and 1 */
32200a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ncm_data_nop_intf,
32300a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ncm_data_intf,
32400a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &hs_ncm_in_desc,
32500a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &hs_ncm_out_desc,
32600a2430fSAndrzej Pietrasiewicz 	NULL,
32700a2430fSAndrzej Pietrasiewicz };
32800a2430fSAndrzej Pietrasiewicz 
32916501138SJussi Kivilinna 
33016501138SJussi Kivilinna /* super speed support: */
33116501138SJussi Kivilinna 
33216501138SJussi Kivilinna static struct usb_endpoint_descriptor ss_ncm_notify_desc = {
33316501138SJussi Kivilinna 	.bLength =		USB_DT_ENDPOINT_SIZE,
33416501138SJussi Kivilinna 	.bDescriptorType =	USB_DT_ENDPOINT,
33516501138SJussi Kivilinna 
33616501138SJussi Kivilinna 	.bEndpointAddress =	USB_DIR_IN,
33716501138SJussi Kivilinna 	.bmAttributes =		USB_ENDPOINT_XFER_INT,
33816501138SJussi Kivilinna 	.wMaxPacketSize =	cpu_to_le16(NCM_STATUS_BYTECOUNT),
33916501138SJussi Kivilinna 	.bInterval =		USB_MS_TO_HS_INTERVAL(NCM_STATUS_INTERVAL_MS)
34016501138SJussi Kivilinna };
34116501138SJussi Kivilinna 
34216501138SJussi Kivilinna static struct usb_ss_ep_comp_descriptor ss_ncm_notify_comp_desc = {
34316501138SJussi Kivilinna 	.bLength =		sizeof(ss_ncm_notify_comp_desc),
34416501138SJussi Kivilinna 	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
34516501138SJussi Kivilinna 
34616501138SJussi Kivilinna 	/* the following 3 values can be tweaked if necessary */
34716501138SJussi Kivilinna 	/* .bMaxBurst =		0, */
34816501138SJussi Kivilinna 	/* .bmAttributes =	0, */
34916501138SJussi Kivilinna 	.wBytesPerInterval =	cpu_to_le16(NCM_STATUS_BYTECOUNT),
35016501138SJussi Kivilinna };
35116501138SJussi Kivilinna 
35216501138SJussi Kivilinna static struct usb_endpoint_descriptor ss_ncm_in_desc = {
35316501138SJussi Kivilinna 	.bLength =		USB_DT_ENDPOINT_SIZE,
35416501138SJussi Kivilinna 	.bDescriptorType =	USB_DT_ENDPOINT,
35516501138SJussi Kivilinna 
35616501138SJussi Kivilinna 	.bEndpointAddress =	USB_DIR_IN,
35716501138SJussi Kivilinna 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
35816501138SJussi Kivilinna 	.wMaxPacketSize =	cpu_to_le16(1024),
35916501138SJussi Kivilinna };
36016501138SJussi Kivilinna 
36116501138SJussi Kivilinna static struct usb_endpoint_descriptor ss_ncm_out_desc = {
36216501138SJussi Kivilinna 	.bLength =		USB_DT_ENDPOINT_SIZE,
36316501138SJussi Kivilinna 	.bDescriptorType =	USB_DT_ENDPOINT,
36416501138SJussi Kivilinna 
36516501138SJussi Kivilinna 	.bEndpointAddress =	USB_DIR_OUT,
36616501138SJussi Kivilinna 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
36716501138SJussi Kivilinna 	.wMaxPacketSize =	cpu_to_le16(1024),
36816501138SJussi Kivilinna };
36916501138SJussi Kivilinna 
37016501138SJussi Kivilinna static struct usb_ss_ep_comp_descriptor ss_ncm_bulk_comp_desc = {
37116501138SJussi Kivilinna 	.bLength =		sizeof(ss_ncm_bulk_comp_desc),
37216501138SJussi Kivilinna 	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
37316501138SJussi Kivilinna 
37416501138SJussi Kivilinna 	/* the following 2 values can be tweaked if necessary */
375a176b1a2SLorenzo Colitti 	.bMaxBurst =		15,
37616501138SJussi Kivilinna 	/* .bmAttributes =	0, */
37716501138SJussi Kivilinna };
37816501138SJussi Kivilinna 
37916501138SJussi Kivilinna static struct usb_descriptor_header *ncm_ss_function[] = {
38016501138SJussi Kivilinna 	(struct usb_descriptor_header *) &ncm_iad_desc,
38116501138SJussi Kivilinna 	/* CDC NCM control descriptors */
38216501138SJussi Kivilinna 	(struct usb_descriptor_header *) &ncm_control_intf,
38316501138SJussi Kivilinna 	(struct usb_descriptor_header *) &ncm_header_desc,
38416501138SJussi Kivilinna 	(struct usb_descriptor_header *) &ncm_union_desc,
38516501138SJussi Kivilinna 	(struct usb_descriptor_header *) &ecm_desc,
38616501138SJussi Kivilinna 	(struct usb_descriptor_header *) &ncm_desc,
38716501138SJussi Kivilinna 	(struct usb_descriptor_header *) &ss_ncm_notify_desc,
38816501138SJussi Kivilinna 	(struct usb_descriptor_header *) &ss_ncm_notify_comp_desc,
38916501138SJussi Kivilinna 	/* data interface, altsettings 0 and 1 */
39016501138SJussi Kivilinna 	(struct usb_descriptor_header *) &ncm_data_nop_intf,
39116501138SJussi Kivilinna 	(struct usb_descriptor_header *) &ncm_data_intf,
39216501138SJussi Kivilinna 	(struct usb_descriptor_header *) &ss_ncm_in_desc,
39316501138SJussi Kivilinna 	(struct usb_descriptor_header *) &ss_ncm_bulk_comp_desc,
39416501138SJussi Kivilinna 	(struct usb_descriptor_header *) &ss_ncm_out_desc,
39516501138SJussi Kivilinna 	(struct usb_descriptor_header *) &ss_ncm_bulk_comp_desc,
39616501138SJussi Kivilinna 	NULL,
39716501138SJussi Kivilinna };
39816501138SJussi Kivilinna 
39900a2430fSAndrzej Pietrasiewicz /* string descriptors: */
40000a2430fSAndrzej Pietrasiewicz 
40100a2430fSAndrzej Pietrasiewicz #define STRING_CTRL_IDX	0
40200a2430fSAndrzej Pietrasiewicz #define STRING_MAC_IDX	1
40300a2430fSAndrzej Pietrasiewicz #define STRING_DATA_IDX	2
40400a2430fSAndrzej Pietrasiewicz #define STRING_IAD_IDX	3
40500a2430fSAndrzej Pietrasiewicz 
40600a2430fSAndrzej Pietrasiewicz static struct usb_string ncm_string_defs[] = {
40700a2430fSAndrzej Pietrasiewicz 	[STRING_CTRL_IDX].s = "CDC Network Control Model (NCM)",
40800a2430fSAndrzej Pietrasiewicz 	[STRING_MAC_IDX].s = "",
40900a2430fSAndrzej Pietrasiewicz 	[STRING_DATA_IDX].s = "CDC Network Data",
41000a2430fSAndrzej Pietrasiewicz 	[STRING_IAD_IDX].s = "CDC NCM",
41100a2430fSAndrzej Pietrasiewicz 	{  } /* end of list */
41200a2430fSAndrzej Pietrasiewicz };
41300a2430fSAndrzej Pietrasiewicz 
41400a2430fSAndrzej Pietrasiewicz static struct usb_gadget_strings ncm_string_table = {
41500a2430fSAndrzej Pietrasiewicz 	.language =		0x0409,	/* en-us */
41600a2430fSAndrzej Pietrasiewicz 	.strings =		ncm_string_defs,
41700a2430fSAndrzej Pietrasiewicz };
41800a2430fSAndrzej Pietrasiewicz 
41900a2430fSAndrzej Pietrasiewicz static struct usb_gadget_strings *ncm_strings[] = {
42000a2430fSAndrzej Pietrasiewicz 	&ncm_string_table,
42100a2430fSAndrzej Pietrasiewicz 	NULL,
42200a2430fSAndrzej Pietrasiewicz };
42300a2430fSAndrzej Pietrasiewicz 
42400a2430fSAndrzej Pietrasiewicz /*
42500a2430fSAndrzej Pietrasiewicz  * Here are options for NCM Datagram Pointer table (NDP) parser.
42600a2430fSAndrzej Pietrasiewicz  * There are 2 different formats: NDP16 and NDP32 in the spec (ch. 3),
42700a2430fSAndrzej Pietrasiewicz  * in NDP16 offsets and sizes fields are 1 16bit word wide,
42800a2430fSAndrzej Pietrasiewicz  * in NDP32 -- 2 16bit words wide. Also signatures are different.
42900a2430fSAndrzej Pietrasiewicz  * To make the parser code the same, put the differences in the structure,
43000a2430fSAndrzej Pietrasiewicz  * and switch pointers to the structures when the format is changed.
43100a2430fSAndrzej Pietrasiewicz  */
43200a2430fSAndrzej Pietrasiewicz 
43300a2430fSAndrzej Pietrasiewicz struct ndp_parser_opts {
43400a2430fSAndrzej Pietrasiewicz 	u32		nth_sign;
43500a2430fSAndrzej Pietrasiewicz 	u32		ndp_sign;
43600a2430fSAndrzej Pietrasiewicz 	unsigned	nth_size;
43700a2430fSAndrzej Pietrasiewicz 	unsigned	ndp_size;
43800a2430fSAndrzej Pietrasiewicz 	unsigned	dpe_size;
43900a2430fSAndrzej Pietrasiewicz 	unsigned	ndplen_align;
44000a2430fSAndrzej Pietrasiewicz 	/* sizes in u16 units */
44100a2430fSAndrzej Pietrasiewicz 	unsigned	dgram_item_len; /* index or length */
44200a2430fSAndrzej Pietrasiewicz 	unsigned	block_length;
44300a2430fSAndrzej Pietrasiewicz 	unsigned	ndp_index;
44400a2430fSAndrzej Pietrasiewicz 	unsigned	reserved1;
44500a2430fSAndrzej Pietrasiewicz 	unsigned	reserved2;
44600a2430fSAndrzej Pietrasiewicz 	unsigned	next_ndp_index;
44700a2430fSAndrzej Pietrasiewicz };
44800a2430fSAndrzej Pietrasiewicz 
449f1e8a41cSMaciej Żenczykowski static const struct ndp_parser_opts ndp16_opts = {
450f1e8a41cSMaciej Żenczykowski 	.nth_sign = USB_CDC_NCM_NTH16_SIGN,
451f1e8a41cSMaciej Żenczykowski 	.ndp_sign = USB_CDC_NCM_NDP16_NOCRC_SIGN,
452f1e8a41cSMaciej Żenczykowski 	.nth_size = sizeof(struct usb_cdc_ncm_nth16),
453f1e8a41cSMaciej Żenczykowski 	.ndp_size = sizeof(struct usb_cdc_ncm_ndp16),
454f1e8a41cSMaciej Żenczykowski 	.dpe_size = sizeof(struct usb_cdc_ncm_dpe16),
455f1e8a41cSMaciej Żenczykowski 	.ndplen_align = 4,
456f1e8a41cSMaciej Żenczykowski 	.dgram_item_len = 1,
457f1e8a41cSMaciej Żenczykowski 	.block_length = 1,
458f1e8a41cSMaciej Żenczykowski 	.ndp_index = 1,
459f1e8a41cSMaciej Żenczykowski 	.reserved1 = 0,
460f1e8a41cSMaciej Żenczykowski 	.reserved2 = 0,
461f1e8a41cSMaciej Żenczykowski 	.next_ndp_index = 1,
462f1e8a41cSMaciej Żenczykowski };
46300a2430fSAndrzej Pietrasiewicz 
464f1e8a41cSMaciej Żenczykowski static const struct ndp_parser_opts ndp32_opts = {
465f1e8a41cSMaciej Żenczykowski 	.nth_sign = USB_CDC_NCM_NTH32_SIGN,
466f1e8a41cSMaciej Żenczykowski 	.ndp_sign = USB_CDC_NCM_NDP32_NOCRC_SIGN,
467f1e8a41cSMaciej Żenczykowski 	.nth_size = sizeof(struct usb_cdc_ncm_nth32),
468f1e8a41cSMaciej Żenczykowski 	.ndp_size = sizeof(struct usb_cdc_ncm_ndp32),
469f1e8a41cSMaciej Żenczykowski 	.dpe_size = sizeof(struct usb_cdc_ncm_dpe32),
470f1e8a41cSMaciej Żenczykowski 	.ndplen_align = 8,
471f1e8a41cSMaciej Żenczykowski 	.dgram_item_len = 2,
472f1e8a41cSMaciej Żenczykowski 	.block_length = 2,
473f1e8a41cSMaciej Żenczykowski 	.ndp_index = 2,
474f1e8a41cSMaciej Żenczykowski 	.reserved1 = 1,
475f1e8a41cSMaciej Żenczykowski 	.reserved2 = 2,
476f1e8a41cSMaciej Żenczykowski 	.next_ndp_index = 2,
477f1e8a41cSMaciej Żenczykowski };
47800a2430fSAndrzej Pietrasiewicz 
put_ncm(__le16 ** p,unsigned size,unsigned val)47900a2430fSAndrzej Pietrasiewicz static inline void put_ncm(__le16 **p, unsigned size, unsigned val)
48000a2430fSAndrzej Pietrasiewicz {
48100a2430fSAndrzej Pietrasiewicz 	switch (size) {
48200a2430fSAndrzej Pietrasiewicz 	case 1:
48300a2430fSAndrzej Pietrasiewicz 		put_unaligned_le16((u16)val, *p);
48400a2430fSAndrzej Pietrasiewicz 		break;
48500a2430fSAndrzej Pietrasiewicz 	case 2:
48600a2430fSAndrzej Pietrasiewicz 		put_unaligned_le32((u32)val, *p);
48700a2430fSAndrzej Pietrasiewicz 
48800a2430fSAndrzej Pietrasiewicz 		break;
48900a2430fSAndrzej Pietrasiewicz 	default:
49000a2430fSAndrzej Pietrasiewicz 		BUG();
49100a2430fSAndrzej Pietrasiewicz 	}
49200a2430fSAndrzej Pietrasiewicz 
49300a2430fSAndrzej Pietrasiewicz 	*p += size;
49400a2430fSAndrzej Pietrasiewicz }
49500a2430fSAndrzej Pietrasiewicz 
get_ncm(__le16 ** p,unsigned size)49600a2430fSAndrzej Pietrasiewicz static inline unsigned get_ncm(__le16 **p, unsigned size)
49700a2430fSAndrzej Pietrasiewicz {
49800a2430fSAndrzej Pietrasiewicz 	unsigned tmp;
49900a2430fSAndrzej Pietrasiewicz 
50000a2430fSAndrzej Pietrasiewicz 	switch (size) {
50100a2430fSAndrzej Pietrasiewicz 	case 1:
50200a2430fSAndrzej Pietrasiewicz 		tmp = get_unaligned_le16(*p);
50300a2430fSAndrzej Pietrasiewicz 		break;
50400a2430fSAndrzej Pietrasiewicz 	case 2:
50500a2430fSAndrzej Pietrasiewicz 		tmp = get_unaligned_le32(*p);
50600a2430fSAndrzej Pietrasiewicz 		break;
50700a2430fSAndrzej Pietrasiewicz 	default:
50800a2430fSAndrzej Pietrasiewicz 		BUG();
50900a2430fSAndrzej Pietrasiewicz 	}
51000a2430fSAndrzej Pietrasiewicz 
51100a2430fSAndrzej Pietrasiewicz 	*p += size;
51200a2430fSAndrzej Pietrasiewicz 	return tmp;
51300a2430fSAndrzej Pietrasiewicz }
51400a2430fSAndrzej Pietrasiewicz 
51500a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
51600a2430fSAndrzej Pietrasiewicz 
ncm_reset_values(struct f_ncm * ncm)51700a2430fSAndrzej Pietrasiewicz static inline void ncm_reset_values(struct f_ncm *ncm)
51800a2430fSAndrzej Pietrasiewicz {
51900a2430fSAndrzej Pietrasiewicz 	ncm->parser_opts = &ndp16_opts;
52000a2430fSAndrzej Pietrasiewicz 	ncm->is_crc = false;
521550eef0cSRomain Izard 	ncm->ndp_sign = ncm->parser_opts->ndp_sign;
52200a2430fSAndrzej Pietrasiewicz 	ncm->port.cdc_filter = DEFAULT_FILTER;
52300a2430fSAndrzej Pietrasiewicz 
52400a2430fSAndrzej Pietrasiewicz 	/* doesn't make sense for ncm, fixed size used */
52500a2430fSAndrzej Pietrasiewicz 	ncm->port.header_len = 0;
52600a2430fSAndrzej Pietrasiewicz 
52700a2430fSAndrzej Pietrasiewicz 	ncm->port.fixed_out_len = le32_to_cpu(ntb_parameters.dwNtbOutMaxSize);
52800a2430fSAndrzej Pietrasiewicz 	ncm->port.fixed_in_len = NTB_DEFAULT_IN_SIZE;
52900a2430fSAndrzej Pietrasiewicz }
53000a2430fSAndrzej Pietrasiewicz 
53100a2430fSAndrzej Pietrasiewicz /*
53200a2430fSAndrzej Pietrasiewicz  * Context: ncm->lock held
53300a2430fSAndrzej Pietrasiewicz  */
ncm_do_notify(struct f_ncm * ncm)53400a2430fSAndrzej Pietrasiewicz static void ncm_do_notify(struct f_ncm *ncm)
53500a2430fSAndrzej Pietrasiewicz {
53600a2430fSAndrzej Pietrasiewicz 	struct usb_request		*req = ncm->notify_req;
53700a2430fSAndrzej Pietrasiewicz 	struct usb_cdc_notification	*event;
53800a2430fSAndrzej Pietrasiewicz 	struct usb_composite_dev	*cdev = ncm->port.func.config->cdev;
53900a2430fSAndrzej Pietrasiewicz 	__le32				*data;
54000a2430fSAndrzej Pietrasiewicz 	int				status;
54100a2430fSAndrzej Pietrasiewicz 
54200a2430fSAndrzej Pietrasiewicz 	/* notification already in flight? */
5435b24c28cSBryan O'Donoghue 	if (atomic_read(&ncm->notify_count))
54400a2430fSAndrzej Pietrasiewicz 		return;
54500a2430fSAndrzej Pietrasiewicz 
54600a2430fSAndrzej Pietrasiewicz 	event = req->buf;
54700a2430fSAndrzej Pietrasiewicz 	switch (ncm->notify_state) {
54800a2430fSAndrzej Pietrasiewicz 	case NCM_NOTIFY_NONE:
54900a2430fSAndrzej Pietrasiewicz 		return;
55000a2430fSAndrzej Pietrasiewicz 
55100a2430fSAndrzej Pietrasiewicz 	case NCM_NOTIFY_CONNECT:
55200a2430fSAndrzej Pietrasiewicz 		event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
55300a2430fSAndrzej Pietrasiewicz 		if (ncm->is_open)
55400a2430fSAndrzej Pietrasiewicz 			event->wValue = cpu_to_le16(1);
55500a2430fSAndrzej Pietrasiewicz 		else
55600a2430fSAndrzej Pietrasiewicz 			event->wValue = cpu_to_le16(0);
55700a2430fSAndrzej Pietrasiewicz 		event->wLength = 0;
55800a2430fSAndrzej Pietrasiewicz 		req->length = sizeof *event;
55900a2430fSAndrzej Pietrasiewicz 
56000a2430fSAndrzej Pietrasiewicz 		DBG(cdev, "notify connect %s\n",
56100a2430fSAndrzej Pietrasiewicz 				ncm->is_open ? "true" : "false");
56200a2430fSAndrzej Pietrasiewicz 		ncm->notify_state = NCM_NOTIFY_NONE;
56300a2430fSAndrzej Pietrasiewicz 		break;
56400a2430fSAndrzej Pietrasiewicz 
56500a2430fSAndrzej Pietrasiewicz 	case NCM_NOTIFY_SPEED:
56600a2430fSAndrzej Pietrasiewicz 		event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE;
56700a2430fSAndrzej Pietrasiewicz 		event->wValue = cpu_to_le16(0);
56800a2430fSAndrzej Pietrasiewicz 		event->wLength = cpu_to_le16(8);
56900a2430fSAndrzej Pietrasiewicz 		req->length = NCM_STATUS_BYTECOUNT;
57000a2430fSAndrzej Pietrasiewicz 
57100a2430fSAndrzej Pietrasiewicz 		/* SPEED_CHANGE data is up/down speeds in bits/sec */
57200a2430fSAndrzej Pietrasiewicz 		data = req->buf + sizeof *event;
5738165763fSLinyu Yuan 		data[0] = cpu_to_le32(gether_bitrate(cdev->gadget));
57400a2430fSAndrzej Pietrasiewicz 		data[1] = data[0];
57500a2430fSAndrzej Pietrasiewicz 
5768165763fSLinyu Yuan 		DBG(cdev, "notify speed %u\n", gether_bitrate(cdev->gadget));
57700a2430fSAndrzej Pietrasiewicz 		ncm->notify_state = NCM_NOTIFY_CONNECT;
57800a2430fSAndrzej Pietrasiewicz 		break;
57900a2430fSAndrzej Pietrasiewicz 	}
58000a2430fSAndrzej Pietrasiewicz 	event->bmRequestType = 0xA1;
58100a2430fSAndrzej Pietrasiewicz 	event->wIndex = cpu_to_le16(ncm->ctrl_id);
58200a2430fSAndrzej Pietrasiewicz 
5835b24c28cSBryan O'Donoghue 	atomic_inc(&ncm->notify_count);
5845b24c28cSBryan O'Donoghue 
58500a2430fSAndrzej Pietrasiewicz 	/*
58600a2430fSAndrzej Pietrasiewicz 	 * In double buffering if there is a space in FIFO,
58700a2430fSAndrzej Pietrasiewicz 	 * completion callback can be called right after the call,
58800a2430fSAndrzej Pietrasiewicz 	 * so unlocking
58900a2430fSAndrzej Pietrasiewicz 	 */
59000a2430fSAndrzej Pietrasiewicz 	spin_unlock(&ncm->lock);
59100a2430fSAndrzej Pietrasiewicz 	status = usb_ep_queue(ncm->notify, req, GFP_ATOMIC);
59200a2430fSAndrzej Pietrasiewicz 	spin_lock(&ncm->lock);
59300a2430fSAndrzej Pietrasiewicz 	if (status < 0) {
5945b24c28cSBryan O'Donoghue 		atomic_dec(&ncm->notify_count);
59500a2430fSAndrzej Pietrasiewicz 		DBG(cdev, "notify --> %d\n", status);
59600a2430fSAndrzej Pietrasiewicz 	}
59700a2430fSAndrzej Pietrasiewicz }
59800a2430fSAndrzej Pietrasiewicz 
59900a2430fSAndrzej Pietrasiewicz /*
60000a2430fSAndrzej Pietrasiewicz  * Context: ncm->lock held
60100a2430fSAndrzej Pietrasiewicz  */
ncm_notify(struct f_ncm * ncm)60200a2430fSAndrzej Pietrasiewicz static void ncm_notify(struct f_ncm *ncm)
60300a2430fSAndrzej Pietrasiewicz {
60400a2430fSAndrzej Pietrasiewicz 	/*
60500a2430fSAndrzej Pietrasiewicz 	 * NOTE on most versions of Linux, host side cdc-ethernet
60600a2430fSAndrzej Pietrasiewicz 	 * won't listen for notifications until its netdevice opens.
60700a2430fSAndrzej Pietrasiewicz 	 * The first notification then sits in the FIFO for a long
60800a2430fSAndrzej Pietrasiewicz 	 * time, and the second one is queued.
60900a2430fSAndrzej Pietrasiewicz 	 *
61000a2430fSAndrzej Pietrasiewicz 	 * If ncm_notify() is called before the second (CONNECT)
61100a2430fSAndrzej Pietrasiewicz 	 * notification is sent, then it will reset to send the SPEED
61200a2430fSAndrzej Pietrasiewicz 	 * notificaion again (and again, and again), but it's not a problem
61300a2430fSAndrzej Pietrasiewicz 	 */
61400a2430fSAndrzej Pietrasiewicz 	ncm->notify_state = NCM_NOTIFY_SPEED;
61500a2430fSAndrzej Pietrasiewicz 	ncm_do_notify(ncm);
61600a2430fSAndrzej Pietrasiewicz }
61700a2430fSAndrzej Pietrasiewicz 
ncm_notify_complete(struct usb_ep * ep,struct usb_request * req)61800a2430fSAndrzej Pietrasiewicz static void ncm_notify_complete(struct usb_ep *ep, struct usb_request *req)
61900a2430fSAndrzej Pietrasiewicz {
62000a2430fSAndrzej Pietrasiewicz 	struct f_ncm			*ncm = req->context;
62100a2430fSAndrzej Pietrasiewicz 	struct usb_composite_dev	*cdev = ncm->port.func.config->cdev;
62200a2430fSAndrzej Pietrasiewicz 	struct usb_cdc_notification	*event = req->buf;
62300a2430fSAndrzej Pietrasiewicz 
62400a2430fSAndrzej Pietrasiewicz 	spin_lock(&ncm->lock);
62500a2430fSAndrzej Pietrasiewicz 	switch (req->status) {
62600a2430fSAndrzej Pietrasiewicz 	case 0:
62700a2430fSAndrzej Pietrasiewicz 		VDBG(cdev, "Notification %02x sent\n",
62800a2430fSAndrzej Pietrasiewicz 		     event->bNotificationType);
6295b24c28cSBryan O'Donoghue 		atomic_dec(&ncm->notify_count);
63000a2430fSAndrzej Pietrasiewicz 		break;
63100a2430fSAndrzej Pietrasiewicz 	case -ECONNRESET:
63200a2430fSAndrzej Pietrasiewicz 	case -ESHUTDOWN:
6335b24c28cSBryan O'Donoghue 		atomic_set(&ncm->notify_count, 0);
63400a2430fSAndrzej Pietrasiewicz 		ncm->notify_state = NCM_NOTIFY_NONE;
63500a2430fSAndrzej Pietrasiewicz 		break;
63600a2430fSAndrzej Pietrasiewicz 	default:
63700a2430fSAndrzej Pietrasiewicz 		DBG(cdev, "event %02x --> %d\n",
63800a2430fSAndrzej Pietrasiewicz 			event->bNotificationType, req->status);
6395b24c28cSBryan O'Donoghue 		atomic_dec(&ncm->notify_count);
64000a2430fSAndrzej Pietrasiewicz 		break;
64100a2430fSAndrzej Pietrasiewicz 	}
64200a2430fSAndrzej Pietrasiewicz 	ncm_do_notify(ncm);
64300a2430fSAndrzej Pietrasiewicz 	spin_unlock(&ncm->lock);
64400a2430fSAndrzej Pietrasiewicz }
64500a2430fSAndrzej Pietrasiewicz 
ncm_ep0out_complete(struct usb_ep * ep,struct usb_request * req)64600a2430fSAndrzej Pietrasiewicz static void ncm_ep0out_complete(struct usb_ep *ep, struct usb_request *req)
64700a2430fSAndrzej Pietrasiewicz {
64800a2430fSAndrzej Pietrasiewicz 	/* now for SET_NTB_INPUT_SIZE only */
64900a2430fSAndrzej Pietrasiewicz 	unsigned		in_size;
65000a2430fSAndrzej Pietrasiewicz 	struct usb_function	*f = req->context;
65100a2430fSAndrzej Pietrasiewicz 	struct f_ncm		*ncm = func_to_ncm(f);
65235bfde36SRobert Baldyga 	struct usb_composite_dev *cdev = f->config->cdev;
65300a2430fSAndrzej Pietrasiewicz 
65400a2430fSAndrzej Pietrasiewicz 	req->context = NULL;
65500a2430fSAndrzej Pietrasiewicz 	if (req->status || req->actual != req->length) {
65600a2430fSAndrzej Pietrasiewicz 		DBG(cdev, "Bad control-OUT transfer\n");
65700a2430fSAndrzej Pietrasiewicz 		goto invalid;
65800a2430fSAndrzej Pietrasiewicz 	}
65900a2430fSAndrzej Pietrasiewicz 
66000a2430fSAndrzej Pietrasiewicz 	in_size = get_unaligned_le32(req->buf);
66100a2430fSAndrzej Pietrasiewicz 	if (in_size < USB_CDC_NCM_NTB_MIN_IN_SIZE ||
66200a2430fSAndrzej Pietrasiewicz 	    in_size > le32_to_cpu(ntb_parameters.dwNtbInMaxSize)) {
66300a2430fSAndrzej Pietrasiewicz 		DBG(cdev, "Got wrong INPUT SIZE (%d) from host\n", in_size);
66400a2430fSAndrzej Pietrasiewicz 		goto invalid;
66500a2430fSAndrzej Pietrasiewicz 	}
66600a2430fSAndrzej Pietrasiewicz 
66700a2430fSAndrzej Pietrasiewicz 	ncm->port.fixed_in_len = in_size;
66800a2430fSAndrzej Pietrasiewicz 	VDBG(cdev, "Set NTB INPUT SIZE %d\n", in_size);
66900a2430fSAndrzej Pietrasiewicz 	return;
67000a2430fSAndrzej Pietrasiewicz 
67100a2430fSAndrzej Pietrasiewicz invalid:
67200a2430fSAndrzej Pietrasiewicz 	usb_ep_set_halt(ep);
67300a2430fSAndrzej Pietrasiewicz 	return;
67400a2430fSAndrzej Pietrasiewicz }
67500a2430fSAndrzej Pietrasiewicz 
ncm_setup(struct usb_function * f,const struct usb_ctrlrequest * ctrl)67600a2430fSAndrzej Pietrasiewicz static int ncm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
67700a2430fSAndrzej Pietrasiewicz {
67800a2430fSAndrzej Pietrasiewicz 	struct f_ncm		*ncm = func_to_ncm(f);
67900a2430fSAndrzej Pietrasiewicz 	struct usb_composite_dev *cdev = f->config->cdev;
68000a2430fSAndrzej Pietrasiewicz 	struct usb_request	*req = cdev->req;
68100a2430fSAndrzej Pietrasiewicz 	int			value = -EOPNOTSUPP;
68200a2430fSAndrzej Pietrasiewicz 	u16			w_index = le16_to_cpu(ctrl->wIndex);
68300a2430fSAndrzej Pietrasiewicz 	u16			w_value = le16_to_cpu(ctrl->wValue);
68400a2430fSAndrzej Pietrasiewicz 	u16			w_length = le16_to_cpu(ctrl->wLength);
68500a2430fSAndrzej Pietrasiewicz 
68600a2430fSAndrzej Pietrasiewicz 	/*
68700a2430fSAndrzej Pietrasiewicz 	 * composite driver infrastructure handles everything except
68800a2430fSAndrzej Pietrasiewicz 	 * CDC class messages; interface activation uses set_alt().
68900a2430fSAndrzej Pietrasiewicz 	 */
69000a2430fSAndrzej Pietrasiewicz 	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
69100a2430fSAndrzej Pietrasiewicz 	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
69200a2430fSAndrzej Pietrasiewicz 			| USB_CDC_SET_ETHERNET_PACKET_FILTER:
69300a2430fSAndrzej Pietrasiewicz 		/*
69400a2430fSAndrzej Pietrasiewicz 		 * see 6.2.30: no data, wIndex = interface,
69500a2430fSAndrzej Pietrasiewicz 		 * wValue = packet filter bitmap
69600a2430fSAndrzej Pietrasiewicz 		 */
69700a2430fSAndrzej Pietrasiewicz 		if (w_length != 0 || w_index != ncm->ctrl_id)
69800a2430fSAndrzej Pietrasiewicz 			goto invalid;
69900a2430fSAndrzej Pietrasiewicz 		DBG(cdev, "packet filter %02x\n", w_value);
70000a2430fSAndrzej Pietrasiewicz 		/*
70100a2430fSAndrzej Pietrasiewicz 		 * REVISIT locking of cdc_filter.  This assumes the UDC
70200a2430fSAndrzej Pietrasiewicz 		 * driver won't have a concurrent packet TX irq running on
70300a2430fSAndrzej Pietrasiewicz 		 * another CPU; or that if it does, this write is atomic...
70400a2430fSAndrzej Pietrasiewicz 		 */
70500a2430fSAndrzej Pietrasiewicz 		ncm->port.cdc_filter = w_value;
70600a2430fSAndrzej Pietrasiewicz 		value = 0;
70700a2430fSAndrzej Pietrasiewicz 		break;
70800a2430fSAndrzej Pietrasiewicz 	/*
70900a2430fSAndrzej Pietrasiewicz 	 * and optionally:
71000a2430fSAndrzej Pietrasiewicz 	 * case USB_CDC_SEND_ENCAPSULATED_COMMAND:
71100a2430fSAndrzej Pietrasiewicz 	 * case USB_CDC_GET_ENCAPSULATED_RESPONSE:
71200a2430fSAndrzej Pietrasiewicz 	 * case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS:
71300a2430fSAndrzej Pietrasiewicz 	 * case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER:
71400a2430fSAndrzej Pietrasiewicz 	 * case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER:
71500a2430fSAndrzej Pietrasiewicz 	 * case USB_CDC_GET_ETHERNET_STATISTIC:
71600a2430fSAndrzej Pietrasiewicz 	 */
71700a2430fSAndrzej Pietrasiewicz 
71800a2430fSAndrzej Pietrasiewicz 	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
71900a2430fSAndrzej Pietrasiewicz 		| USB_CDC_GET_NTB_PARAMETERS:
72000a2430fSAndrzej Pietrasiewicz 
72100a2430fSAndrzej Pietrasiewicz 		if (w_length == 0 || w_value != 0 || w_index != ncm->ctrl_id)
72200a2430fSAndrzej Pietrasiewicz 			goto invalid;
72300a2430fSAndrzej Pietrasiewicz 		value = w_length > sizeof ntb_parameters ?
72400a2430fSAndrzej Pietrasiewicz 			sizeof ntb_parameters : w_length;
72500a2430fSAndrzej Pietrasiewicz 		memcpy(req->buf, &ntb_parameters, value);
72600a2430fSAndrzej Pietrasiewicz 		VDBG(cdev, "Host asked NTB parameters\n");
72700a2430fSAndrzej Pietrasiewicz 		break;
72800a2430fSAndrzej Pietrasiewicz 
72900a2430fSAndrzej Pietrasiewicz 	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
73000a2430fSAndrzej Pietrasiewicz 		| USB_CDC_GET_NTB_INPUT_SIZE:
73100a2430fSAndrzej Pietrasiewicz 
73200a2430fSAndrzej Pietrasiewicz 		if (w_length < 4 || w_value != 0 || w_index != ncm->ctrl_id)
73300a2430fSAndrzej Pietrasiewicz 			goto invalid;
73400a2430fSAndrzej Pietrasiewicz 		put_unaligned_le32(ncm->port.fixed_in_len, req->buf);
73500a2430fSAndrzej Pietrasiewicz 		value = 4;
73600a2430fSAndrzej Pietrasiewicz 		VDBG(cdev, "Host asked INPUT SIZE, sending %d\n",
73700a2430fSAndrzej Pietrasiewicz 		     ncm->port.fixed_in_len);
73800a2430fSAndrzej Pietrasiewicz 		break;
73900a2430fSAndrzej Pietrasiewicz 
74000a2430fSAndrzej Pietrasiewicz 	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
74100a2430fSAndrzej Pietrasiewicz 		| USB_CDC_SET_NTB_INPUT_SIZE:
74200a2430fSAndrzej Pietrasiewicz 	{
74300a2430fSAndrzej Pietrasiewicz 		if (w_length != 4 || w_value != 0 || w_index != ncm->ctrl_id)
74400a2430fSAndrzej Pietrasiewicz 			goto invalid;
74500a2430fSAndrzej Pietrasiewicz 		req->complete = ncm_ep0out_complete;
74600a2430fSAndrzej Pietrasiewicz 		req->length = w_length;
74700a2430fSAndrzej Pietrasiewicz 		req->context = f;
74800a2430fSAndrzej Pietrasiewicz 
74900a2430fSAndrzej Pietrasiewicz 		value = req->length;
75000a2430fSAndrzej Pietrasiewicz 		break;
75100a2430fSAndrzej Pietrasiewicz 	}
75200a2430fSAndrzej Pietrasiewicz 
75300a2430fSAndrzej Pietrasiewicz 	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
75400a2430fSAndrzej Pietrasiewicz 		| USB_CDC_GET_NTB_FORMAT:
75500a2430fSAndrzej Pietrasiewicz 	{
75600a2430fSAndrzej Pietrasiewicz 		uint16_t format;
75700a2430fSAndrzej Pietrasiewicz 
75800a2430fSAndrzej Pietrasiewicz 		if (w_length < 2 || w_value != 0 || w_index != ncm->ctrl_id)
75900a2430fSAndrzej Pietrasiewicz 			goto invalid;
76000a2430fSAndrzej Pietrasiewicz 		format = (ncm->parser_opts == &ndp16_opts) ? 0x0000 : 0x0001;
76100a2430fSAndrzej Pietrasiewicz 		put_unaligned_le16(format, req->buf);
76200a2430fSAndrzej Pietrasiewicz 		value = 2;
76300a2430fSAndrzej Pietrasiewicz 		VDBG(cdev, "Host asked NTB FORMAT, sending %d\n", format);
76400a2430fSAndrzej Pietrasiewicz 		break;
76500a2430fSAndrzej Pietrasiewicz 	}
76600a2430fSAndrzej Pietrasiewicz 
76700a2430fSAndrzej Pietrasiewicz 	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
76800a2430fSAndrzej Pietrasiewicz 		| USB_CDC_SET_NTB_FORMAT:
76900a2430fSAndrzej Pietrasiewicz 	{
77000a2430fSAndrzej Pietrasiewicz 		if (w_length != 0 || w_index != ncm->ctrl_id)
77100a2430fSAndrzej Pietrasiewicz 			goto invalid;
77200a2430fSAndrzej Pietrasiewicz 		switch (w_value) {
77300a2430fSAndrzej Pietrasiewicz 		case 0x0000:
77400a2430fSAndrzej Pietrasiewicz 			ncm->parser_opts = &ndp16_opts;
77500a2430fSAndrzej Pietrasiewicz 			DBG(cdev, "NCM16 selected\n");
77600a2430fSAndrzej Pietrasiewicz 			break;
77700a2430fSAndrzej Pietrasiewicz 		case 0x0001:
77800a2430fSAndrzej Pietrasiewicz 			ncm->parser_opts = &ndp32_opts;
77900a2430fSAndrzej Pietrasiewicz 			DBG(cdev, "NCM32 selected\n");
78000a2430fSAndrzej Pietrasiewicz 			break;
78100a2430fSAndrzej Pietrasiewicz 		default:
78200a2430fSAndrzej Pietrasiewicz 			goto invalid;
78300a2430fSAndrzej Pietrasiewicz 		}
78400a2430fSAndrzej Pietrasiewicz 		value = 0;
78500a2430fSAndrzej Pietrasiewicz 		break;
78600a2430fSAndrzej Pietrasiewicz 	}
78700a2430fSAndrzej Pietrasiewicz 	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
78800a2430fSAndrzej Pietrasiewicz 		| USB_CDC_GET_CRC_MODE:
78900a2430fSAndrzej Pietrasiewicz 	{
79000a2430fSAndrzej Pietrasiewicz 		uint16_t is_crc;
79100a2430fSAndrzej Pietrasiewicz 
79200a2430fSAndrzej Pietrasiewicz 		if (w_length < 2 || w_value != 0 || w_index != ncm->ctrl_id)
79300a2430fSAndrzej Pietrasiewicz 			goto invalid;
79400a2430fSAndrzej Pietrasiewicz 		is_crc = ncm->is_crc ? 0x0001 : 0x0000;
79500a2430fSAndrzej Pietrasiewicz 		put_unaligned_le16(is_crc, req->buf);
79600a2430fSAndrzej Pietrasiewicz 		value = 2;
79700a2430fSAndrzej Pietrasiewicz 		VDBG(cdev, "Host asked CRC MODE, sending %d\n", is_crc);
79800a2430fSAndrzej Pietrasiewicz 		break;
79900a2430fSAndrzej Pietrasiewicz 	}
80000a2430fSAndrzej Pietrasiewicz 
80100a2430fSAndrzej Pietrasiewicz 	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
80200a2430fSAndrzej Pietrasiewicz 		| USB_CDC_SET_CRC_MODE:
80300a2430fSAndrzej Pietrasiewicz 	{
80400a2430fSAndrzej Pietrasiewicz 		if (w_length != 0 || w_index != ncm->ctrl_id)
80500a2430fSAndrzej Pietrasiewicz 			goto invalid;
80600a2430fSAndrzej Pietrasiewicz 		switch (w_value) {
80700a2430fSAndrzej Pietrasiewicz 		case 0x0000:
80800a2430fSAndrzej Pietrasiewicz 			ncm->is_crc = false;
80900a2430fSAndrzej Pietrasiewicz 			DBG(cdev, "non-CRC mode selected\n");
81000a2430fSAndrzej Pietrasiewicz 			break;
81100a2430fSAndrzej Pietrasiewicz 		case 0x0001:
81200a2430fSAndrzej Pietrasiewicz 			ncm->is_crc = true;
81300a2430fSAndrzej Pietrasiewicz 			DBG(cdev, "CRC mode selected\n");
81400a2430fSAndrzej Pietrasiewicz 			break;
81500a2430fSAndrzej Pietrasiewicz 		default:
81600a2430fSAndrzej Pietrasiewicz 			goto invalid;
81700a2430fSAndrzej Pietrasiewicz 		}
81800a2430fSAndrzej Pietrasiewicz 		value = 0;
81900a2430fSAndrzej Pietrasiewicz 		break;
82000a2430fSAndrzej Pietrasiewicz 	}
82100a2430fSAndrzej Pietrasiewicz 
82200a2430fSAndrzej Pietrasiewicz 	/* and disabled in ncm descriptor: */
82300a2430fSAndrzej Pietrasiewicz 	/* case USB_CDC_GET_NET_ADDRESS: */
82400a2430fSAndrzej Pietrasiewicz 	/* case USB_CDC_SET_NET_ADDRESS: */
82500a2430fSAndrzej Pietrasiewicz 	/* case USB_CDC_GET_MAX_DATAGRAM_SIZE: */
82600a2430fSAndrzej Pietrasiewicz 	/* case USB_CDC_SET_MAX_DATAGRAM_SIZE: */
82700a2430fSAndrzej Pietrasiewicz 
82800a2430fSAndrzej Pietrasiewicz 	default:
82900a2430fSAndrzej Pietrasiewicz invalid:
83000a2430fSAndrzej Pietrasiewicz 		DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
83100a2430fSAndrzej Pietrasiewicz 			ctrl->bRequestType, ctrl->bRequest,
83200a2430fSAndrzej Pietrasiewicz 			w_value, w_index, w_length);
83300a2430fSAndrzej Pietrasiewicz 	}
834550eef0cSRomain Izard 	ncm->ndp_sign = ncm->parser_opts->ndp_sign |
835550eef0cSRomain Izard 		(ncm->is_crc ? NCM_NDP_HDR_CRC : 0);
83600a2430fSAndrzej Pietrasiewicz 
83700a2430fSAndrzej Pietrasiewicz 	/* respond with data transfer or status phase? */
83800a2430fSAndrzej Pietrasiewicz 	if (value >= 0) {
83900a2430fSAndrzej Pietrasiewicz 		DBG(cdev, "ncm req%02x.%02x v%04x i%04x l%d\n",
84000a2430fSAndrzej Pietrasiewicz 			ctrl->bRequestType, ctrl->bRequest,
84100a2430fSAndrzej Pietrasiewicz 			w_value, w_index, w_length);
84200a2430fSAndrzej Pietrasiewicz 		req->zero = 0;
84300a2430fSAndrzej Pietrasiewicz 		req->length = value;
84400a2430fSAndrzej Pietrasiewicz 		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
84500a2430fSAndrzej Pietrasiewicz 		if (value < 0)
84600a2430fSAndrzej Pietrasiewicz 			ERROR(cdev, "ncm req %02x.%02x response err %d\n",
84700a2430fSAndrzej Pietrasiewicz 					ctrl->bRequestType, ctrl->bRequest,
84800a2430fSAndrzej Pietrasiewicz 					value);
84900a2430fSAndrzej Pietrasiewicz 	}
85000a2430fSAndrzej Pietrasiewicz 
85100a2430fSAndrzej Pietrasiewicz 	/* device either stalls (value < 0) or reports success */
85200a2430fSAndrzej Pietrasiewicz 	return value;
85300a2430fSAndrzej Pietrasiewicz }
85400a2430fSAndrzej Pietrasiewicz 
85500a2430fSAndrzej Pietrasiewicz 
ncm_set_alt(struct usb_function * f,unsigned intf,unsigned alt)85600a2430fSAndrzej Pietrasiewicz static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
85700a2430fSAndrzej Pietrasiewicz {
85800a2430fSAndrzej Pietrasiewicz 	struct f_ncm		*ncm = func_to_ncm(f);
85900a2430fSAndrzej Pietrasiewicz 	struct usb_composite_dev *cdev = f->config->cdev;
86000a2430fSAndrzej Pietrasiewicz 
86100a2430fSAndrzej Pietrasiewicz 	/* Control interface has only altsetting 0 */
86200a2430fSAndrzej Pietrasiewicz 	if (intf == ncm->ctrl_id) {
86300a2430fSAndrzej Pietrasiewicz 		if (alt != 0)
86400a2430fSAndrzej Pietrasiewicz 			goto fail;
86500a2430fSAndrzej Pietrasiewicz 
86600a2430fSAndrzej Pietrasiewicz 		DBG(cdev, "reset ncm control %d\n", intf);
86700a2430fSAndrzej Pietrasiewicz 		usb_ep_disable(ncm->notify);
86800a2430fSAndrzej Pietrasiewicz 
86900a2430fSAndrzej Pietrasiewicz 		if (!(ncm->notify->desc)) {
87000a2430fSAndrzej Pietrasiewicz 			DBG(cdev, "init ncm ctrl %d\n", intf);
87100a2430fSAndrzej Pietrasiewicz 			if (config_ep_by_speed(cdev->gadget, f, ncm->notify))
87200a2430fSAndrzej Pietrasiewicz 				goto fail;
87300a2430fSAndrzej Pietrasiewicz 		}
87400a2430fSAndrzej Pietrasiewicz 		usb_ep_enable(ncm->notify);
87500a2430fSAndrzej Pietrasiewicz 
87600a2430fSAndrzej Pietrasiewicz 	/* Data interface has two altsettings, 0 and 1 */
87700a2430fSAndrzej Pietrasiewicz 	} else if (intf == ncm->data_id) {
87800a2430fSAndrzej Pietrasiewicz 		if (alt > 1)
87900a2430fSAndrzej Pietrasiewicz 			goto fail;
88000a2430fSAndrzej Pietrasiewicz 
8816334b8e4SNorihiko Hama 		if (ncm->netdev) {
88200a2430fSAndrzej Pietrasiewicz 			DBG(cdev, "reset ncm\n");
88300a2430fSAndrzej Pietrasiewicz 			ncm->netdev = NULL;
88400a2430fSAndrzej Pietrasiewicz 			gether_disconnect(&ncm->port);
88500a2430fSAndrzej Pietrasiewicz 			ncm_reset_values(ncm);
88600a2430fSAndrzej Pietrasiewicz 		}
88700a2430fSAndrzej Pietrasiewicz 
88800a2430fSAndrzej Pietrasiewicz 		/*
88900a2430fSAndrzej Pietrasiewicz 		 * CDC Network only sends data in non-default altsettings.
89000a2430fSAndrzej Pietrasiewicz 		 * Changing altsettings resets filters, statistics, etc.
89100a2430fSAndrzej Pietrasiewicz 		 */
89200a2430fSAndrzej Pietrasiewicz 		if (alt == 1) {
89300a2430fSAndrzej Pietrasiewicz 			struct net_device	*net;
89400a2430fSAndrzej Pietrasiewicz 
89500a2430fSAndrzej Pietrasiewicz 			if (!ncm->port.in_ep->desc ||
89600a2430fSAndrzej Pietrasiewicz 			    !ncm->port.out_ep->desc) {
89700a2430fSAndrzej Pietrasiewicz 				DBG(cdev, "init ncm\n");
89800a2430fSAndrzej Pietrasiewicz 				if (config_ep_by_speed(cdev->gadget, f,
89900a2430fSAndrzej Pietrasiewicz 						       ncm->port.in_ep) ||
90000a2430fSAndrzej Pietrasiewicz 				    config_ep_by_speed(cdev->gadget, f,
90100a2430fSAndrzej Pietrasiewicz 						       ncm->port.out_ep)) {
90200a2430fSAndrzej Pietrasiewicz 					ncm->port.in_ep->desc = NULL;
90300a2430fSAndrzej Pietrasiewicz 					ncm->port.out_ep->desc = NULL;
90400a2430fSAndrzej Pietrasiewicz 					goto fail;
90500a2430fSAndrzej Pietrasiewicz 				}
90600a2430fSAndrzej Pietrasiewicz 			}
90700a2430fSAndrzej Pietrasiewicz 
90800a2430fSAndrzej Pietrasiewicz 			/* TODO */
90900a2430fSAndrzej Pietrasiewicz 			/* Enable zlps by default for NCM conformance;
91000a2430fSAndrzej Pietrasiewicz 			 * override for musb_hdrc (avoids txdma ovhead)
91100a2430fSAndrzej Pietrasiewicz 			 */
9127a896d40SRobert Baldyga 			ncm->port.is_zlp_ok =
9137a896d40SRobert Baldyga 				gadget_is_zlp_supported(cdev->gadget);
91400a2430fSAndrzej Pietrasiewicz 			ncm->port.cdc_filter = DEFAULT_FILTER;
91500a2430fSAndrzej Pietrasiewicz 			DBG(cdev, "activate ncm\n");
91600a2430fSAndrzej Pietrasiewicz 			net = gether_connect(&ncm->port);
91700a2430fSAndrzej Pietrasiewicz 			if (IS_ERR(net))
91800a2430fSAndrzej Pietrasiewicz 				return PTR_ERR(net);
91900a2430fSAndrzej Pietrasiewicz 			ncm->netdev = net;
92000a2430fSAndrzej Pietrasiewicz 		}
92100a2430fSAndrzej Pietrasiewicz 
92200a2430fSAndrzej Pietrasiewicz 		spin_lock(&ncm->lock);
92300a2430fSAndrzej Pietrasiewicz 		ncm_notify(ncm);
92400a2430fSAndrzej Pietrasiewicz 		spin_unlock(&ncm->lock);
92500a2430fSAndrzej Pietrasiewicz 	} else
92600a2430fSAndrzej Pietrasiewicz 		goto fail;
92700a2430fSAndrzej Pietrasiewicz 
92800a2430fSAndrzej Pietrasiewicz 	return 0;
92900a2430fSAndrzej Pietrasiewicz fail:
93000a2430fSAndrzej Pietrasiewicz 	return -EINVAL;
93100a2430fSAndrzej Pietrasiewicz }
93200a2430fSAndrzej Pietrasiewicz 
93300a2430fSAndrzej Pietrasiewicz /*
93400a2430fSAndrzej Pietrasiewicz  * Because the data interface supports multiple altsettings,
93500a2430fSAndrzej Pietrasiewicz  * this NCM function *MUST* implement a get_alt() method.
93600a2430fSAndrzej Pietrasiewicz  */
ncm_get_alt(struct usb_function * f,unsigned intf)93700a2430fSAndrzej Pietrasiewicz static int ncm_get_alt(struct usb_function *f, unsigned intf)
93800a2430fSAndrzej Pietrasiewicz {
93900a2430fSAndrzej Pietrasiewicz 	struct f_ncm		*ncm = func_to_ncm(f);
94000a2430fSAndrzej Pietrasiewicz 
94100a2430fSAndrzej Pietrasiewicz 	if (intf == ncm->ctrl_id)
94200a2430fSAndrzej Pietrasiewicz 		return 0;
9436b4012a2SRobert Baldyga 	return ncm->port.in_ep->enabled ? 1 : 0;
94400a2430fSAndrzej Pietrasiewicz }
94500a2430fSAndrzej Pietrasiewicz 
package_for_tx(struct f_ncm * ncm)94600a2430fSAndrzej Pietrasiewicz static struct sk_buff *package_for_tx(struct f_ncm *ncm)
94700a2430fSAndrzej Pietrasiewicz {
94800a2430fSAndrzej Pietrasiewicz 	__le16		*ntb_iter;
94900a2430fSAndrzej Pietrasiewicz 	struct sk_buff	*skb2 = NULL;
95000a2430fSAndrzej Pietrasiewicz 	unsigned	ndp_pad;
95100a2430fSAndrzej Pietrasiewicz 	unsigned	ndp_index;
95200a2430fSAndrzej Pietrasiewicz 	unsigned	new_len;
95300a2430fSAndrzej Pietrasiewicz 
95400a2430fSAndrzej Pietrasiewicz 	const struct ndp_parser_opts *opts = ncm->parser_opts;
95500a2430fSAndrzej Pietrasiewicz 	const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment);
95600a2430fSAndrzej Pietrasiewicz 	const int dgram_idx_len = 2 * 2 * opts->dgram_item_len;
95700a2430fSAndrzej Pietrasiewicz 
95800a2430fSAndrzej Pietrasiewicz 	/* Stop the timer */
95900a2430fSAndrzej Pietrasiewicz 	hrtimer_try_to_cancel(&ncm->task_timer);
96000a2430fSAndrzej Pietrasiewicz 
96100a2430fSAndrzej Pietrasiewicz 	ndp_pad = ALIGN(ncm->skb_tx_data->len, ndp_align) -
96200a2430fSAndrzej Pietrasiewicz 			ncm->skb_tx_data->len;
96300a2430fSAndrzej Pietrasiewicz 	ndp_index = ncm->skb_tx_data->len + ndp_pad;
96400a2430fSAndrzej Pietrasiewicz 	new_len = ndp_index + dgram_idx_len + ncm->skb_tx_ndp->len;
96500a2430fSAndrzej Pietrasiewicz 
96600a2430fSAndrzej Pietrasiewicz 	/* Set the final BlockLength and wNdpIndex */
96700a2430fSAndrzej Pietrasiewicz 	ntb_iter = (void *) ncm->skb_tx_data->data;
96800a2430fSAndrzej Pietrasiewicz 	/* Increment pointer to BlockLength */
96900a2430fSAndrzej Pietrasiewicz 	ntb_iter += 2 + 1 + 1;
97000a2430fSAndrzej Pietrasiewicz 	put_ncm(&ntb_iter, opts->block_length, new_len);
97100a2430fSAndrzej Pietrasiewicz 	put_ncm(&ntb_iter, opts->ndp_index, ndp_index);
97200a2430fSAndrzej Pietrasiewicz 
97300a2430fSAndrzej Pietrasiewicz 	/* Set the final NDP wLength */
97400a2430fSAndrzej Pietrasiewicz 	new_len = opts->ndp_size +
97500a2430fSAndrzej Pietrasiewicz 			(ncm->ndp_dgram_count * dgram_idx_len);
97600a2430fSAndrzej Pietrasiewicz 	ncm->ndp_dgram_count = 0;
97700a2430fSAndrzej Pietrasiewicz 	/* Increment from start to wLength */
97800a2430fSAndrzej Pietrasiewicz 	ntb_iter = (void *) ncm->skb_tx_ndp->data;
97900a2430fSAndrzej Pietrasiewicz 	ntb_iter += 2;
98000a2430fSAndrzej Pietrasiewicz 	put_unaligned_le16(new_len, ntb_iter);
98100a2430fSAndrzej Pietrasiewicz 
98200a2430fSAndrzej Pietrasiewicz 	/* Merge the skbs */
98300a2430fSAndrzej Pietrasiewicz 	swap(skb2, ncm->skb_tx_data);
98400a2430fSAndrzej Pietrasiewicz 	if (ncm->skb_tx_data) {
98538314e59STorsten Polle 		dev_consume_skb_any(ncm->skb_tx_data);
98600a2430fSAndrzej Pietrasiewicz 		ncm->skb_tx_data = NULL;
98700a2430fSAndrzej Pietrasiewicz 	}
98800a2430fSAndrzej Pietrasiewicz 
98900a2430fSAndrzej Pietrasiewicz 	/* Insert NDP alignment. */
990b952f4dfSyuan linyu 	skb_put_zero(skb2, ndp_pad);
99100a2430fSAndrzej Pietrasiewicz 
99200a2430fSAndrzej Pietrasiewicz 	/* Copy NTB across. */
993b952f4dfSyuan linyu 	skb_put_data(skb2, ncm->skb_tx_ndp->data, ncm->skb_tx_ndp->len);
99438314e59STorsten Polle 	dev_consume_skb_any(ncm->skb_tx_ndp);
99500a2430fSAndrzej Pietrasiewicz 	ncm->skb_tx_ndp = NULL;
99600a2430fSAndrzej Pietrasiewicz 
99700a2430fSAndrzej Pietrasiewicz 	/* Insert zero'd datagram. */
998b952f4dfSyuan linyu 	skb_put_zero(skb2, dgram_idx_len);
99900a2430fSAndrzej Pietrasiewicz 
100000a2430fSAndrzej Pietrasiewicz 	return skb2;
100100a2430fSAndrzej Pietrasiewicz }
100200a2430fSAndrzej Pietrasiewicz 
ncm_wrap_ntb(struct gether * port,struct sk_buff * skb)100300a2430fSAndrzej Pietrasiewicz static struct sk_buff *ncm_wrap_ntb(struct gether *port,
100400a2430fSAndrzej Pietrasiewicz 				    struct sk_buff *skb)
100500a2430fSAndrzej Pietrasiewicz {
100600a2430fSAndrzej Pietrasiewicz 	struct f_ncm	*ncm = func_to_ncm(&port->func);
100700a2430fSAndrzej Pietrasiewicz 	struct sk_buff	*skb2 = NULL;
10086607d1a4SMaciej Żenczykowski 
10096607d1a4SMaciej Żenczykowski 	if (skb) {
101000a2430fSAndrzej Pietrasiewicz 		int		ncb_len = 0;
101100a2430fSAndrzej Pietrasiewicz 		__le16		*ntb_data;
101200a2430fSAndrzej Pietrasiewicz 		__le16		*ntb_ndp;
101300a2430fSAndrzej Pietrasiewicz 		int		dgram_pad;
101400a2430fSAndrzej Pietrasiewicz 
101500a2430fSAndrzej Pietrasiewicz 		unsigned	max_size = ncm->port.fixed_in_len;
101600a2430fSAndrzej Pietrasiewicz 		const struct ndp_parser_opts *opts = ncm->parser_opts;
101700a2430fSAndrzej Pietrasiewicz 		const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment);
101800a2430fSAndrzej Pietrasiewicz 		const int div = le16_to_cpu(ntb_parameters.wNdpInDivisor);
101900a2430fSAndrzej Pietrasiewicz 		const int rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder);
102000a2430fSAndrzej Pietrasiewicz 		const int dgram_idx_len = 2 * 2 * opts->dgram_item_len;
102100a2430fSAndrzej Pietrasiewicz 
102200a2430fSAndrzej Pietrasiewicz 		/* Add the CRC if required up front */
102300a2430fSAndrzej Pietrasiewicz 		if (ncm->is_crc) {
102400a2430fSAndrzej Pietrasiewicz 			uint32_t	crc;
102500a2430fSAndrzej Pietrasiewicz 			__le16		*crc_pos;
102600a2430fSAndrzej Pietrasiewicz 
102700a2430fSAndrzej Pietrasiewicz 			crc = ~crc32_le(~0,
102800a2430fSAndrzej Pietrasiewicz 					skb->data,
102900a2430fSAndrzej Pietrasiewicz 					skb->len);
10304df864c1SJohannes Berg 			crc_pos = skb_put(skb, sizeof(uint32_t));
103100a2430fSAndrzej Pietrasiewicz 			put_unaligned_le32(crc, crc_pos);
103200a2430fSAndrzej Pietrasiewicz 		}
103300a2430fSAndrzej Pietrasiewicz 
103400a2430fSAndrzej Pietrasiewicz 		/* If the new skb is too big for the current NCM NTB then
103500a2430fSAndrzej Pietrasiewicz 		 * set the current stored skb to be sent now and clear it
103600a2430fSAndrzej Pietrasiewicz 		 * ready for new data.
103700a2430fSAndrzej Pietrasiewicz 		 * NOTE: Assume maximum align for speed of calculation.
103800a2430fSAndrzej Pietrasiewicz 		 */
103900a2430fSAndrzej Pietrasiewicz 		if (ncm->skb_tx_data
104000a2430fSAndrzej Pietrasiewicz 		    && (ncm->ndp_dgram_count >= TX_MAX_NUM_DPE
104100a2430fSAndrzej Pietrasiewicz 		    || (ncm->skb_tx_data->len +
104200a2430fSAndrzej Pietrasiewicz 		    div + rem + skb->len +
104300a2430fSAndrzej Pietrasiewicz 		    ncm->skb_tx_ndp->len + ndp_align + (2 * dgram_idx_len))
104400a2430fSAndrzej Pietrasiewicz 		    > max_size)) {
104500a2430fSAndrzej Pietrasiewicz 			skb2 = package_for_tx(ncm);
104600a2430fSAndrzej Pietrasiewicz 			if (!skb2)
104700a2430fSAndrzej Pietrasiewicz 				goto err;
104800a2430fSAndrzej Pietrasiewicz 		}
104900a2430fSAndrzej Pietrasiewicz 
105000a2430fSAndrzej Pietrasiewicz 		if (!ncm->skb_tx_data) {
105100a2430fSAndrzej Pietrasiewicz 			ncb_len = opts->nth_size;
105200a2430fSAndrzej Pietrasiewicz 			dgram_pad = ALIGN(ncb_len, div) + rem - ncb_len;
105300a2430fSAndrzej Pietrasiewicz 			ncb_len += dgram_pad;
105400a2430fSAndrzej Pietrasiewicz 
105500a2430fSAndrzej Pietrasiewicz 			/* Create a new skb for the NTH and datagrams. */
105600a2430fSAndrzej Pietrasiewicz 			ncm->skb_tx_data = alloc_skb(max_size, GFP_ATOMIC);
105700a2430fSAndrzej Pietrasiewicz 			if (!ncm->skb_tx_data)
105800a2430fSAndrzej Pietrasiewicz 				goto err;
105900a2430fSAndrzej Pietrasiewicz 
10609a5380c3STorsten Polle 			ncm->skb_tx_data->dev = ncm->netdev;
1061b080db58SJohannes Berg 			ntb_data = skb_put_zero(ncm->skb_tx_data, ncb_len);
106200a2430fSAndrzej Pietrasiewicz 			/* dwSignature */
106300a2430fSAndrzej Pietrasiewicz 			put_unaligned_le32(opts->nth_sign, ntb_data);
106400a2430fSAndrzej Pietrasiewicz 			ntb_data += 2;
106500a2430fSAndrzej Pietrasiewicz 			/* wHeaderLength */
106600a2430fSAndrzej Pietrasiewicz 			put_unaligned_le16(opts->nth_size, ntb_data++);
106700a2430fSAndrzej Pietrasiewicz 
106800a2430fSAndrzej Pietrasiewicz 			/* Allocate an skb for storing the NDP,
106900a2430fSAndrzej Pietrasiewicz 			 * TX_MAX_NUM_DPE should easily suffice for a
107000a2430fSAndrzej Pietrasiewicz 			 * 16k packet.
107100a2430fSAndrzej Pietrasiewicz 			 */
107200a2430fSAndrzej Pietrasiewicz 			ncm->skb_tx_ndp = alloc_skb((int)(opts->ndp_size
107300a2430fSAndrzej Pietrasiewicz 						    + opts->dpe_size
107400a2430fSAndrzej Pietrasiewicz 						    * TX_MAX_NUM_DPE),
107500a2430fSAndrzej Pietrasiewicz 						    GFP_ATOMIC);
107600a2430fSAndrzej Pietrasiewicz 			if (!ncm->skb_tx_ndp)
107700a2430fSAndrzej Pietrasiewicz 				goto err;
10789a5380c3STorsten Polle 
10799a5380c3STorsten Polle 			ncm->skb_tx_ndp->dev = ncm->netdev;
10804df864c1SJohannes Berg 			ntb_ndp = skb_put(ncm->skb_tx_ndp, opts->ndp_size);
108100a2430fSAndrzej Pietrasiewicz 			memset(ntb_ndp, 0, ncb_len);
108200a2430fSAndrzej Pietrasiewicz 			/* dwSignature */
108300a2430fSAndrzej Pietrasiewicz 			put_unaligned_le32(ncm->ndp_sign, ntb_ndp);
108400a2430fSAndrzej Pietrasiewicz 			ntb_ndp += 2;
108500a2430fSAndrzej Pietrasiewicz 
108600a2430fSAndrzej Pietrasiewicz 			/* There is always a zeroed entry */
108700a2430fSAndrzej Pietrasiewicz 			ncm->ndp_dgram_count = 1;
108800a2430fSAndrzej Pietrasiewicz 
108900a2430fSAndrzej Pietrasiewicz 			/* Note: we skip opts->next_ndp_index */
109000a2430fSAndrzej Pietrasiewicz 
10911958ff5aSMaciej Żenczykowski 			/* Start the timer. */
10928b0e1953SThomas Gleixner 			hrtimer_start(&ncm->task_timer, TX_TIMEOUT_NSECS,
1093b1a31a5fSThomas Gleixner 				      HRTIMER_MODE_REL_SOFT);
10941958ff5aSMaciej Żenczykowski 		}
109500a2430fSAndrzej Pietrasiewicz 
109600a2430fSAndrzej Pietrasiewicz 		/* Add the datagram position entries */
1097b080db58SJohannes Berg 		ntb_ndp = skb_put_zero(ncm->skb_tx_ndp, dgram_idx_len);
109800a2430fSAndrzej Pietrasiewicz 
109900a2430fSAndrzej Pietrasiewicz 		ncb_len = ncm->skb_tx_data->len;
110000a2430fSAndrzej Pietrasiewicz 		dgram_pad = ALIGN(ncb_len, div) + rem - ncb_len;
110100a2430fSAndrzej Pietrasiewicz 		ncb_len += dgram_pad;
110200a2430fSAndrzej Pietrasiewicz 
110300a2430fSAndrzej Pietrasiewicz 		/* (d)wDatagramIndex */
110400a2430fSAndrzej Pietrasiewicz 		put_ncm(&ntb_ndp, opts->dgram_item_len, ncb_len);
110500a2430fSAndrzej Pietrasiewicz 		/* (d)wDatagramLength */
110600a2430fSAndrzej Pietrasiewicz 		put_ncm(&ntb_ndp, opts->dgram_item_len, skb->len);
110700a2430fSAndrzej Pietrasiewicz 		ncm->ndp_dgram_count++;
110800a2430fSAndrzej Pietrasiewicz 
110900a2430fSAndrzej Pietrasiewicz 		/* Add the new data to the skb */
1110b952f4dfSyuan linyu 		skb_put_zero(ncm->skb_tx_data, dgram_pad);
1111b952f4dfSyuan linyu 		skb_put_data(ncm->skb_tx_data, skb->data, skb->len);
111238314e59STorsten Polle 		dev_consume_skb_any(skb);
111300a2430fSAndrzej Pietrasiewicz 		skb = NULL;
111400a2430fSAndrzej Pietrasiewicz 
1115dbaaca9aSMaciej Żenczykowski 	} else if (ncm->skb_tx_data) {
1116dbaaca9aSMaciej Żenczykowski 		/* If we get here ncm_wrap_ntb() was called with NULL skb,
1117dbaaca9aSMaciej Żenczykowski 		 * because eth_start_xmit() was called with NULL skb by
1118dbaaca9aSMaciej Żenczykowski 		 * ncm_tx_timeout() - hence, this is our signal to flush/send.
1119dbaaca9aSMaciej Żenczykowski 		 */
112000a2430fSAndrzej Pietrasiewicz 		skb2 = package_for_tx(ncm);
112100a2430fSAndrzej Pietrasiewicz 		if (!skb2)
112200a2430fSAndrzej Pietrasiewicz 			goto err;
112300a2430fSAndrzej Pietrasiewicz 	}
112400a2430fSAndrzej Pietrasiewicz 
112500a2430fSAndrzej Pietrasiewicz 	return skb2;
112600a2430fSAndrzej Pietrasiewicz 
112700a2430fSAndrzej Pietrasiewicz err:
112800a2430fSAndrzej Pietrasiewicz 	ncm->netdev->stats.tx_dropped++;
112900a2430fSAndrzej Pietrasiewicz 
113000a2430fSAndrzej Pietrasiewicz 	if (skb)
113100a2430fSAndrzej Pietrasiewicz 		dev_kfree_skb_any(skb);
113200a2430fSAndrzej Pietrasiewicz 	if (ncm->skb_tx_data)
113300a2430fSAndrzej Pietrasiewicz 		dev_kfree_skb_any(ncm->skb_tx_data);
113400a2430fSAndrzej Pietrasiewicz 	if (ncm->skb_tx_ndp)
113500a2430fSAndrzej Pietrasiewicz 		dev_kfree_skb_any(ncm->skb_tx_ndp);
113600a2430fSAndrzej Pietrasiewicz 
113700a2430fSAndrzej Pietrasiewicz 	return NULL;
113800a2430fSAndrzej Pietrasiewicz }
113900a2430fSAndrzej Pietrasiewicz 
114000a2430fSAndrzej Pietrasiewicz /*
1141b1a31a5fSThomas Gleixner  * The transmit should only be run if no skb data has been sent
1142b1a31a5fSThomas Gleixner  * for a certain duration.
114300a2430fSAndrzej Pietrasiewicz  */
ncm_tx_timeout(struct hrtimer * data)1144b1a31a5fSThomas Gleixner static enum hrtimer_restart ncm_tx_timeout(struct hrtimer *data)
114500a2430fSAndrzej Pietrasiewicz {
1146b1a31a5fSThomas Gleixner 	struct f_ncm *ncm = container_of(data, struct f_ncm, task_timer);
1147cf4e2e88SMaciej Żenczykowski 	struct net_device *netdev = READ_ONCE(ncm->netdev);
114800a2430fSAndrzej Pietrasiewicz 
1149ec017d6bSMaciej Żenczykowski 	if (netdev) {
1150c2c0e8b2SDavid S. Miller 		/* XXX This allowance of a NULL skb argument to ndo_start_xmit
1151c2c0e8b2SDavid S. Miller 		 * XXX is not sane.  The gadget layer should be redesigned so
1152c2c0e8b2SDavid S. Miller 		 * XXX that the dev->wrap() invocations to build SKBs is transparent
1153c2c0e8b2SDavid S. Miller 		 * XXX and performed in some way outside of the ndo_start_xmit
1154c2c0e8b2SDavid S. Miller 		 * XXX interface.
1155cf4e2e88SMaciej Żenczykowski 		 *
1156cf4e2e88SMaciej Żenczykowski 		 * This will call directly into u_ether's eth_start_xmit()
1157c2c0e8b2SDavid S. Miller 		 */
1158cf4e2e88SMaciej Żenczykowski 		netdev->netdev_ops->ndo_start_xmit(NULL, netdev);
115900a2430fSAndrzej Pietrasiewicz 	}
116000a2430fSAndrzej Pietrasiewicz 	return HRTIMER_NORESTART;
116100a2430fSAndrzej Pietrasiewicz }
116200a2430fSAndrzej Pietrasiewicz 
ncm_unwrap_ntb(struct gether * port,struct sk_buff * skb,struct sk_buff_head * list)116300a2430fSAndrzej Pietrasiewicz static int ncm_unwrap_ntb(struct gether *port,
116400a2430fSAndrzej Pietrasiewicz 			  struct sk_buff *skb,
116500a2430fSAndrzej Pietrasiewicz 			  struct sk_buff_head *list)
116600a2430fSAndrzej Pietrasiewicz {
116700a2430fSAndrzej Pietrasiewicz 	struct f_ncm	*ncm = func_to_ncm(&port->func);
1168427694cfSKrishna Kurapati 	unsigned char	*ntb_ptr = skb->data;
1169427694cfSKrishna Kurapati 	__le16		*tmp;
117000a2430fSAndrzej Pietrasiewicz 	unsigned	index, index2;
117100a2430fSAndrzej Pietrasiewicz 	int		ndp_index;
117200a2430fSAndrzej Pietrasiewicz 	unsigned	dg_len, dg_len2;
117300a2430fSAndrzej Pietrasiewicz 	unsigned	ndp_len;
11742b74b0a0SBrooke Basile 	unsigned	block_len;
117500a2430fSAndrzej Pietrasiewicz 	struct sk_buff	*skb2;
117600a2430fSAndrzej Pietrasiewicz 	int		ret = -EINVAL;
11772b74b0a0SBrooke Basile 	unsigned	ntb_max = le32_to_cpu(ntb_parameters.dwNtbOutMaxSize);
11781900daeeSKrishna Kurapati 	unsigned	frame_max;
117900a2430fSAndrzej Pietrasiewicz 	const struct ndp_parser_opts *opts = ncm->parser_opts;
118000a2430fSAndrzej Pietrasiewicz 	unsigned	crc_len = ncm->is_crc ? sizeof(uint32_t) : 0;
118100a2430fSAndrzej Pietrasiewicz 	int		dgram_counter;
1182427694cfSKrishna Kurapati 	int		to_process = skb->len;
11831900daeeSKrishna Kurapati 	struct f_ncm_opts *ncm_opts;
11841900daeeSKrishna Kurapati 
11851900daeeSKrishna Kurapati 	ncm_opts = container_of(port->func.fi, struct f_ncm_opts, func_inst);
11861900daeeSKrishna Kurapati 	frame_max = ncm_opts->max_segment_size;
1187427694cfSKrishna Kurapati 
1188427694cfSKrishna Kurapati parse_ntb:
1189427694cfSKrishna Kurapati 	tmp = (__le16 *)ntb_ptr;
119000a2430fSAndrzej Pietrasiewicz 
119100a2430fSAndrzej Pietrasiewicz 	/* dwSignature */
119200a2430fSAndrzej Pietrasiewicz 	if (get_unaligned_le32(tmp) != opts->nth_sign) {
119300a2430fSAndrzej Pietrasiewicz 		INFO(port->func.config->cdev, "Wrong NTH SIGN, skblen %d\n",
119400a2430fSAndrzej Pietrasiewicz 			skb->len);
119500a2430fSAndrzej Pietrasiewicz 		print_hex_dump(KERN_INFO, "HEAD:", DUMP_PREFIX_ADDRESS, 32, 1,
119600a2430fSAndrzej Pietrasiewicz 			       skb->data, 32, false);
119700a2430fSAndrzej Pietrasiewicz 
119800a2430fSAndrzej Pietrasiewicz 		goto err;
119900a2430fSAndrzej Pietrasiewicz 	}
120000a2430fSAndrzej Pietrasiewicz 	tmp += 2;
120100a2430fSAndrzej Pietrasiewicz 	/* wHeaderLength */
120200a2430fSAndrzej Pietrasiewicz 	if (get_unaligned_le16(tmp++) != opts->nth_size) {
120300a2430fSAndrzej Pietrasiewicz 		INFO(port->func.config->cdev, "Wrong NTB headersize\n");
120400a2430fSAndrzej Pietrasiewicz 		goto err;
120500a2430fSAndrzej Pietrasiewicz 	}
120600a2430fSAndrzej Pietrasiewicz 	tmp++; /* skip wSequence */
120700a2430fSAndrzej Pietrasiewicz 
12082b74b0a0SBrooke Basile 	block_len = get_ncm(&tmp, opts->block_length);
120900a2430fSAndrzej Pietrasiewicz 	/* (d)wBlockLength */
12102b74b0a0SBrooke Basile 	if (block_len > ntb_max) {
121100a2430fSAndrzej Pietrasiewicz 		INFO(port->func.config->cdev, "OUT size exceeded\n");
121200a2430fSAndrzej Pietrasiewicz 		goto err;
121300a2430fSAndrzej Pietrasiewicz 	}
121400a2430fSAndrzej Pietrasiewicz 
121500a2430fSAndrzej Pietrasiewicz 	ndp_index = get_ncm(&tmp, opts->ndp_index);
121600a2430fSAndrzej Pietrasiewicz 
121700a2430fSAndrzej Pietrasiewicz 	/* Run through all the NDP's in the NTB */
121800a2430fSAndrzej Pietrasiewicz 	do {
12192b74b0a0SBrooke Basile 		/*
12202b74b0a0SBrooke Basile 		 * NCM 3.2
12212b74b0a0SBrooke Basile 		 * dwNdpIndex
12222b74b0a0SBrooke Basile 		 */
12232b74b0a0SBrooke Basile 		if (((ndp_index % 4) != 0) ||
12242b74b0a0SBrooke Basile 				(ndp_index < opts->nth_size) ||
12252b74b0a0SBrooke Basile 				(ndp_index > (block_len -
12262b74b0a0SBrooke Basile 					      opts->ndp_size))) {
122700a2430fSAndrzej Pietrasiewicz 			INFO(port->func.config->cdev, "Bad index: %#X\n",
122800a2430fSAndrzej Pietrasiewicz 			     ndp_index);
122900a2430fSAndrzej Pietrasiewicz 			goto err;
123000a2430fSAndrzej Pietrasiewicz 		}
123100a2430fSAndrzej Pietrasiewicz 
12322b74b0a0SBrooke Basile 		/*
12332b74b0a0SBrooke Basile 		 * walk through NDP
12342b74b0a0SBrooke Basile 		 * dwSignature
12352b74b0a0SBrooke Basile 		 */
1236427694cfSKrishna Kurapati 		tmp = (__le16 *)(ntb_ptr + ndp_index);
123700a2430fSAndrzej Pietrasiewicz 		if (get_unaligned_le32(tmp) != ncm->ndp_sign) {
123800a2430fSAndrzej Pietrasiewicz 			INFO(port->func.config->cdev, "Wrong NDP SIGN\n");
123900a2430fSAndrzej Pietrasiewicz 			goto err;
124000a2430fSAndrzej Pietrasiewicz 		}
124100a2430fSAndrzej Pietrasiewicz 		tmp += 2;
124200a2430fSAndrzej Pietrasiewicz 
124300a2430fSAndrzej Pietrasiewicz 		ndp_len = get_unaligned_le16(tmp++);
124400a2430fSAndrzej Pietrasiewicz 		/*
124500a2430fSAndrzej Pietrasiewicz 		 * NCM 3.3.1
12462b74b0a0SBrooke Basile 		 * wLength
124700a2430fSAndrzej Pietrasiewicz 		 * entry is 2 items
124800a2430fSAndrzej Pietrasiewicz 		 * item size is 16/32 bits, opts->dgram_item_len * 2 bytes
124900a2430fSAndrzej Pietrasiewicz 		 * minimal: struct usb_cdc_ncm_ndpX + normal entry + zero entry
125000a2430fSAndrzej Pietrasiewicz 		 * Each entry is a dgram index and a dgram length.
125100a2430fSAndrzej Pietrasiewicz 		 */
125200a2430fSAndrzej Pietrasiewicz 		if ((ndp_len < opts->ndp_size
12532b74b0a0SBrooke Basile 				+ 2 * 2 * (opts->dgram_item_len * 2)) ||
12542b74b0a0SBrooke Basile 				(ndp_len % opts->ndplen_align != 0)) {
125500a2430fSAndrzej Pietrasiewicz 			INFO(port->func.config->cdev, "Bad NDP length: %#X\n",
125600a2430fSAndrzej Pietrasiewicz 			     ndp_len);
125700a2430fSAndrzej Pietrasiewicz 			goto err;
125800a2430fSAndrzej Pietrasiewicz 		}
125900a2430fSAndrzej Pietrasiewicz 		tmp += opts->reserved1;
126000a2430fSAndrzej Pietrasiewicz 		/* Check for another NDP (d)wNextNdpIndex */
126100a2430fSAndrzej Pietrasiewicz 		ndp_index = get_ncm(&tmp, opts->next_ndp_index);
126200a2430fSAndrzej Pietrasiewicz 		tmp += opts->reserved2;
126300a2430fSAndrzej Pietrasiewicz 
126400a2430fSAndrzej Pietrasiewicz 		ndp_len -= opts->ndp_size;
126500a2430fSAndrzej Pietrasiewicz 		index2 = get_ncm(&tmp, opts->dgram_item_len);
126600a2430fSAndrzej Pietrasiewicz 		dg_len2 = get_ncm(&tmp, opts->dgram_item_len);
126700a2430fSAndrzej Pietrasiewicz 		dgram_counter = 0;
126800a2430fSAndrzej Pietrasiewicz 
126900a2430fSAndrzej Pietrasiewicz 		do {
127000a2430fSAndrzej Pietrasiewicz 			index = index2;
12712b74b0a0SBrooke Basile 			/* wDatagramIndex[0] */
12722b74b0a0SBrooke Basile 			if ((index < opts->nth_size) ||
12732b74b0a0SBrooke Basile 					(index > block_len - opts->dpe_size)) {
12742b74b0a0SBrooke Basile 				INFO(port->func.config->cdev,
12752b74b0a0SBrooke Basile 				     "Bad index: %#X\n", index);
12762b74b0a0SBrooke Basile 				goto err;
12772b74b0a0SBrooke Basile 			}
12782b74b0a0SBrooke Basile 
127900a2430fSAndrzej Pietrasiewicz 			dg_len = dg_len2;
12802b74b0a0SBrooke Basile 			/*
12812b74b0a0SBrooke Basile 			 * wDatagramLength[0]
12822b74b0a0SBrooke Basile 			 * ethernet hdr + crc or larger than max frame size
12832b74b0a0SBrooke Basile 			 */
12842b74b0a0SBrooke Basile 			if ((dg_len < 14 + crc_len) ||
12852b74b0a0SBrooke Basile 					(dg_len > frame_max)) {
128600a2430fSAndrzej Pietrasiewicz 				INFO(port->func.config->cdev,
128700a2430fSAndrzej Pietrasiewicz 				     "Bad dgram length: %#X\n", dg_len);
128800a2430fSAndrzej Pietrasiewicz 				goto err;
128900a2430fSAndrzej Pietrasiewicz 			}
129000a2430fSAndrzej Pietrasiewicz 			if (ncm->is_crc) {
129100a2430fSAndrzej Pietrasiewicz 				uint32_t crc, crc2;
129200a2430fSAndrzej Pietrasiewicz 
1293427694cfSKrishna Kurapati 				crc = get_unaligned_le32(ntb_ptr +
129400a2430fSAndrzej Pietrasiewicz 							 index + dg_len -
129500a2430fSAndrzej Pietrasiewicz 							 crc_len);
129600a2430fSAndrzej Pietrasiewicz 				crc2 = ~crc32_le(~0,
1297427694cfSKrishna Kurapati 						 ntb_ptr + index,
129800a2430fSAndrzej Pietrasiewicz 						 dg_len - crc_len);
129900a2430fSAndrzej Pietrasiewicz 				if (crc != crc2) {
130000a2430fSAndrzej Pietrasiewicz 					INFO(port->func.config->cdev,
130100a2430fSAndrzej Pietrasiewicz 					     "Bad CRC\n");
130200a2430fSAndrzej Pietrasiewicz 					goto err;
130300a2430fSAndrzej Pietrasiewicz 				}
130400a2430fSAndrzej Pietrasiewicz 			}
130500a2430fSAndrzej Pietrasiewicz 
130600a2430fSAndrzej Pietrasiewicz 			index2 = get_ncm(&tmp, opts->dgram_item_len);
130700a2430fSAndrzej Pietrasiewicz 			dg_len2 = get_ncm(&tmp, opts->dgram_item_len);
130800a2430fSAndrzej Pietrasiewicz 
13092b74b0a0SBrooke Basile 			/* wDatagramIndex[1] */
13102b74b0a0SBrooke Basile 			if (index2 > block_len - opts->dpe_size) {
13112b74b0a0SBrooke Basile 				INFO(port->func.config->cdev,
13122b74b0a0SBrooke Basile 				     "Bad index: %#X\n", index2);
13132b74b0a0SBrooke Basile 				goto err;
13142b74b0a0SBrooke Basile 			}
13152b74b0a0SBrooke Basile 
131600a2430fSAndrzej Pietrasiewicz 			/*
131700a2430fSAndrzej Pietrasiewicz 			 * Copy the data into a new skb.
131800a2430fSAndrzej Pietrasiewicz 			 * This ensures the truesize is correct
131900a2430fSAndrzej Pietrasiewicz 			 */
132000a2430fSAndrzej Pietrasiewicz 			skb2 = netdev_alloc_skb_ip_align(ncm->netdev,
132100a2430fSAndrzej Pietrasiewicz 							 dg_len - crc_len);
132200a2430fSAndrzej Pietrasiewicz 			if (skb2 == NULL)
132300a2430fSAndrzej Pietrasiewicz 				goto err;
1324427694cfSKrishna Kurapati 			skb_put_data(skb2, ntb_ptr + index,
132559ae1d12SJohannes Berg 				     dg_len - crc_len);
132600a2430fSAndrzej Pietrasiewicz 
132700a2430fSAndrzej Pietrasiewicz 			skb_queue_tail(list, skb2);
132800a2430fSAndrzej Pietrasiewicz 
132900a2430fSAndrzej Pietrasiewicz 			ndp_len -= 2 * (opts->dgram_item_len * 2);
133000a2430fSAndrzej Pietrasiewicz 
133100a2430fSAndrzej Pietrasiewicz 			dgram_counter++;
1332028296e4SBryan O'Donoghue 			if (index2 == 0 || dg_len2 == 0)
1333028296e4SBryan O'Donoghue 				break;
133400a2430fSAndrzej Pietrasiewicz 		} while (ndp_len > 2 * (opts->dgram_item_len * 2));
133500a2430fSAndrzej Pietrasiewicz 	} while (ndp_index);
133600a2430fSAndrzej Pietrasiewicz 
133700a2430fSAndrzej Pietrasiewicz 	VDBG(port->func.config->cdev,
133800a2430fSAndrzej Pietrasiewicz 	     "Parsed NTB with %d frames\n", dgram_counter);
1339427694cfSKrishna Kurapati 
1340427694cfSKrishna Kurapati 	to_process -= block_len;
134176c51146SKrishna Kurapati 
134276c51146SKrishna Kurapati 	/*
134376c51146SKrishna Kurapati 	 * Windows NCM driver avoids USB ZLPs by adding a 1-byte
134476c51146SKrishna Kurapati 	 * zero pad as needed.
134576c51146SKrishna Kurapati 	 */
134676c51146SKrishna Kurapati 	if (to_process == 1 &&
134776c51146SKrishna Kurapati 	    (*(unsigned char *)(ntb_ptr + block_len) == 0x00)) {
134876c51146SKrishna Kurapati 		to_process--;
1349f90ce1e0SKrishna Kurapati 	} else if ((to_process > 0) && (block_len != 0)) {
1350427694cfSKrishna Kurapati 		ntb_ptr = (unsigned char *)(ntb_ptr + block_len);
1351427694cfSKrishna Kurapati 		goto parse_ntb;
1352427694cfSKrishna Kurapati 	}
1353427694cfSKrishna Kurapati 
1354427694cfSKrishna Kurapati 	dev_consume_skb_any(skb);
1355427694cfSKrishna Kurapati 
135600a2430fSAndrzej Pietrasiewicz 	return 0;
135700a2430fSAndrzej Pietrasiewicz err:
135800a2430fSAndrzej Pietrasiewicz 	skb_queue_purge(list);
135900a2430fSAndrzej Pietrasiewicz 	dev_kfree_skb_any(skb);
136000a2430fSAndrzej Pietrasiewicz 	return ret;
136100a2430fSAndrzej Pietrasiewicz }
136200a2430fSAndrzej Pietrasiewicz 
ncm_disable(struct usb_function * f)136300a2430fSAndrzej Pietrasiewicz static void ncm_disable(struct usb_function *f)
136400a2430fSAndrzej Pietrasiewicz {
136500a2430fSAndrzej Pietrasiewicz 	struct f_ncm		*ncm = func_to_ncm(f);
136600a2430fSAndrzej Pietrasiewicz 	struct usb_composite_dev *cdev = f->config->cdev;
136700a2430fSAndrzej Pietrasiewicz 
136800a2430fSAndrzej Pietrasiewicz 	DBG(cdev, "ncm deactivated\n");
136900a2430fSAndrzej Pietrasiewicz 
13706334b8e4SNorihiko Hama 	if (ncm->netdev) {
137100a2430fSAndrzej Pietrasiewicz 		ncm->netdev = NULL;
137200a2430fSAndrzej Pietrasiewicz 		gether_disconnect(&ncm->port);
137300a2430fSAndrzej Pietrasiewicz 	}
137400a2430fSAndrzej Pietrasiewicz 
13756b4012a2SRobert Baldyga 	if (ncm->notify->enabled) {
137600a2430fSAndrzej Pietrasiewicz 		usb_ep_disable(ncm->notify);
137700a2430fSAndrzej Pietrasiewicz 		ncm->notify->desc = NULL;
137800a2430fSAndrzej Pietrasiewicz 	}
137900a2430fSAndrzej Pietrasiewicz }
138000a2430fSAndrzej Pietrasiewicz 
138100a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
138200a2430fSAndrzej Pietrasiewicz 
138300a2430fSAndrzej Pietrasiewicz /*
138400a2430fSAndrzej Pietrasiewicz  * Callbacks let us notify the host about connect/disconnect when the
138500a2430fSAndrzej Pietrasiewicz  * net device is opened or closed.
138600a2430fSAndrzej Pietrasiewicz  *
138700a2430fSAndrzej Pietrasiewicz  * For testing, note that link states on this side include both opened
138800a2430fSAndrzej Pietrasiewicz  * and closed variants of:
138900a2430fSAndrzej Pietrasiewicz  *
139000a2430fSAndrzej Pietrasiewicz  *   - disconnected/unconfigured
139100a2430fSAndrzej Pietrasiewicz  *   - configured but inactive (data alt 0)
139200a2430fSAndrzej Pietrasiewicz  *   - configured and active (data alt 1)
139300a2430fSAndrzej Pietrasiewicz  *
139400a2430fSAndrzej Pietrasiewicz  * Each needs to be tested with unplug, rmmod, SET_CONFIGURATION, and
139500a2430fSAndrzej Pietrasiewicz  * SET_INTERFACE (altsetting).  Remember also that "configured" doesn't
139600a2430fSAndrzej Pietrasiewicz  * imply the host is actually polling the notification endpoint, and
139700a2430fSAndrzej Pietrasiewicz  * likewise that "active" doesn't imply it's actually using the data
139800a2430fSAndrzej Pietrasiewicz  * endpoints for traffic.
139900a2430fSAndrzej Pietrasiewicz  */
140000a2430fSAndrzej Pietrasiewicz 
ncm_open(struct gether * geth)140100a2430fSAndrzej Pietrasiewicz static void ncm_open(struct gether *geth)
140200a2430fSAndrzej Pietrasiewicz {
140300a2430fSAndrzej Pietrasiewicz 	struct f_ncm		*ncm = func_to_ncm(&geth->func);
140400a2430fSAndrzej Pietrasiewicz 
140500a2430fSAndrzej Pietrasiewicz 	DBG(ncm->port.func.config->cdev, "%s\n", __func__);
140600a2430fSAndrzej Pietrasiewicz 
140700a2430fSAndrzej Pietrasiewicz 	spin_lock(&ncm->lock);
140800a2430fSAndrzej Pietrasiewicz 	ncm->is_open = true;
140900a2430fSAndrzej Pietrasiewicz 	ncm_notify(ncm);
141000a2430fSAndrzej Pietrasiewicz 	spin_unlock(&ncm->lock);
141100a2430fSAndrzej Pietrasiewicz }
141200a2430fSAndrzej Pietrasiewicz 
ncm_close(struct gether * geth)141300a2430fSAndrzej Pietrasiewicz static void ncm_close(struct gether *geth)
141400a2430fSAndrzej Pietrasiewicz {
141500a2430fSAndrzej Pietrasiewicz 	struct f_ncm		*ncm = func_to_ncm(&geth->func);
141600a2430fSAndrzej Pietrasiewicz 
141700a2430fSAndrzej Pietrasiewicz 	DBG(ncm->port.func.config->cdev, "%s\n", __func__);
141800a2430fSAndrzej Pietrasiewicz 
141900a2430fSAndrzej Pietrasiewicz 	spin_lock(&ncm->lock);
142000a2430fSAndrzej Pietrasiewicz 	ncm->is_open = false;
142100a2430fSAndrzej Pietrasiewicz 	ncm_notify(ncm);
142200a2430fSAndrzej Pietrasiewicz 	spin_unlock(&ncm->lock);
142300a2430fSAndrzej Pietrasiewicz }
142400a2430fSAndrzej Pietrasiewicz 
142500a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
142600a2430fSAndrzej Pietrasiewicz 
142700a2430fSAndrzej Pietrasiewicz /* ethernet function driver setup/binding */
142800a2430fSAndrzej Pietrasiewicz 
ncm_bind(struct usb_configuration * c,struct usb_function * f)142900a2430fSAndrzej Pietrasiewicz static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
143000a2430fSAndrzej Pietrasiewicz {
143100a2430fSAndrzej Pietrasiewicz 	struct usb_composite_dev *cdev = c->cdev;
143200a2430fSAndrzej Pietrasiewicz 	struct f_ncm		*ncm = func_to_ncm(f);
143300a2430fSAndrzej Pietrasiewicz 	struct usb_string	*us;
1434a04224daSHardik Gajjar 	int			status = 0;
143500a2430fSAndrzej Pietrasiewicz 	struct usb_ep		*ep;
143600a2430fSAndrzej Pietrasiewicz 	struct f_ncm_opts	*ncm_opts;
143700a2430fSAndrzej Pietrasiewicz 
143800a2430fSAndrzej Pietrasiewicz 	if (!can_support_ecm(cdev->gadget))
143900a2430fSAndrzej Pietrasiewicz 		return -EINVAL;
144000a2430fSAndrzej Pietrasiewicz 
144100a2430fSAndrzej Pietrasiewicz 	ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst);
144279340929SRomain Izard 
144379340929SRomain Izard 	if (cdev->use_os_string) {
144479340929SRomain Izard 		f->os_desc_table = kzalloc(sizeof(*f->os_desc_table),
144579340929SRomain Izard 					   GFP_KERNEL);
144679340929SRomain Izard 		if (!f->os_desc_table)
144779340929SRomain Izard 			return -ENOMEM;
144879340929SRomain Izard 		f->os_desc_n = 1;
144979340929SRomain Izard 		f->os_desc_table[0].os_desc = &ncm_opts->ncm_os_desc;
145079340929SRomain Izard 	}
145179340929SRomain Izard 
145200a2430fSAndrzej Pietrasiewicz 	mutex_lock(&ncm_opts->lock);
145300a2430fSAndrzej Pietrasiewicz 	gether_set_gadget(ncm_opts->net, cdev->gadget);
14541900daeeSKrishna Kurapati 	if (!ncm_opts->bound) {
14551900daeeSKrishna Kurapati 		ncm_opts->net->mtu = (ncm_opts->max_segment_size - ETH_HLEN);
145600a2430fSAndrzej Pietrasiewicz 		status = gether_register_netdev(ncm_opts->net);
14571900daeeSKrishna Kurapati 	}
145800a2430fSAndrzej Pietrasiewicz 	mutex_unlock(&ncm_opts->lock);
1459a04224daSHardik Gajjar 
146000a2430fSAndrzej Pietrasiewicz 	if (status)
146179340929SRomain Izard 		goto fail;
1462a04224daSHardik Gajjar 
146300a2430fSAndrzej Pietrasiewicz 	ncm_opts->bound = true;
1464a04224daSHardik Gajjar 
146500a2430fSAndrzej Pietrasiewicz 	us = usb_gstrings_attach(cdev, ncm_strings,
146600a2430fSAndrzej Pietrasiewicz 				 ARRAY_SIZE(ncm_string_defs));
146779340929SRomain Izard 	if (IS_ERR(us)) {
146879340929SRomain Izard 		status = PTR_ERR(us);
146979340929SRomain Izard 		goto fail;
147079340929SRomain Izard 	}
147100a2430fSAndrzej Pietrasiewicz 	ncm_control_intf.iInterface = us[STRING_CTRL_IDX].id;
147200a2430fSAndrzej Pietrasiewicz 	ncm_data_nop_intf.iInterface = us[STRING_DATA_IDX].id;
147300a2430fSAndrzej Pietrasiewicz 	ncm_data_intf.iInterface = us[STRING_DATA_IDX].id;
147400a2430fSAndrzej Pietrasiewicz 	ecm_desc.iMACAddress = us[STRING_MAC_IDX].id;
147500a2430fSAndrzej Pietrasiewicz 	ncm_iad_desc.iFunction = us[STRING_IAD_IDX].id;
147600a2430fSAndrzej Pietrasiewicz 
147700a2430fSAndrzej Pietrasiewicz 	/* allocate instance-specific interface IDs */
147800a2430fSAndrzej Pietrasiewicz 	status = usb_interface_id(c, f);
147900a2430fSAndrzej Pietrasiewicz 	if (status < 0)
148000a2430fSAndrzej Pietrasiewicz 		goto fail;
148100a2430fSAndrzej Pietrasiewicz 	ncm->ctrl_id = status;
148200a2430fSAndrzej Pietrasiewicz 	ncm_iad_desc.bFirstInterface = status;
148300a2430fSAndrzej Pietrasiewicz 
148400a2430fSAndrzej Pietrasiewicz 	ncm_control_intf.bInterfaceNumber = status;
148500a2430fSAndrzej Pietrasiewicz 	ncm_union_desc.bMasterInterface0 = status;
148600a2430fSAndrzej Pietrasiewicz 
148779340929SRomain Izard 	if (cdev->use_os_string)
148879340929SRomain Izard 		f->os_desc_table[0].if_id =
148979340929SRomain Izard 			ncm_iad_desc.bFirstInterface;
149079340929SRomain Izard 
149100a2430fSAndrzej Pietrasiewicz 	status = usb_interface_id(c, f);
149200a2430fSAndrzej Pietrasiewicz 	if (status < 0)
149300a2430fSAndrzej Pietrasiewicz 		goto fail;
149400a2430fSAndrzej Pietrasiewicz 	ncm->data_id = status;
149500a2430fSAndrzej Pietrasiewicz 
149600a2430fSAndrzej Pietrasiewicz 	ncm_data_nop_intf.bInterfaceNumber = status;
149700a2430fSAndrzej Pietrasiewicz 	ncm_data_intf.bInterfaceNumber = status;
149800a2430fSAndrzej Pietrasiewicz 	ncm_union_desc.bSlaveInterface0 = status;
149900a2430fSAndrzej Pietrasiewicz 
15009dc29241SKrishna Kurapati 	ecm_desc.wMaxSegmentSize = cpu_to_le16(ncm_opts->max_segment_size);
15011900daeeSKrishna Kurapati 
150200a2430fSAndrzej Pietrasiewicz 	status = -ENODEV;
150300a2430fSAndrzej Pietrasiewicz 
150400a2430fSAndrzej Pietrasiewicz 	/* allocate instance-specific endpoints */
150500a2430fSAndrzej Pietrasiewicz 	ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_in_desc);
150600a2430fSAndrzej Pietrasiewicz 	if (!ep)
150700a2430fSAndrzej Pietrasiewicz 		goto fail;
150800a2430fSAndrzej Pietrasiewicz 	ncm->port.in_ep = ep;
150900a2430fSAndrzej Pietrasiewicz 
151000a2430fSAndrzej Pietrasiewicz 	ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_out_desc);
151100a2430fSAndrzej Pietrasiewicz 	if (!ep)
151200a2430fSAndrzej Pietrasiewicz 		goto fail;
151300a2430fSAndrzej Pietrasiewicz 	ncm->port.out_ep = ep;
151400a2430fSAndrzej Pietrasiewicz 
151500a2430fSAndrzej Pietrasiewicz 	ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_notify_desc);
151600a2430fSAndrzej Pietrasiewicz 	if (!ep)
151700a2430fSAndrzej Pietrasiewicz 		goto fail;
151800a2430fSAndrzej Pietrasiewicz 	ncm->notify = ep;
151900a2430fSAndrzej Pietrasiewicz 
152000a2430fSAndrzej Pietrasiewicz 	status = -ENOMEM;
152100a2430fSAndrzej Pietrasiewicz 
152200a2430fSAndrzej Pietrasiewicz 	/* allocate notification request and buffer */
152300a2430fSAndrzej Pietrasiewicz 	ncm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
152400a2430fSAndrzej Pietrasiewicz 	if (!ncm->notify_req)
152500a2430fSAndrzej Pietrasiewicz 		goto fail;
152600a2430fSAndrzej Pietrasiewicz 	ncm->notify_req->buf = kmalloc(NCM_STATUS_BYTECOUNT, GFP_KERNEL);
152700a2430fSAndrzej Pietrasiewicz 	if (!ncm->notify_req->buf)
152800a2430fSAndrzej Pietrasiewicz 		goto fail;
152900a2430fSAndrzej Pietrasiewicz 	ncm->notify_req->context = ncm;
153000a2430fSAndrzej Pietrasiewicz 	ncm->notify_req->complete = ncm_notify_complete;
153100a2430fSAndrzej Pietrasiewicz 
153200a2430fSAndrzej Pietrasiewicz 	/*
153300a2430fSAndrzej Pietrasiewicz 	 * support all relevant hardware speeds... we expect that when
153400a2430fSAndrzej Pietrasiewicz 	 * hardware is dual speed, all bulk-capable endpoints work at
153500a2430fSAndrzej Pietrasiewicz 	 * both speeds
153600a2430fSAndrzej Pietrasiewicz 	 */
153700a2430fSAndrzej Pietrasiewicz 	hs_ncm_in_desc.bEndpointAddress = fs_ncm_in_desc.bEndpointAddress;
153800a2430fSAndrzej Pietrasiewicz 	hs_ncm_out_desc.bEndpointAddress = fs_ncm_out_desc.bEndpointAddress;
153900a2430fSAndrzej Pietrasiewicz 	hs_ncm_notify_desc.bEndpointAddress =
154000a2430fSAndrzej Pietrasiewicz 		fs_ncm_notify_desc.bEndpointAddress;
154100a2430fSAndrzej Pietrasiewicz 
154216501138SJussi Kivilinna 	ss_ncm_in_desc.bEndpointAddress = fs_ncm_in_desc.bEndpointAddress;
154316501138SJussi Kivilinna 	ss_ncm_out_desc.bEndpointAddress = fs_ncm_out_desc.bEndpointAddress;
154416501138SJussi Kivilinna 	ss_ncm_notify_desc.bEndpointAddress =
154516501138SJussi Kivilinna 		fs_ncm_notify_desc.bEndpointAddress;
154616501138SJussi Kivilinna 
154700a2430fSAndrzej Pietrasiewicz 	status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function,
15487974ecd7SLorenzo Colitti 			ncm_ss_function, ncm_ss_function);
15498b920f16SPavitrakumar Managutte 	if (status)
15508b920f16SPavitrakumar Managutte 		goto fail;
15518b920f16SPavitrakumar Managutte 
155200a2430fSAndrzej Pietrasiewicz 	/*
155300a2430fSAndrzej Pietrasiewicz 	 * NOTE:  all that is done without knowing or caring about
155400a2430fSAndrzej Pietrasiewicz 	 * the network link ... which is unavailable to this code
155500a2430fSAndrzej Pietrasiewicz 	 * until we're activated via set_alt().
155600a2430fSAndrzej Pietrasiewicz 	 */
155700a2430fSAndrzej Pietrasiewicz 
155800a2430fSAndrzej Pietrasiewicz 	ncm->port.open = ncm_open;
155900a2430fSAndrzej Pietrasiewicz 	ncm->port.close = ncm_close;
156000a2430fSAndrzej Pietrasiewicz 
1561b1a31a5fSThomas Gleixner 	hrtimer_init(&ncm->task_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
156200a2430fSAndrzej Pietrasiewicz 	ncm->task_timer.function = ncm_tx_timeout;
156300a2430fSAndrzej Pietrasiewicz 
1564333ab99eSLinyu Yuan 	DBG(cdev, "CDC Network: IN/%s OUT/%s NOTIFY/%s\n",
156500a2430fSAndrzej Pietrasiewicz 			ncm->port.in_ep->name, ncm->port.out_ep->name,
156600a2430fSAndrzej Pietrasiewicz 			ncm->notify->name);
156700a2430fSAndrzej Pietrasiewicz 	return 0;
156800a2430fSAndrzej Pietrasiewicz 
156900a2430fSAndrzej Pietrasiewicz fail:
157079340929SRomain Izard 	kfree(f->os_desc_table);
157179340929SRomain Izard 	f->os_desc_n = 0;
157279340929SRomain Izard 
157300a2430fSAndrzej Pietrasiewicz 	if (ncm->notify_req) {
157400a2430fSAndrzej Pietrasiewicz 		kfree(ncm->notify_req->buf);
157500a2430fSAndrzej Pietrasiewicz 		usb_ep_free_request(ncm->notify, ncm->notify_req);
157600a2430fSAndrzej Pietrasiewicz 	}
157700a2430fSAndrzej Pietrasiewicz 
157800a2430fSAndrzej Pietrasiewicz 	ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
157900a2430fSAndrzej Pietrasiewicz 
158000a2430fSAndrzej Pietrasiewicz 	return status;
158100a2430fSAndrzej Pietrasiewicz }
158200a2430fSAndrzej Pietrasiewicz 
to_f_ncm_opts(struct config_item * item)158300a2430fSAndrzej Pietrasiewicz static inline struct f_ncm_opts *to_f_ncm_opts(struct config_item *item)
158400a2430fSAndrzej Pietrasiewicz {
158500a2430fSAndrzej Pietrasiewicz 	return container_of(to_config_group(item), struct f_ncm_opts,
158600a2430fSAndrzej Pietrasiewicz 			    func_inst.group);
158700a2430fSAndrzej Pietrasiewicz }
158800a2430fSAndrzej Pietrasiewicz 
158900a2430fSAndrzej Pietrasiewicz /* f_ncm_item_ops */
159000a2430fSAndrzej Pietrasiewicz USB_ETHERNET_CONFIGFS_ITEM(ncm);
159100a2430fSAndrzej Pietrasiewicz 
159200a2430fSAndrzej Pietrasiewicz /* f_ncm_opts_dev_addr */
159300a2430fSAndrzej Pietrasiewicz USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ncm);
159400a2430fSAndrzej Pietrasiewicz 
159500a2430fSAndrzej Pietrasiewicz /* f_ncm_opts_host_addr */
159600a2430fSAndrzej Pietrasiewicz USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ncm);
159700a2430fSAndrzej Pietrasiewicz 
159800a2430fSAndrzej Pietrasiewicz /* f_ncm_opts_qmult */
159900a2430fSAndrzej Pietrasiewicz USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ncm);
160000a2430fSAndrzej Pietrasiewicz 
160100a2430fSAndrzej Pietrasiewicz /* f_ncm_opts_ifname */
160200a2430fSAndrzej Pietrasiewicz USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ncm);
160300a2430fSAndrzej Pietrasiewicz 
ncm_opts_max_segment_size_show(struct config_item * item,char * page)16041900daeeSKrishna Kurapati static ssize_t ncm_opts_max_segment_size_show(struct config_item *item,
16051900daeeSKrishna Kurapati 					      char *page)
16061900daeeSKrishna Kurapati {
16071900daeeSKrishna Kurapati 	struct f_ncm_opts *opts = to_f_ncm_opts(item);
16081900daeeSKrishna Kurapati 	u16 segment_size;
16091900daeeSKrishna Kurapati 
16101900daeeSKrishna Kurapati 	mutex_lock(&opts->lock);
16111900daeeSKrishna Kurapati 	segment_size = opts->max_segment_size;
16121900daeeSKrishna Kurapati 	mutex_unlock(&opts->lock);
16131900daeeSKrishna Kurapati 
16141900daeeSKrishna Kurapati 	return sysfs_emit(page, "%u\n", segment_size);
16151900daeeSKrishna Kurapati }
16161900daeeSKrishna Kurapati 
ncm_opts_max_segment_size_store(struct config_item * item,const char * page,size_t len)16171900daeeSKrishna Kurapati static ssize_t ncm_opts_max_segment_size_store(struct config_item *item,
16181900daeeSKrishna Kurapati 					       const char *page, size_t len)
16191900daeeSKrishna Kurapati {
16201900daeeSKrishna Kurapati 	struct f_ncm_opts *opts = to_f_ncm_opts(item);
16211900daeeSKrishna Kurapati 	u16 segment_size;
16221900daeeSKrishna Kurapati 	int ret;
16231900daeeSKrishna Kurapati 
16241900daeeSKrishna Kurapati 	mutex_lock(&opts->lock);
16251900daeeSKrishna Kurapati 	if (opts->refcnt) {
16261900daeeSKrishna Kurapati 		ret = -EBUSY;
16271900daeeSKrishna Kurapati 		goto out;
16281900daeeSKrishna Kurapati 	}
16291900daeeSKrishna Kurapati 
16301900daeeSKrishna Kurapati 	ret = kstrtou16(page, 0, &segment_size);
16311900daeeSKrishna Kurapati 	if (ret)
16321900daeeSKrishna Kurapati 		goto out;
16331900daeeSKrishna Kurapati 
16341900daeeSKrishna Kurapati 	if (segment_size > MAX_DATAGRAM_SIZE) {
16351900daeeSKrishna Kurapati 		ret = -EINVAL;
16361900daeeSKrishna Kurapati 		goto out;
16371900daeeSKrishna Kurapati 	}
16381900daeeSKrishna Kurapati 
16391900daeeSKrishna Kurapati 	opts->max_segment_size = segment_size;
16401900daeeSKrishna Kurapati 	ret = len;
16411900daeeSKrishna Kurapati out:
16421900daeeSKrishna Kurapati 	mutex_unlock(&opts->lock);
16431900daeeSKrishna Kurapati 	return ret;
16441900daeeSKrishna Kurapati }
16451900daeeSKrishna Kurapati 
16461900daeeSKrishna Kurapati CONFIGFS_ATTR(ncm_opts_, max_segment_size);
16471900daeeSKrishna Kurapati 
164800a2430fSAndrzej Pietrasiewicz static struct configfs_attribute *ncm_attrs[] = {
1649f9a63da3SChristoph Hellwig 	&ncm_opts_attr_dev_addr,
1650f9a63da3SChristoph Hellwig 	&ncm_opts_attr_host_addr,
1651f9a63da3SChristoph Hellwig 	&ncm_opts_attr_qmult,
1652f9a63da3SChristoph Hellwig 	&ncm_opts_attr_ifname,
16531900daeeSKrishna Kurapati 	&ncm_opts_attr_max_segment_size,
165400a2430fSAndrzej Pietrasiewicz 	NULL,
165500a2430fSAndrzej Pietrasiewicz };
165600a2430fSAndrzej Pietrasiewicz 
165797363902SBhumika Goyal static const struct config_item_type ncm_func_type = {
165800a2430fSAndrzej Pietrasiewicz 	.ct_item_ops	= &ncm_item_ops,
165900a2430fSAndrzej Pietrasiewicz 	.ct_attrs	= ncm_attrs,
166000a2430fSAndrzej Pietrasiewicz 	.ct_owner	= THIS_MODULE,
166100a2430fSAndrzej Pietrasiewicz };
166200a2430fSAndrzej Pietrasiewicz 
ncm_free_inst(struct usb_function_instance * f)166300a2430fSAndrzej Pietrasiewicz static void ncm_free_inst(struct usb_function_instance *f)
166400a2430fSAndrzej Pietrasiewicz {
166500a2430fSAndrzej Pietrasiewicz 	struct f_ncm_opts *opts;
166600a2430fSAndrzej Pietrasiewicz 
166700a2430fSAndrzej Pietrasiewicz 	opts = container_of(f, struct f_ncm_opts, func_inst);
166800a2430fSAndrzej Pietrasiewicz 	if (opts->bound)
166900a2430fSAndrzej Pietrasiewicz 		gether_cleanup(netdev_priv(opts->net));
167000a2430fSAndrzej Pietrasiewicz 	else
167100a2430fSAndrzej Pietrasiewicz 		free_netdev(opts->net);
167279340929SRomain Izard 	kfree(opts->ncm_interf_group);
167300a2430fSAndrzej Pietrasiewicz 	kfree(opts);
167400a2430fSAndrzej Pietrasiewicz }
167500a2430fSAndrzej Pietrasiewicz 
ncm_alloc_inst(void)167600a2430fSAndrzej Pietrasiewicz static struct usb_function_instance *ncm_alloc_inst(void)
167700a2430fSAndrzej Pietrasiewicz {
167800a2430fSAndrzej Pietrasiewicz 	struct f_ncm_opts *opts;
167979340929SRomain Izard 	struct usb_os_desc *descs[1];
168079340929SRomain Izard 	char *names[1];
168179340929SRomain Izard 	struct config_group *ncm_interf_group;
168200a2430fSAndrzej Pietrasiewicz 
168300a2430fSAndrzej Pietrasiewicz 	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
168400a2430fSAndrzej Pietrasiewicz 	if (!opts)
168500a2430fSAndrzej Pietrasiewicz 		return ERR_PTR(-ENOMEM);
168679340929SRomain Izard 	opts->ncm_os_desc.ext_compat_id = opts->ncm_ext_compat_id;
168779340929SRomain Izard 
168800a2430fSAndrzej Pietrasiewicz 	mutex_init(&opts->lock);
168900a2430fSAndrzej Pietrasiewicz 	opts->func_inst.free_func_inst = ncm_free_inst;
169000a2430fSAndrzej Pietrasiewicz 	opts->net = gether_setup_default();
169100a2430fSAndrzej Pietrasiewicz 	if (IS_ERR(opts->net)) {
169200a2430fSAndrzej Pietrasiewicz 		struct net_device *net = opts->net;
169300a2430fSAndrzej Pietrasiewicz 		kfree(opts);
169400a2430fSAndrzej Pietrasiewicz 		return ERR_CAST(net);
169500a2430fSAndrzej Pietrasiewicz 	}
16969dc29241SKrishna Kurapati 	opts->max_segment_size = ETH_FRAME_LEN;
169779340929SRomain Izard 	INIT_LIST_HEAD(&opts->ncm_os_desc.ext_prop);
169879340929SRomain Izard 
169979340929SRomain Izard 	descs[0] = &opts->ncm_os_desc;
170079340929SRomain Izard 	names[0] = "ncm";
170100a2430fSAndrzej Pietrasiewicz 
170200a2430fSAndrzej Pietrasiewicz 	config_group_init_type_name(&opts->func_inst.group, "", &ncm_func_type);
170379340929SRomain Izard 	ncm_interf_group =
170479340929SRomain Izard 		usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs,
170579340929SRomain Izard 					       names, THIS_MODULE);
170679340929SRomain Izard 	if (IS_ERR(ncm_interf_group)) {
170779340929SRomain Izard 		ncm_free_inst(&opts->func_inst);
170879340929SRomain Izard 		return ERR_CAST(ncm_interf_group);
170979340929SRomain Izard 	}
171079340929SRomain Izard 	opts->ncm_interf_group = ncm_interf_group;
171100a2430fSAndrzej Pietrasiewicz 
171200a2430fSAndrzej Pietrasiewicz 	return &opts->func_inst;
171300a2430fSAndrzej Pietrasiewicz }
171400a2430fSAndrzej Pietrasiewicz 
ncm_free(struct usb_function * f)171500a2430fSAndrzej Pietrasiewicz static void ncm_free(struct usb_function *f)
171600a2430fSAndrzej Pietrasiewicz {
171700a2430fSAndrzej Pietrasiewicz 	struct f_ncm *ncm;
171800a2430fSAndrzej Pietrasiewicz 	struct f_ncm_opts *opts;
171900a2430fSAndrzej Pietrasiewicz 
172000a2430fSAndrzej Pietrasiewicz 	ncm = func_to_ncm(f);
172100a2430fSAndrzej Pietrasiewicz 	opts = container_of(f->fi, struct f_ncm_opts, func_inst);
172200a2430fSAndrzej Pietrasiewicz 	kfree(ncm);
172300a2430fSAndrzej Pietrasiewicz 	mutex_lock(&opts->lock);
172400a2430fSAndrzej Pietrasiewicz 	opts->refcnt--;
172500a2430fSAndrzej Pietrasiewicz 	mutex_unlock(&opts->lock);
172600a2430fSAndrzej Pietrasiewicz }
172700a2430fSAndrzej Pietrasiewicz 
ncm_unbind(struct usb_configuration * c,struct usb_function * f)172800a2430fSAndrzej Pietrasiewicz static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
172900a2430fSAndrzej Pietrasiewicz {
173000a2430fSAndrzej Pietrasiewicz 	struct f_ncm *ncm = func_to_ncm(f);
173100a2430fSAndrzej Pietrasiewicz 
173200a2430fSAndrzej Pietrasiewicz 	DBG(c->cdev, "ncm unbind\n");
173300a2430fSAndrzej Pietrasiewicz 
173400a2430fSAndrzej Pietrasiewicz 	hrtimer_cancel(&ncm->task_timer);
173500a2430fSAndrzej Pietrasiewicz 
173679340929SRomain Izard 	kfree(f->os_desc_table);
173779340929SRomain Izard 	f->os_desc_n = 0;
173879340929SRomain Izard 
173900a2430fSAndrzej Pietrasiewicz 	ncm_string_defs[0].id = 0;
174000a2430fSAndrzej Pietrasiewicz 	usb_free_all_descriptors(f);
174100a2430fSAndrzej Pietrasiewicz 
17425b24c28cSBryan O'Donoghue 	if (atomic_read(&ncm->notify_count)) {
17435b24c28cSBryan O'Donoghue 		usb_ep_dequeue(ncm->notify, ncm->notify_req);
17445b24c28cSBryan O'Donoghue 		atomic_set(&ncm->notify_count, 0);
17455b24c28cSBryan O'Donoghue 	}
17465b24c28cSBryan O'Donoghue 
174700a2430fSAndrzej Pietrasiewicz 	kfree(ncm->notify_req->buf);
174800a2430fSAndrzej Pietrasiewicz 	usb_ep_free_request(ncm->notify, ncm->notify_req);
174900a2430fSAndrzej Pietrasiewicz }
175000a2430fSAndrzej Pietrasiewicz 
ncm_alloc(struct usb_function_instance * fi)175100a2430fSAndrzej Pietrasiewicz static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
175200a2430fSAndrzej Pietrasiewicz {
175300a2430fSAndrzej Pietrasiewicz 	struct f_ncm		*ncm;
175400a2430fSAndrzej Pietrasiewicz 	struct f_ncm_opts	*opts;
175500a2430fSAndrzej Pietrasiewicz 	int status;
175600a2430fSAndrzej Pietrasiewicz 
175700a2430fSAndrzej Pietrasiewicz 	/* allocate and initialize one new instance */
175800a2430fSAndrzej Pietrasiewicz 	ncm = kzalloc(sizeof(*ncm), GFP_KERNEL);
175900a2430fSAndrzej Pietrasiewicz 	if (!ncm)
176000a2430fSAndrzej Pietrasiewicz 		return ERR_PTR(-ENOMEM);
176100a2430fSAndrzej Pietrasiewicz 
176200a2430fSAndrzej Pietrasiewicz 	opts = container_of(fi, struct f_ncm_opts, func_inst);
176300a2430fSAndrzej Pietrasiewicz 	mutex_lock(&opts->lock);
176400a2430fSAndrzej Pietrasiewicz 	opts->refcnt++;
176500a2430fSAndrzej Pietrasiewicz 
176600a2430fSAndrzej Pietrasiewicz 	/* export host's Ethernet address in CDC format */
176700a2430fSAndrzej Pietrasiewicz 	status = gether_get_host_addr_cdc(opts->net, ncm->ethaddr,
176800a2430fSAndrzej Pietrasiewicz 				      sizeof(ncm->ethaddr));
176900a2430fSAndrzej Pietrasiewicz 	if (status < 12) { /* strlen("01234567890a") */
177000a2430fSAndrzej Pietrasiewicz 		kfree(ncm);
177100a2430fSAndrzej Pietrasiewicz 		mutex_unlock(&opts->lock);
177200a2430fSAndrzej Pietrasiewicz 		return ERR_PTR(-EINVAL);
177300a2430fSAndrzej Pietrasiewicz 	}
177400a2430fSAndrzej Pietrasiewicz 	ncm_string_defs[STRING_MAC_IDX].s = ncm->ethaddr;
177500a2430fSAndrzej Pietrasiewicz 
177600a2430fSAndrzej Pietrasiewicz 	spin_lock_init(&ncm->lock);
177700a2430fSAndrzej Pietrasiewicz 	ncm_reset_values(ncm);
177800a2430fSAndrzej Pietrasiewicz 	ncm->port.ioport = netdev_priv(opts->net);
177900a2430fSAndrzej Pietrasiewicz 	mutex_unlock(&opts->lock);
178000a2430fSAndrzej Pietrasiewicz 	ncm->port.is_fixed = true;
178100a2430fSAndrzej Pietrasiewicz 	ncm->port.supports_multi_frame = true;
178200a2430fSAndrzej Pietrasiewicz 
178300a2430fSAndrzej Pietrasiewicz 	ncm->port.func.name = "cdc_network";
178400a2430fSAndrzej Pietrasiewicz 	/* descriptors are per-instance copies */
178500a2430fSAndrzej Pietrasiewicz 	ncm->port.func.bind = ncm_bind;
178600a2430fSAndrzej Pietrasiewicz 	ncm->port.func.unbind = ncm_unbind;
178700a2430fSAndrzej Pietrasiewicz 	ncm->port.func.set_alt = ncm_set_alt;
178800a2430fSAndrzej Pietrasiewicz 	ncm->port.func.get_alt = ncm_get_alt;
178900a2430fSAndrzej Pietrasiewicz 	ncm->port.func.setup = ncm_setup;
179000a2430fSAndrzej Pietrasiewicz 	ncm->port.func.disable = ncm_disable;
179100a2430fSAndrzej Pietrasiewicz 	ncm->port.func.free_func = ncm_free;
179200a2430fSAndrzej Pietrasiewicz 
179300a2430fSAndrzej Pietrasiewicz 	ncm->port.wrap = ncm_wrap_ntb;
179400a2430fSAndrzej Pietrasiewicz 	ncm->port.unwrap = ncm_unwrap_ntb;
179500a2430fSAndrzej Pietrasiewicz 
179600a2430fSAndrzej Pietrasiewicz 	return &ncm->port.func;
179700a2430fSAndrzej Pietrasiewicz }
179800a2430fSAndrzej Pietrasiewicz 
179900a2430fSAndrzej Pietrasiewicz DECLARE_USB_FUNCTION_INIT(ncm, ncm_alloc_inst, ncm_alloc);
1800*1cb9ba5eSJeff Johnson MODULE_DESCRIPTION("USB CDC Network (NCM) link function driver");
180100a2430fSAndrzej Pietrasiewicz MODULE_LICENSE("GPL");
180200a2430fSAndrzej Pietrasiewicz MODULE_AUTHOR("Yauheni Kaliuta");
1803