xref: /linux/drivers/usb/gadget/function/f_ncm.c (revision 4df864c1d9afb46e2461a9f808d9f11a42d31bad)
100a2430fSAndrzej Pietrasiewicz /*
200a2430fSAndrzej Pietrasiewicz  * f_ncm.c -- USB CDC Network (NCM) link function driver
300a2430fSAndrzej Pietrasiewicz  *
400a2430fSAndrzej Pietrasiewicz  * Copyright (C) 2010 Nokia Corporation
500a2430fSAndrzej Pietrasiewicz  * Contact: Yauheni Kaliuta <yauheni.kaliuta@nokia.com>
600a2430fSAndrzej Pietrasiewicz  *
700a2430fSAndrzej Pietrasiewicz  * The driver borrows from f_ecm.c which is:
800a2430fSAndrzej Pietrasiewicz  *
900a2430fSAndrzej Pietrasiewicz  * Copyright (C) 2003-2005,2008 David Brownell
1000a2430fSAndrzej Pietrasiewicz  * Copyright (C) 2008 Nokia Corporation
1100a2430fSAndrzej Pietrasiewicz  *
1200a2430fSAndrzej Pietrasiewicz  * This program is free software; you can redistribute it and/or modify
1300a2430fSAndrzej Pietrasiewicz  * it under the terms of the GNU General Public License as published by
1400a2430fSAndrzej Pietrasiewicz  * the Free Software Foundation; either version 2 of the License, or
1500a2430fSAndrzej Pietrasiewicz  * (at your option) any later version.
1600a2430fSAndrzej Pietrasiewicz  */
1700a2430fSAndrzej Pietrasiewicz 
1800a2430fSAndrzej Pietrasiewicz #include <linux/kernel.h>
19282ccf6eSFlorian Westphal #include <linux/interrupt.h>
2000a2430fSAndrzej Pietrasiewicz #include <linux/module.h>
2100a2430fSAndrzej Pietrasiewicz #include <linux/device.h>
2200a2430fSAndrzej Pietrasiewicz #include <linux/etherdevice.h>
2300a2430fSAndrzej Pietrasiewicz #include <linux/crc32.h>
2400a2430fSAndrzej Pietrasiewicz 
2500a2430fSAndrzej Pietrasiewicz #include <linux/usb/cdc.h>
2600a2430fSAndrzej Pietrasiewicz 
2700a2430fSAndrzej Pietrasiewicz #include "u_ether.h"
2800a2430fSAndrzej Pietrasiewicz #include "u_ether_configfs.h"
2900a2430fSAndrzej Pietrasiewicz #include "u_ncm.h"
3000a2430fSAndrzej Pietrasiewicz 
3100a2430fSAndrzej Pietrasiewicz /*
3200a2430fSAndrzej Pietrasiewicz  * This function is a "CDC Network Control Model" (CDC NCM) Ethernet link.
3300a2430fSAndrzej Pietrasiewicz  * NCM is intended to be used with high-speed network attachments.
3400a2430fSAndrzej Pietrasiewicz  *
3500a2430fSAndrzej Pietrasiewicz  * Note that NCM requires the use of "alternate settings" for its data
3600a2430fSAndrzej Pietrasiewicz  * interface.  This means that the set_alt() method has real work to do,
3700a2430fSAndrzej Pietrasiewicz  * and also means that a get_alt() method is required.
3800a2430fSAndrzej Pietrasiewicz  */
3900a2430fSAndrzej Pietrasiewicz 
4000a2430fSAndrzej Pietrasiewicz /* to trigger crc/non-crc ndp signature */
4100a2430fSAndrzej Pietrasiewicz 
4200a2430fSAndrzej Pietrasiewicz #define NCM_NDP_HDR_CRC_MASK	0x01000000
4300a2430fSAndrzej Pietrasiewicz #define NCM_NDP_HDR_CRC		0x01000000
4400a2430fSAndrzej Pietrasiewicz #define NCM_NDP_HDR_NOCRC	0x00000000
4500a2430fSAndrzej Pietrasiewicz 
4600a2430fSAndrzej Pietrasiewicz enum ncm_notify_state {
4700a2430fSAndrzej Pietrasiewicz 	NCM_NOTIFY_NONE,		/* don't notify */
4800a2430fSAndrzej Pietrasiewicz 	NCM_NOTIFY_CONNECT,		/* issue CONNECT next */
4900a2430fSAndrzej Pietrasiewicz 	NCM_NOTIFY_SPEED,		/* issue SPEED_CHANGE next */
5000a2430fSAndrzej Pietrasiewicz };
5100a2430fSAndrzej Pietrasiewicz 
5200a2430fSAndrzej Pietrasiewicz struct f_ncm {
5300a2430fSAndrzej Pietrasiewicz 	struct gether			port;
5400a2430fSAndrzej Pietrasiewicz 	u8				ctrl_id, data_id;
5500a2430fSAndrzej Pietrasiewicz 
5600a2430fSAndrzej Pietrasiewicz 	char				ethaddr[14];
5700a2430fSAndrzej Pietrasiewicz 
5800a2430fSAndrzej Pietrasiewicz 	struct usb_ep			*notify;
5900a2430fSAndrzej Pietrasiewicz 	struct usb_request		*notify_req;
6000a2430fSAndrzej Pietrasiewicz 	u8				notify_state;
6100a2430fSAndrzej Pietrasiewicz 	bool				is_open;
6200a2430fSAndrzej Pietrasiewicz 
6300a2430fSAndrzej Pietrasiewicz 	const struct ndp_parser_opts	*parser_opts;
6400a2430fSAndrzej Pietrasiewicz 	bool				is_crc;
6500a2430fSAndrzej Pietrasiewicz 	u32				ndp_sign;
6600a2430fSAndrzej Pietrasiewicz 
6700a2430fSAndrzej Pietrasiewicz 	/*
6800a2430fSAndrzej Pietrasiewicz 	 * for notification, it is accessed from both
6900a2430fSAndrzej Pietrasiewicz 	 * callback and ethernet open/close
7000a2430fSAndrzej Pietrasiewicz 	 */
7100a2430fSAndrzej Pietrasiewicz 	spinlock_t			lock;
7200a2430fSAndrzej Pietrasiewicz 
7300a2430fSAndrzej Pietrasiewicz 	struct net_device		*netdev;
7400a2430fSAndrzej Pietrasiewicz 
7500a2430fSAndrzej Pietrasiewicz 	/* For multi-frame NDP TX */
7600a2430fSAndrzej Pietrasiewicz 	struct sk_buff			*skb_tx_data;
7700a2430fSAndrzej Pietrasiewicz 	struct sk_buff			*skb_tx_ndp;
7800a2430fSAndrzej Pietrasiewicz 	u16				ndp_dgram_count;
7900a2430fSAndrzej Pietrasiewicz 	bool				timer_force_tx;
8000a2430fSAndrzej Pietrasiewicz 	struct tasklet_struct		tx_tasklet;
8100a2430fSAndrzej Pietrasiewicz 	struct hrtimer			task_timer;
8200a2430fSAndrzej Pietrasiewicz 
8300a2430fSAndrzej Pietrasiewicz 	bool				timer_stopping;
8400a2430fSAndrzej Pietrasiewicz };
8500a2430fSAndrzej Pietrasiewicz 
8600a2430fSAndrzej Pietrasiewicz static inline struct f_ncm *func_to_ncm(struct usb_function *f)
8700a2430fSAndrzej Pietrasiewicz {
8800a2430fSAndrzej Pietrasiewicz 	return container_of(f, struct f_ncm, port.func);
8900a2430fSAndrzej Pietrasiewicz }
9000a2430fSAndrzej Pietrasiewicz 
9100a2430fSAndrzej Pietrasiewicz /* peak (theoretical) bulk transfer rate in bits-per-second */
9200a2430fSAndrzej Pietrasiewicz static inline unsigned ncm_bitrate(struct usb_gadget *g)
9300a2430fSAndrzej Pietrasiewicz {
9416501138SJussi Kivilinna 	if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
9516501138SJussi Kivilinna 		return 13 * 1024 * 8 * 1000 * 8;
9616501138SJussi Kivilinna 	else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
9700a2430fSAndrzej Pietrasiewicz 		return 13 * 512 * 8 * 1000 * 8;
9800a2430fSAndrzej Pietrasiewicz 	else
9900a2430fSAndrzej Pietrasiewicz 		return 19 *  64 * 1 * 1000 * 8;
10000a2430fSAndrzej Pietrasiewicz }
10100a2430fSAndrzej Pietrasiewicz 
10200a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
10300a2430fSAndrzej Pietrasiewicz 
10400a2430fSAndrzej Pietrasiewicz /*
10500a2430fSAndrzej Pietrasiewicz  * We cannot group frames so use just the minimal size which ok to put
10600a2430fSAndrzej Pietrasiewicz  * one max-size ethernet frame.
10700a2430fSAndrzej Pietrasiewicz  * If the host can group frames, allow it to do that, 16K is selected,
10800a2430fSAndrzej Pietrasiewicz  * because it's used by default by the current linux host driver
10900a2430fSAndrzej Pietrasiewicz  */
11000a2430fSAndrzej Pietrasiewicz #define NTB_DEFAULT_IN_SIZE	16384
11100a2430fSAndrzej Pietrasiewicz #define NTB_OUT_SIZE		16384
11200a2430fSAndrzej Pietrasiewicz 
11300a2430fSAndrzej Pietrasiewicz /* Allocation for storing the NDP, 32 should suffice for a
11400a2430fSAndrzej Pietrasiewicz  * 16k packet. This allows a maximum of 32 * 507 Byte packets to
11500a2430fSAndrzej Pietrasiewicz  * be transmitted in a single 16kB skb, though when sending full size
11600a2430fSAndrzej Pietrasiewicz  * packets this limit will be plenty.
11700a2430fSAndrzej Pietrasiewicz  * Smaller packets are not likely to be trying to maximize the
11800a2430fSAndrzej Pietrasiewicz  * throughput and will be mstly sending smaller infrequent frames.
11900a2430fSAndrzej Pietrasiewicz  */
12000a2430fSAndrzej Pietrasiewicz #define TX_MAX_NUM_DPE		32
12100a2430fSAndrzej Pietrasiewicz 
12200a2430fSAndrzej Pietrasiewicz /* Delay for the transmit to wait before sending an unfilled NTB frame. */
12300a2430fSAndrzej Pietrasiewicz #define TX_TIMEOUT_NSECS	300000
12400a2430fSAndrzej Pietrasiewicz 
12500a2430fSAndrzej Pietrasiewicz #define FORMATS_SUPPORTED	(USB_CDC_NCM_NTB16_SUPPORTED |	\
12600a2430fSAndrzej Pietrasiewicz 				 USB_CDC_NCM_NTB32_SUPPORTED)
12700a2430fSAndrzej Pietrasiewicz 
12800a2430fSAndrzej Pietrasiewicz static struct usb_cdc_ncm_ntb_parameters ntb_parameters = {
12900a2430fSAndrzej Pietrasiewicz 	.wLength = cpu_to_le16(sizeof(ntb_parameters)),
13000a2430fSAndrzej Pietrasiewicz 	.bmNtbFormatsSupported = cpu_to_le16(FORMATS_SUPPORTED),
13100a2430fSAndrzej Pietrasiewicz 	.dwNtbInMaxSize = cpu_to_le32(NTB_DEFAULT_IN_SIZE),
13200a2430fSAndrzej Pietrasiewicz 	.wNdpInDivisor = cpu_to_le16(4),
13300a2430fSAndrzej Pietrasiewicz 	.wNdpInPayloadRemainder = cpu_to_le16(0),
13400a2430fSAndrzej Pietrasiewicz 	.wNdpInAlignment = cpu_to_le16(4),
13500a2430fSAndrzej Pietrasiewicz 
13600a2430fSAndrzej Pietrasiewicz 	.dwNtbOutMaxSize = cpu_to_le32(NTB_OUT_SIZE),
13700a2430fSAndrzej Pietrasiewicz 	.wNdpOutDivisor = cpu_to_le16(4),
13800a2430fSAndrzej Pietrasiewicz 	.wNdpOutPayloadRemainder = cpu_to_le16(0),
13900a2430fSAndrzej Pietrasiewicz 	.wNdpOutAlignment = cpu_to_le16(4),
14000a2430fSAndrzej Pietrasiewicz };
14100a2430fSAndrzej Pietrasiewicz 
14200a2430fSAndrzej Pietrasiewicz /*
14300a2430fSAndrzej Pietrasiewicz  * Use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one
14400a2430fSAndrzej Pietrasiewicz  * packet, to simplify cancellation; and a big transfer interval, to
14500a2430fSAndrzej Pietrasiewicz  * waste less bandwidth.
14600a2430fSAndrzej Pietrasiewicz  */
14700a2430fSAndrzej Pietrasiewicz 
14800a2430fSAndrzej Pietrasiewicz #define NCM_STATUS_INTERVAL_MS		32
14900a2430fSAndrzej Pietrasiewicz #define NCM_STATUS_BYTECOUNT		16	/* 8 byte header + data */
15000a2430fSAndrzej Pietrasiewicz 
15100a2430fSAndrzej Pietrasiewicz static struct usb_interface_assoc_descriptor ncm_iad_desc = {
15200a2430fSAndrzej Pietrasiewicz 	.bLength =		sizeof ncm_iad_desc,
15300a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_INTERFACE_ASSOCIATION,
15400a2430fSAndrzej Pietrasiewicz 
15500a2430fSAndrzej Pietrasiewicz 	/* .bFirstInterface =	DYNAMIC, */
15600a2430fSAndrzej Pietrasiewicz 	.bInterfaceCount =	2,	/* control + data */
15700a2430fSAndrzej Pietrasiewicz 	.bFunctionClass =	USB_CLASS_COMM,
15800a2430fSAndrzej Pietrasiewicz 	.bFunctionSubClass =	USB_CDC_SUBCLASS_NCM,
15900a2430fSAndrzej Pietrasiewicz 	.bFunctionProtocol =	USB_CDC_PROTO_NONE,
16000a2430fSAndrzej Pietrasiewicz 	/* .iFunction =		DYNAMIC */
16100a2430fSAndrzej Pietrasiewicz };
16200a2430fSAndrzej Pietrasiewicz 
16300a2430fSAndrzej Pietrasiewicz /* interface descriptor: */
16400a2430fSAndrzej Pietrasiewicz 
16500a2430fSAndrzej Pietrasiewicz static struct usb_interface_descriptor ncm_control_intf = {
16600a2430fSAndrzej Pietrasiewicz 	.bLength =		sizeof ncm_control_intf,
16700a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_INTERFACE,
16800a2430fSAndrzej Pietrasiewicz 
16900a2430fSAndrzej Pietrasiewicz 	/* .bInterfaceNumber = DYNAMIC */
17000a2430fSAndrzej Pietrasiewicz 	.bNumEndpoints =	1,
17100a2430fSAndrzej Pietrasiewicz 	.bInterfaceClass =	USB_CLASS_COMM,
17200a2430fSAndrzej Pietrasiewicz 	.bInterfaceSubClass =	USB_CDC_SUBCLASS_NCM,
17300a2430fSAndrzej Pietrasiewicz 	.bInterfaceProtocol =	USB_CDC_PROTO_NONE,
17400a2430fSAndrzej Pietrasiewicz 	/* .iInterface = DYNAMIC */
17500a2430fSAndrzej Pietrasiewicz };
17600a2430fSAndrzej Pietrasiewicz 
17700a2430fSAndrzej Pietrasiewicz static struct usb_cdc_header_desc ncm_header_desc = {
17800a2430fSAndrzej Pietrasiewicz 	.bLength =		sizeof ncm_header_desc,
17900a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_CS_INTERFACE,
18000a2430fSAndrzej Pietrasiewicz 	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
18100a2430fSAndrzej Pietrasiewicz 
18200a2430fSAndrzej Pietrasiewicz 	.bcdCDC =		cpu_to_le16(0x0110),
18300a2430fSAndrzej Pietrasiewicz };
18400a2430fSAndrzej Pietrasiewicz 
18500a2430fSAndrzej Pietrasiewicz static struct usb_cdc_union_desc ncm_union_desc = {
18600a2430fSAndrzej Pietrasiewicz 	.bLength =		sizeof(ncm_union_desc),
18700a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_CS_INTERFACE,
18800a2430fSAndrzej Pietrasiewicz 	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
18900a2430fSAndrzej Pietrasiewicz 	/* .bMasterInterface0 =	DYNAMIC */
19000a2430fSAndrzej Pietrasiewicz 	/* .bSlaveInterface0 =	DYNAMIC */
19100a2430fSAndrzej Pietrasiewicz };
19200a2430fSAndrzej Pietrasiewicz 
19300a2430fSAndrzej Pietrasiewicz static struct usb_cdc_ether_desc ecm_desc = {
19400a2430fSAndrzej Pietrasiewicz 	.bLength =		sizeof ecm_desc,
19500a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_CS_INTERFACE,
19600a2430fSAndrzej Pietrasiewicz 	.bDescriptorSubType =	USB_CDC_ETHERNET_TYPE,
19700a2430fSAndrzej Pietrasiewicz 
19800a2430fSAndrzej Pietrasiewicz 	/* this descriptor actually adds value, surprise! */
19900a2430fSAndrzej Pietrasiewicz 	/* .iMACAddress = DYNAMIC */
20000a2430fSAndrzej Pietrasiewicz 	.bmEthernetStatistics =	cpu_to_le32(0), /* no statistics */
20100a2430fSAndrzej Pietrasiewicz 	.wMaxSegmentSize =	cpu_to_le16(ETH_FRAME_LEN),
20200a2430fSAndrzej Pietrasiewicz 	.wNumberMCFilters =	cpu_to_le16(0),
20300a2430fSAndrzej Pietrasiewicz 	.bNumberPowerFilters =	0,
20400a2430fSAndrzej Pietrasiewicz };
20500a2430fSAndrzej Pietrasiewicz 
20600a2430fSAndrzej Pietrasiewicz #define NCAPS	(USB_CDC_NCM_NCAP_ETH_FILTER | USB_CDC_NCM_NCAP_CRC_MODE)
20700a2430fSAndrzej Pietrasiewicz 
20800a2430fSAndrzej Pietrasiewicz static struct usb_cdc_ncm_desc ncm_desc = {
20900a2430fSAndrzej Pietrasiewicz 	.bLength =		sizeof ncm_desc,
21000a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_CS_INTERFACE,
21100a2430fSAndrzej Pietrasiewicz 	.bDescriptorSubType =	USB_CDC_NCM_TYPE,
21200a2430fSAndrzej Pietrasiewicz 
21300a2430fSAndrzej Pietrasiewicz 	.bcdNcmVersion =	cpu_to_le16(0x0100),
21400a2430fSAndrzej Pietrasiewicz 	/* can process SetEthernetPacketFilter */
21500a2430fSAndrzej Pietrasiewicz 	.bmNetworkCapabilities = NCAPS,
21600a2430fSAndrzej Pietrasiewicz };
21700a2430fSAndrzej Pietrasiewicz 
21800a2430fSAndrzej Pietrasiewicz /* the default data interface has no endpoints ... */
21900a2430fSAndrzej Pietrasiewicz 
22000a2430fSAndrzej Pietrasiewicz static struct usb_interface_descriptor ncm_data_nop_intf = {
22100a2430fSAndrzej Pietrasiewicz 	.bLength =		sizeof ncm_data_nop_intf,
22200a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_INTERFACE,
22300a2430fSAndrzej Pietrasiewicz 
22400a2430fSAndrzej Pietrasiewicz 	.bInterfaceNumber =	1,
22500a2430fSAndrzej Pietrasiewicz 	.bAlternateSetting =	0,
22600a2430fSAndrzej Pietrasiewicz 	.bNumEndpoints =	0,
22700a2430fSAndrzej Pietrasiewicz 	.bInterfaceClass =	USB_CLASS_CDC_DATA,
22800a2430fSAndrzej Pietrasiewicz 	.bInterfaceSubClass =	0,
22900a2430fSAndrzej Pietrasiewicz 	.bInterfaceProtocol =	USB_CDC_NCM_PROTO_NTB,
23000a2430fSAndrzej Pietrasiewicz 	/* .iInterface = DYNAMIC */
23100a2430fSAndrzej Pietrasiewicz };
23200a2430fSAndrzej Pietrasiewicz 
23300a2430fSAndrzej Pietrasiewicz /* ... but the "real" data interface has two bulk endpoints */
23400a2430fSAndrzej Pietrasiewicz 
23500a2430fSAndrzej Pietrasiewicz static struct usb_interface_descriptor ncm_data_intf = {
23600a2430fSAndrzej Pietrasiewicz 	.bLength =		sizeof ncm_data_intf,
23700a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_INTERFACE,
23800a2430fSAndrzej Pietrasiewicz 
23900a2430fSAndrzej Pietrasiewicz 	.bInterfaceNumber =	1,
24000a2430fSAndrzej Pietrasiewicz 	.bAlternateSetting =	1,
24100a2430fSAndrzej Pietrasiewicz 	.bNumEndpoints =	2,
24200a2430fSAndrzej Pietrasiewicz 	.bInterfaceClass =	USB_CLASS_CDC_DATA,
24300a2430fSAndrzej Pietrasiewicz 	.bInterfaceSubClass =	0,
24400a2430fSAndrzej Pietrasiewicz 	.bInterfaceProtocol =	USB_CDC_NCM_PROTO_NTB,
24500a2430fSAndrzej Pietrasiewicz 	/* .iInterface = DYNAMIC */
24600a2430fSAndrzej Pietrasiewicz };
24700a2430fSAndrzej Pietrasiewicz 
24800a2430fSAndrzej Pietrasiewicz /* full speed support: */
24900a2430fSAndrzej Pietrasiewicz 
25000a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor fs_ncm_notify_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_INT,
25600a2430fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(NCM_STATUS_BYTECOUNT),
25700a2430fSAndrzej Pietrasiewicz 	.bInterval =		NCM_STATUS_INTERVAL_MS,
25800a2430fSAndrzej Pietrasiewicz };
25900a2430fSAndrzej Pietrasiewicz 
26000a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor fs_ncm_in_desc = {
26100a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
26200a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
26300a2430fSAndrzej Pietrasiewicz 
26400a2430fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_IN,
26500a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
26600a2430fSAndrzej Pietrasiewicz };
26700a2430fSAndrzej Pietrasiewicz 
26800a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor fs_ncm_out_desc = {
26900a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
27000a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
27100a2430fSAndrzej Pietrasiewicz 
27200a2430fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_OUT,
27300a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
27400a2430fSAndrzej Pietrasiewicz };
27500a2430fSAndrzej Pietrasiewicz 
27600a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *ncm_fs_function[] = {
27700a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ncm_iad_desc,
27800a2430fSAndrzej Pietrasiewicz 	/* CDC NCM control descriptors */
27900a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ncm_control_intf,
28000a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ncm_header_desc,
28100a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ncm_union_desc,
28200a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ecm_desc,
28300a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ncm_desc,
28400a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &fs_ncm_notify_desc,
28500a2430fSAndrzej Pietrasiewicz 	/* data interface, altsettings 0 and 1 */
28600a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ncm_data_nop_intf,
28700a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ncm_data_intf,
28800a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &fs_ncm_in_desc,
28900a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &fs_ncm_out_desc,
29000a2430fSAndrzej Pietrasiewicz 	NULL,
29100a2430fSAndrzej Pietrasiewicz };
29200a2430fSAndrzej Pietrasiewicz 
29300a2430fSAndrzej Pietrasiewicz /* high speed support: */
29400a2430fSAndrzej Pietrasiewicz 
29500a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hs_ncm_notify_desc = {
29600a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
29700a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
29800a2430fSAndrzej Pietrasiewicz 
29900a2430fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_IN,
30000a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_INT,
30100a2430fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(NCM_STATUS_BYTECOUNT),
30200a2430fSAndrzej Pietrasiewicz 	.bInterval =		USB_MS_TO_HS_INTERVAL(NCM_STATUS_INTERVAL_MS),
30300a2430fSAndrzej Pietrasiewicz };
30400a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hs_ncm_in_desc = {
30500a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
30600a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
30700a2430fSAndrzej Pietrasiewicz 
30800a2430fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_IN,
30900a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
31000a2430fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(512),
31100a2430fSAndrzej Pietrasiewicz };
31200a2430fSAndrzej Pietrasiewicz 
31300a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hs_ncm_out_desc = {
31400a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
31500a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
31600a2430fSAndrzej Pietrasiewicz 
31700a2430fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_OUT,
31800a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
31900a2430fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(512),
32000a2430fSAndrzej Pietrasiewicz };
32100a2430fSAndrzej Pietrasiewicz 
32200a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *ncm_hs_function[] = {
32300a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ncm_iad_desc,
32400a2430fSAndrzej Pietrasiewicz 	/* CDC NCM control descriptors */
32500a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ncm_control_intf,
32600a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ncm_header_desc,
32700a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ncm_union_desc,
32800a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ecm_desc,
32900a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ncm_desc,
33000a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &hs_ncm_notify_desc,
33100a2430fSAndrzej Pietrasiewicz 	/* data interface, altsettings 0 and 1 */
33200a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ncm_data_nop_intf,
33300a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ncm_data_intf,
33400a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &hs_ncm_in_desc,
33500a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &hs_ncm_out_desc,
33600a2430fSAndrzej Pietrasiewicz 	NULL,
33700a2430fSAndrzej Pietrasiewicz };
33800a2430fSAndrzej Pietrasiewicz 
33916501138SJussi Kivilinna 
34016501138SJussi Kivilinna /* super speed support: */
34116501138SJussi Kivilinna 
34216501138SJussi Kivilinna static struct usb_endpoint_descriptor ss_ncm_notify_desc = {
34316501138SJussi Kivilinna 	.bLength =		USB_DT_ENDPOINT_SIZE,
34416501138SJussi Kivilinna 	.bDescriptorType =	USB_DT_ENDPOINT,
34516501138SJussi Kivilinna 
34616501138SJussi Kivilinna 	.bEndpointAddress =	USB_DIR_IN,
34716501138SJussi Kivilinna 	.bmAttributes =		USB_ENDPOINT_XFER_INT,
34816501138SJussi Kivilinna 	.wMaxPacketSize =	cpu_to_le16(NCM_STATUS_BYTECOUNT),
34916501138SJussi Kivilinna 	.bInterval =		USB_MS_TO_HS_INTERVAL(NCM_STATUS_INTERVAL_MS)
35016501138SJussi Kivilinna };
35116501138SJussi Kivilinna 
35216501138SJussi Kivilinna static struct usb_ss_ep_comp_descriptor ss_ncm_notify_comp_desc = {
35316501138SJussi Kivilinna 	.bLength =		sizeof(ss_ncm_notify_comp_desc),
35416501138SJussi Kivilinna 	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
35516501138SJussi Kivilinna 
35616501138SJussi Kivilinna 	/* the following 3 values can be tweaked if necessary */
35716501138SJussi Kivilinna 	/* .bMaxBurst =		0, */
35816501138SJussi Kivilinna 	/* .bmAttributes =	0, */
35916501138SJussi Kivilinna 	.wBytesPerInterval =	cpu_to_le16(NCM_STATUS_BYTECOUNT),
36016501138SJussi Kivilinna };
36116501138SJussi Kivilinna 
36216501138SJussi Kivilinna static struct usb_endpoint_descriptor ss_ncm_in_desc = {
36316501138SJussi Kivilinna 	.bLength =		USB_DT_ENDPOINT_SIZE,
36416501138SJussi Kivilinna 	.bDescriptorType =	USB_DT_ENDPOINT,
36516501138SJussi Kivilinna 
36616501138SJussi Kivilinna 	.bEndpointAddress =	USB_DIR_IN,
36716501138SJussi Kivilinna 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
36816501138SJussi Kivilinna 	.wMaxPacketSize =	cpu_to_le16(1024),
36916501138SJussi Kivilinna };
37016501138SJussi Kivilinna 
37116501138SJussi Kivilinna static struct usb_endpoint_descriptor ss_ncm_out_desc = {
37216501138SJussi Kivilinna 	.bLength =		USB_DT_ENDPOINT_SIZE,
37316501138SJussi Kivilinna 	.bDescriptorType =	USB_DT_ENDPOINT,
37416501138SJussi Kivilinna 
37516501138SJussi Kivilinna 	.bEndpointAddress =	USB_DIR_OUT,
37616501138SJussi Kivilinna 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
37716501138SJussi Kivilinna 	.wMaxPacketSize =	cpu_to_le16(1024),
37816501138SJussi Kivilinna };
37916501138SJussi Kivilinna 
38016501138SJussi Kivilinna static struct usb_ss_ep_comp_descriptor ss_ncm_bulk_comp_desc = {
38116501138SJussi Kivilinna 	.bLength =		sizeof(ss_ncm_bulk_comp_desc),
38216501138SJussi Kivilinna 	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
38316501138SJussi Kivilinna 
38416501138SJussi Kivilinna 	/* the following 2 values can be tweaked if necessary */
38516501138SJussi Kivilinna 	/* .bMaxBurst =		0, */
38616501138SJussi Kivilinna 	/* .bmAttributes =	0, */
38716501138SJussi Kivilinna };
38816501138SJussi Kivilinna 
38916501138SJussi Kivilinna static struct usb_descriptor_header *ncm_ss_function[] = {
39016501138SJussi Kivilinna 	(struct usb_descriptor_header *) &ncm_iad_desc,
39116501138SJussi Kivilinna 	/* CDC NCM control descriptors */
39216501138SJussi Kivilinna 	(struct usb_descriptor_header *) &ncm_control_intf,
39316501138SJussi Kivilinna 	(struct usb_descriptor_header *) &ncm_header_desc,
39416501138SJussi Kivilinna 	(struct usb_descriptor_header *) &ncm_union_desc,
39516501138SJussi Kivilinna 	(struct usb_descriptor_header *) &ecm_desc,
39616501138SJussi Kivilinna 	(struct usb_descriptor_header *) &ncm_desc,
39716501138SJussi Kivilinna 	(struct usb_descriptor_header *) &ss_ncm_notify_desc,
39816501138SJussi Kivilinna 	(struct usb_descriptor_header *) &ss_ncm_notify_comp_desc,
39916501138SJussi Kivilinna 	/* data interface, altsettings 0 and 1 */
40016501138SJussi Kivilinna 	(struct usb_descriptor_header *) &ncm_data_nop_intf,
40116501138SJussi Kivilinna 	(struct usb_descriptor_header *) &ncm_data_intf,
40216501138SJussi Kivilinna 	(struct usb_descriptor_header *) &ss_ncm_in_desc,
40316501138SJussi Kivilinna 	(struct usb_descriptor_header *) &ss_ncm_bulk_comp_desc,
40416501138SJussi Kivilinna 	(struct usb_descriptor_header *) &ss_ncm_out_desc,
40516501138SJussi Kivilinna 	(struct usb_descriptor_header *) &ss_ncm_bulk_comp_desc,
40616501138SJussi Kivilinna 	NULL,
40716501138SJussi Kivilinna };
40816501138SJussi Kivilinna 
40900a2430fSAndrzej Pietrasiewicz /* string descriptors: */
41000a2430fSAndrzej Pietrasiewicz 
41100a2430fSAndrzej Pietrasiewicz #define STRING_CTRL_IDX	0
41200a2430fSAndrzej Pietrasiewicz #define STRING_MAC_IDX	1
41300a2430fSAndrzej Pietrasiewicz #define STRING_DATA_IDX	2
41400a2430fSAndrzej Pietrasiewicz #define STRING_IAD_IDX	3
41500a2430fSAndrzej Pietrasiewicz 
41600a2430fSAndrzej Pietrasiewicz static struct usb_string ncm_string_defs[] = {
41700a2430fSAndrzej Pietrasiewicz 	[STRING_CTRL_IDX].s = "CDC Network Control Model (NCM)",
41800a2430fSAndrzej Pietrasiewicz 	[STRING_MAC_IDX].s = "",
41900a2430fSAndrzej Pietrasiewicz 	[STRING_DATA_IDX].s = "CDC Network Data",
42000a2430fSAndrzej Pietrasiewicz 	[STRING_IAD_IDX].s = "CDC NCM",
42100a2430fSAndrzej Pietrasiewicz 	{  } /* end of list */
42200a2430fSAndrzej Pietrasiewicz };
42300a2430fSAndrzej Pietrasiewicz 
42400a2430fSAndrzej Pietrasiewicz static struct usb_gadget_strings ncm_string_table = {
42500a2430fSAndrzej Pietrasiewicz 	.language =		0x0409,	/* en-us */
42600a2430fSAndrzej Pietrasiewicz 	.strings =		ncm_string_defs,
42700a2430fSAndrzej Pietrasiewicz };
42800a2430fSAndrzej Pietrasiewicz 
42900a2430fSAndrzej Pietrasiewicz static struct usb_gadget_strings *ncm_strings[] = {
43000a2430fSAndrzej Pietrasiewicz 	&ncm_string_table,
43100a2430fSAndrzej Pietrasiewicz 	NULL,
43200a2430fSAndrzej Pietrasiewicz };
43300a2430fSAndrzej Pietrasiewicz 
43400a2430fSAndrzej Pietrasiewicz /*
43500a2430fSAndrzej Pietrasiewicz  * Here are options for NCM Datagram Pointer table (NDP) parser.
43600a2430fSAndrzej Pietrasiewicz  * There are 2 different formats: NDP16 and NDP32 in the spec (ch. 3),
43700a2430fSAndrzej Pietrasiewicz  * in NDP16 offsets and sizes fields are 1 16bit word wide,
43800a2430fSAndrzej Pietrasiewicz  * in NDP32 -- 2 16bit words wide. Also signatures are different.
43900a2430fSAndrzej Pietrasiewicz  * To make the parser code the same, put the differences in the structure,
44000a2430fSAndrzej Pietrasiewicz  * and switch pointers to the structures when the format is changed.
44100a2430fSAndrzej Pietrasiewicz  */
44200a2430fSAndrzej Pietrasiewicz 
44300a2430fSAndrzej Pietrasiewicz struct ndp_parser_opts {
44400a2430fSAndrzej Pietrasiewicz 	u32		nth_sign;
44500a2430fSAndrzej Pietrasiewicz 	u32		ndp_sign;
44600a2430fSAndrzej Pietrasiewicz 	unsigned	nth_size;
44700a2430fSAndrzej Pietrasiewicz 	unsigned	ndp_size;
44800a2430fSAndrzej Pietrasiewicz 	unsigned	dpe_size;
44900a2430fSAndrzej Pietrasiewicz 	unsigned	ndplen_align;
45000a2430fSAndrzej Pietrasiewicz 	/* sizes in u16 units */
45100a2430fSAndrzej Pietrasiewicz 	unsigned	dgram_item_len; /* index or length */
45200a2430fSAndrzej Pietrasiewicz 	unsigned	block_length;
45300a2430fSAndrzej Pietrasiewicz 	unsigned	ndp_index;
45400a2430fSAndrzej Pietrasiewicz 	unsigned	reserved1;
45500a2430fSAndrzej Pietrasiewicz 	unsigned	reserved2;
45600a2430fSAndrzej Pietrasiewicz 	unsigned	next_ndp_index;
45700a2430fSAndrzej Pietrasiewicz };
45800a2430fSAndrzej Pietrasiewicz 
45900a2430fSAndrzej Pietrasiewicz #define INIT_NDP16_OPTS {					\
46000a2430fSAndrzej Pietrasiewicz 		.nth_sign = USB_CDC_NCM_NTH16_SIGN,		\
46100a2430fSAndrzej Pietrasiewicz 		.ndp_sign = USB_CDC_NCM_NDP16_NOCRC_SIGN,	\
46200a2430fSAndrzej Pietrasiewicz 		.nth_size = sizeof(struct usb_cdc_ncm_nth16),	\
46300a2430fSAndrzej Pietrasiewicz 		.ndp_size = sizeof(struct usb_cdc_ncm_ndp16),	\
46400a2430fSAndrzej Pietrasiewicz 		.dpe_size = sizeof(struct usb_cdc_ncm_dpe16),	\
46500a2430fSAndrzej Pietrasiewicz 		.ndplen_align = 4,				\
46600a2430fSAndrzej Pietrasiewicz 		.dgram_item_len = 1,				\
46700a2430fSAndrzej Pietrasiewicz 		.block_length = 1,				\
46800a2430fSAndrzej Pietrasiewicz 		.ndp_index = 1,					\
46900a2430fSAndrzej Pietrasiewicz 		.reserved1 = 0,					\
47000a2430fSAndrzej Pietrasiewicz 		.reserved2 = 0,					\
47100a2430fSAndrzej Pietrasiewicz 		.next_ndp_index = 1,				\
47200a2430fSAndrzej Pietrasiewicz 	}
47300a2430fSAndrzej Pietrasiewicz 
47400a2430fSAndrzej Pietrasiewicz 
47500a2430fSAndrzej Pietrasiewicz #define INIT_NDP32_OPTS {					\
47600a2430fSAndrzej Pietrasiewicz 		.nth_sign = USB_CDC_NCM_NTH32_SIGN,		\
47700a2430fSAndrzej Pietrasiewicz 		.ndp_sign = USB_CDC_NCM_NDP32_NOCRC_SIGN,	\
47800a2430fSAndrzej Pietrasiewicz 		.nth_size = sizeof(struct usb_cdc_ncm_nth32),	\
47900a2430fSAndrzej Pietrasiewicz 		.ndp_size = sizeof(struct usb_cdc_ncm_ndp32),	\
48000a2430fSAndrzej Pietrasiewicz 		.dpe_size = sizeof(struct usb_cdc_ncm_dpe32),	\
48100a2430fSAndrzej Pietrasiewicz 		.ndplen_align = 8,				\
48200a2430fSAndrzej Pietrasiewicz 		.dgram_item_len = 2,				\
48300a2430fSAndrzej Pietrasiewicz 		.block_length = 2,				\
48400a2430fSAndrzej Pietrasiewicz 		.ndp_index = 2,					\
48500a2430fSAndrzej Pietrasiewicz 		.reserved1 = 1,					\
48600a2430fSAndrzej Pietrasiewicz 		.reserved2 = 2,					\
48700a2430fSAndrzej Pietrasiewicz 		.next_ndp_index = 2,				\
48800a2430fSAndrzej Pietrasiewicz 	}
48900a2430fSAndrzej Pietrasiewicz 
49000a2430fSAndrzej Pietrasiewicz static const struct ndp_parser_opts ndp16_opts = INIT_NDP16_OPTS;
49100a2430fSAndrzej Pietrasiewicz static const struct ndp_parser_opts ndp32_opts = INIT_NDP32_OPTS;
49200a2430fSAndrzej Pietrasiewicz 
49300a2430fSAndrzej Pietrasiewicz static inline void put_ncm(__le16 **p, unsigned size, unsigned val)
49400a2430fSAndrzej Pietrasiewicz {
49500a2430fSAndrzej Pietrasiewicz 	switch (size) {
49600a2430fSAndrzej Pietrasiewicz 	case 1:
49700a2430fSAndrzej Pietrasiewicz 		put_unaligned_le16((u16)val, *p);
49800a2430fSAndrzej Pietrasiewicz 		break;
49900a2430fSAndrzej Pietrasiewicz 	case 2:
50000a2430fSAndrzej Pietrasiewicz 		put_unaligned_le32((u32)val, *p);
50100a2430fSAndrzej Pietrasiewicz 
50200a2430fSAndrzej Pietrasiewicz 		break;
50300a2430fSAndrzej Pietrasiewicz 	default:
50400a2430fSAndrzej Pietrasiewicz 		BUG();
50500a2430fSAndrzej Pietrasiewicz 	}
50600a2430fSAndrzej Pietrasiewicz 
50700a2430fSAndrzej Pietrasiewicz 	*p += size;
50800a2430fSAndrzej Pietrasiewicz }
50900a2430fSAndrzej Pietrasiewicz 
51000a2430fSAndrzej Pietrasiewicz static inline unsigned get_ncm(__le16 **p, unsigned size)
51100a2430fSAndrzej Pietrasiewicz {
51200a2430fSAndrzej Pietrasiewicz 	unsigned tmp;
51300a2430fSAndrzej Pietrasiewicz 
51400a2430fSAndrzej Pietrasiewicz 	switch (size) {
51500a2430fSAndrzej Pietrasiewicz 	case 1:
51600a2430fSAndrzej Pietrasiewicz 		tmp = get_unaligned_le16(*p);
51700a2430fSAndrzej Pietrasiewicz 		break;
51800a2430fSAndrzej Pietrasiewicz 	case 2:
51900a2430fSAndrzej Pietrasiewicz 		tmp = get_unaligned_le32(*p);
52000a2430fSAndrzej Pietrasiewicz 		break;
52100a2430fSAndrzej Pietrasiewicz 	default:
52200a2430fSAndrzej Pietrasiewicz 		BUG();
52300a2430fSAndrzej Pietrasiewicz 	}
52400a2430fSAndrzej Pietrasiewicz 
52500a2430fSAndrzej Pietrasiewicz 	*p += size;
52600a2430fSAndrzej Pietrasiewicz 	return tmp;
52700a2430fSAndrzej Pietrasiewicz }
52800a2430fSAndrzej Pietrasiewicz 
52900a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
53000a2430fSAndrzej Pietrasiewicz 
53100a2430fSAndrzej Pietrasiewicz static inline void ncm_reset_values(struct f_ncm *ncm)
53200a2430fSAndrzej Pietrasiewicz {
53300a2430fSAndrzej Pietrasiewicz 	ncm->parser_opts = &ndp16_opts;
53400a2430fSAndrzej Pietrasiewicz 	ncm->is_crc = false;
53500a2430fSAndrzej Pietrasiewicz 	ncm->port.cdc_filter = DEFAULT_FILTER;
53600a2430fSAndrzej Pietrasiewicz 
53700a2430fSAndrzej Pietrasiewicz 	/* doesn't make sense for ncm, fixed size used */
53800a2430fSAndrzej Pietrasiewicz 	ncm->port.header_len = 0;
53900a2430fSAndrzej Pietrasiewicz 
54000a2430fSAndrzej Pietrasiewicz 	ncm->port.fixed_out_len = le32_to_cpu(ntb_parameters.dwNtbOutMaxSize);
54100a2430fSAndrzej Pietrasiewicz 	ncm->port.fixed_in_len = NTB_DEFAULT_IN_SIZE;
54200a2430fSAndrzej Pietrasiewicz }
54300a2430fSAndrzej Pietrasiewicz 
54400a2430fSAndrzej Pietrasiewicz /*
54500a2430fSAndrzej Pietrasiewicz  * Context: ncm->lock held
54600a2430fSAndrzej Pietrasiewicz  */
54700a2430fSAndrzej Pietrasiewicz static void ncm_do_notify(struct f_ncm *ncm)
54800a2430fSAndrzej Pietrasiewicz {
54900a2430fSAndrzej Pietrasiewicz 	struct usb_request		*req = ncm->notify_req;
55000a2430fSAndrzej Pietrasiewicz 	struct usb_cdc_notification	*event;
55100a2430fSAndrzej Pietrasiewicz 	struct usb_composite_dev	*cdev = ncm->port.func.config->cdev;
55200a2430fSAndrzej Pietrasiewicz 	__le32				*data;
55300a2430fSAndrzej Pietrasiewicz 	int				status;
55400a2430fSAndrzej Pietrasiewicz 
55500a2430fSAndrzej Pietrasiewicz 	/* notification already in flight? */
55600a2430fSAndrzej Pietrasiewicz 	if (!req)
55700a2430fSAndrzej Pietrasiewicz 		return;
55800a2430fSAndrzej Pietrasiewicz 
55900a2430fSAndrzej Pietrasiewicz 	event = req->buf;
56000a2430fSAndrzej Pietrasiewicz 	switch (ncm->notify_state) {
56100a2430fSAndrzej Pietrasiewicz 	case NCM_NOTIFY_NONE:
56200a2430fSAndrzej Pietrasiewicz 		return;
56300a2430fSAndrzej Pietrasiewicz 
56400a2430fSAndrzej Pietrasiewicz 	case NCM_NOTIFY_CONNECT:
56500a2430fSAndrzej Pietrasiewicz 		event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
56600a2430fSAndrzej Pietrasiewicz 		if (ncm->is_open)
56700a2430fSAndrzej Pietrasiewicz 			event->wValue = cpu_to_le16(1);
56800a2430fSAndrzej Pietrasiewicz 		else
56900a2430fSAndrzej Pietrasiewicz 			event->wValue = cpu_to_le16(0);
57000a2430fSAndrzej Pietrasiewicz 		event->wLength = 0;
57100a2430fSAndrzej Pietrasiewicz 		req->length = sizeof *event;
57200a2430fSAndrzej Pietrasiewicz 
57300a2430fSAndrzej Pietrasiewicz 		DBG(cdev, "notify connect %s\n",
57400a2430fSAndrzej Pietrasiewicz 				ncm->is_open ? "true" : "false");
57500a2430fSAndrzej Pietrasiewicz 		ncm->notify_state = NCM_NOTIFY_NONE;
57600a2430fSAndrzej Pietrasiewicz 		break;
57700a2430fSAndrzej Pietrasiewicz 
57800a2430fSAndrzej Pietrasiewicz 	case NCM_NOTIFY_SPEED:
57900a2430fSAndrzej Pietrasiewicz 		event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE;
58000a2430fSAndrzej Pietrasiewicz 		event->wValue = cpu_to_le16(0);
58100a2430fSAndrzej Pietrasiewicz 		event->wLength = cpu_to_le16(8);
58200a2430fSAndrzej Pietrasiewicz 		req->length = NCM_STATUS_BYTECOUNT;
58300a2430fSAndrzej Pietrasiewicz 
58400a2430fSAndrzej Pietrasiewicz 		/* SPEED_CHANGE data is up/down speeds in bits/sec */
58500a2430fSAndrzej Pietrasiewicz 		data = req->buf + sizeof *event;
58600a2430fSAndrzej Pietrasiewicz 		data[0] = cpu_to_le32(ncm_bitrate(cdev->gadget));
58700a2430fSAndrzej Pietrasiewicz 		data[1] = data[0];
58800a2430fSAndrzej Pietrasiewicz 
58900a2430fSAndrzej Pietrasiewicz 		DBG(cdev, "notify speed %d\n", ncm_bitrate(cdev->gadget));
59000a2430fSAndrzej Pietrasiewicz 		ncm->notify_state = NCM_NOTIFY_CONNECT;
59100a2430fSAndrzej Pietrasiewicz 		break;
59200a2430fSAndrzej Pietrasiewicz 	}
59300a2430fSAndrzej Pietrasiewicz 	event->bmRequestType = 0xA1;
59400a2430fSAndrzej Pietrasiewicz 	event->wIndex = cpu_to_le16(ncm->ctrl_id);
59500a2430fSAndrzej Pietrasiewicz 
59600a2430fSAndrzej Pietrasiewicz 	ncm->notify_req = NULL;
59700a2430fSAndrzej Pietrasiewicz 	/*
59800a2430fSAndrzej Pietrasiewicz 	 * In double buffering if there is a space in FIFO,
59900a2430fSAndrzej Pietrasiewicz 	 * completion callback can be called right after the call,
60000a2430fSAndrzej Pietrasiewicz 	 * so unlocking
60100a2430fSAndrzej Pietrasiewicz 	 */
60200a2430fSAndrzej Pietrasiewicz 	spin_unlock(&ncm->lock);
60300a2430fSAndrzej Pietrasiewicz 	status = usb_ep_queue(ncm->notify, req, GFP_ATOMIC);
60400a2430fSAndrzej Pietrasiewicz 	spin_lock(&ncm->lock);
60500a2430fSAndrzej Pietrasiewicz 	if (status < 0) {
60600a2430fSAndrzej Pietrasiewicz 		ncm->notify_req = req;
60700a2430fSAndrzej Pietrasiewicz 		DBG(cdev, "notify --> %d\n", status);
60800a2430fSAndrzej Pietrasiewicz 	}
60900a2430fSAndrzej Pietrasiewicz }
61000a2430fSAndrzej Pietrasiewicz 
61100a2430fSAndrzej Pietrasiewicz /*
61200a2430fSAndrzej Pietrasiewicz  * Context: ncm->lock held
61300a2430fSAndrzej Pietrasiewicz  */
61400a2430fSAndrzej Pietrasiewicz static void ncm_notify(struct f_ncm *ncm)
61500a2430fSAndrzej Pietrasiewicz {
61600a2430fSAndrzej Pietrasiewicz 	/*
61700a2430fSAndrzej Pietrasiewicz 	 * NOTE on most versions of Linux, host side cdc-ethernet
61800a2430fSAndrzej Pietrasiewicz 	 * won't listen for notifications until its netdevice opens.
61900a2430fSAndrzej Pietrasiewicz 	 * The first notification then sits in the FIFO for a long
62000a2430fSAndrzej Pietrasiewicz 	 * time, and the second one is queued.
62100a2430fSAndrzej Pietrasiewicz 	 *
62200a2430fSAndrzej Pietrasiewicz 	 * If ncm_notify() is called before the second (CONNECT)
62300a2430fSAndrzej Pietrasiewicz 	 * notification is sent, then it will reset to send the SPEED
62400a2430fSAndrzej Pietrasiewicz 	 * notificaion again (and again, and again), but it's not a problem
62500a2430fSAndrzej Pietrasiewicz 	 */
62600a2430fSAndrzej Pietrasiewicz 	ncm->notify_state = NCM_NOTIFY_SPEED;
62700a2430fSAndrzej Pietrasiewicz 	ncm_do_notify(ncm);
62800a2430fSAndrzej Pietrasiewicz }
62900a2430fSAndrzej Pietrasiewicz 
63000a2430fSAndrzej Pietrasiewicz static void ncm_notify_complete(struct usb_ep *ep, struct usb_request *req)
63100a2430fSAndrzej Pietrasiewicz {
63200a2430fSAndrzej Pietrasiewicz 	struct f_ncm			*ncm = req->context;
63300a2430fSAndrzej Pietrasiewicz 	struct usb_composite_dev	*cdev = ncm->port.func.config->cdev;
63400a2430fSAndrzej Pietrasiewicz 	struct usb_cdc_notification	*event = req->buf;
63500a2430fSAndrzej Pietrasiewicz 
63600a2430fSAndrzej Pietrasiewicz 	spin_lock(&ncm->lock);
63700a2430fSAndrzej Pietrasiewicz 	switch (req->status) {
63800a2430fSAndrzej Pietrasiewicz 	case 0:
63900a2430fSAndrzej Pietrasiewicz 		VDBG(cdev, "Notification %02x sent\n",
64000a2430fSAndrzej Pietrasiewicz 		     event->bNotificationType);
64100a2430fSAndrzej Pietrasiewicz 		break;
64200a2430fSAndrzej Pietrasiewicz 	case -ECONNRESET:
64300a2430fSAndrzej Pietrasiewicz 	case -ESHUTDOWN:
64400a2430fSAndrzej Pietrasiewicz 		ncm->notify_state = NCM_NOTIFY_NONE;
64500a2430fSAndrzej Pietrasiewicz 		break;
64600a2430fSAndrzej Pietrasiewicz 	default:
64700a2430fSAndrzej Pietrasiewicz 		DBG(cdev, "event %02x --> %d\n",
64800a2430fSAndrzej Pietrasiewicz 			event->bNotificationType, req->status);
64900a2430fSAndrzej Pietrasiewicz 		break;
65000a2430fSAndrzej Pietrasiewicz 	}
65100a2430fSAndrzej Pietrasiewicz 	ncm->notify_req = req;
65200a2430fSAndrzej Pietrasiewicz 	ncm_do_notify(ncm);
65300a2430fSAndrzej Pietrasiewicz 	spin_unlock(&ncm->lock);
65400a2430fSAndrzej Pietrasiewicz }
65500a2430fSAndrzej Pietrasiewicz 
65600a2430fSAndrzej Pietrasiewicz static void ncm_ep0out_complete(struct usb_ep *ep, struct usb_request *req)
65700a2430fSAndrzej Pietrasiewicz {
65800a2430fSAndrzej Pietrasiewicz 	/* now for SET_NTB_INPUT_SIZE only */
65900a2430fSAndrzej Pietrasiewicz 	unsigned		in_size;
66000a2430fSAndrzej Pietrasiewicz 	struct usb_function	*f = req->context;
66100a2430fSAndrzej Pietrasiewicz 	struct f_ncm		*ncm = func_to_ncm(f);
66235bfde36SRobert Baldyga 	struct usb_composite_dev *cdev = f->config->cdev;
66300a2430fSAndrzej Pietrasiewicz 
66400a2430fSAndrzej Pietrasiewicz 	req->context = NULL;
66500a2430fSAndrzej Pietrasiewicz 	if (req->status || req->actual != req->length) {
66600a2430fSAndrzej Pietrasiewicz 		DBG(cdev, "Bad control-OUT transfer\n");
66700a2430fSAndrzej Pietrasiewicz 		goto invalid;
66800a2430fSAndrzej Pietrasiewicz 	}
66900a2430fSAndrzej Pietrasiewicz 
67000a2430fSAndrzej Pietrasiewicz 	in_size = get_unaligned_le32(req->buf);
67100a2430fSAndrzej Pietrasiewicz 	if (in_size < USB_CDC_NCM_NTB_MIN_IN_SIZE ||
67200a2430fSAndrzej Pietrasiewicz 	    in_size > le32_to_cpu(ntb_parameters.dwNtbInMaxSize)) {
67300a2430fSAndrzej Pietrasiewicz 		DBG(cdev, "Got wrong INPUT SIZE (%d) from host\n", in_size);
67400a2430fSAndrzej Pietrasiewicz 		goto invalid;
67500a2430fSAndrzej Pietrasiewicz 	}
67600a2430fSAndrzej Pietrasiewicz 
67700a2430fSAndrzej Pietrasiewicz 	ncm->port.fixed_in_len = in_size;
67800a2430fSAndrzej Pietrasiewicz 	VDBG(cdev, "Set NTB INPUT SIZE %d\n", in_size);
67900a2430fSAndrzej Pietrasiewicz 	return;
68000a2430fSAndrzej Pietrasiewicz 
68100a2430fSAndrzej Pietrasiewicz invalid:
68200a2430fSAndrzej Pietrasiewicz 	usb_ep_set_halt(ep);
68300a2430fSAndrzej Pietrasiewicz 	return;
68400a2430fSAndrzej Pietrasiewicz }
68500a2430fSAndrzej Pietrasiewicz 
68600a2430fSAndrzej Pietrasiewicz static int ncm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
68700a2430fSAndrzej Pietrasiewicz {
68800a2430fSAndrzej Pietrasiewicz 	struct f_ncm		*ncm = func_to_ncm(f);
68900a2430fSAndrzej Pietrasiewicz 	struct usb_composite_dev *cdev = f->config->cdev;
69000a2430fSAndrzej Pietrasiewicz 	struct usb_request	*req = cdev->req;
69100a2430fSAndrzej Pietrasiewicz 	int			value = -EOPNOTSUPP;
69200a2430fSAndrzej Pietrasiewicz 	u16			w_index = le16_to_cpu(ctrl->wIndex);
69300a2430fSAndrzej Pietrasiewicz 	u16			w_value = le16_to_cpu(ctrl->wValue);
69400a2430fSAndrzej Pietrasiewicz 	u16			w_length = le16_to_cpu(ctrl->wLength);
69500a2430fSAndrzej Pietrasiewicz 
69600a2430fSAndrzej Pietrasiewicz 	/*
69700a2430fSAndrzej Pietrasiewicz 	 * composite driver infrastructure handles everything except
69800a2430fSAndrzej Pietrasiewicz 	 * CDC class messages; interface activation uses set_alt().
69900a2430fSAndrzej Pietrasiewicz 	 */
70000a2430fSAndrzej Pietrasiewicz 	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
70100a2430fSAndrzej Pietrasiewicz 	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
70200a2430fSAndrzej Pietrasiewicz 			| USB_CDC_SET_ETHERNET_PACKET_FILTER:
70300a2430fSAndrzej Pietrasiewicz 		/*
70400a2430fSAndrzej Pietrasiewicz 		 * see 6.2.30: no data, wIndex = interface,
70500a2430fSAndrzej Pietrasiewicz 		 * wValue = packet filter bitmap
70600a2430fSAndrzej Pietrasiewicz 		 */
70700a2430fSAndrzej Pietrasiewicz 		if (w_length != 0 || w_index != ncm->ctrl_id)
70800a2430fSAndrzej Pietrasiewicz 			goto invalid;
70900a2430fSAndrzej Pietrasiewicz 		DBG(cdev, "packet filter %02x\n", w_value);
71000a2430fSAndrzej Pietrasiewicz 		/*
71100a2430fSAndrzej Pietrasiewicz 		 * REVISIT locking of cdc_filter.  This assumes the UDC
71200a2430fSAndrzej Pietrasiewicz 		 * driver won't have a concurrent packet TX irq running on
71300a2430fSAndrzej Pietrasiewicz 		 * another CPU; or that if it does, this write is atomic...
71400a2430fSAndrzej Pietrasiewicz 		 */
71500a2430fSAndrzej Pietrasiewicz 		ncm->port.cdc_filter = w_value;
71600a2430fSAndrzej Pietrasiewicz 		value = 0;
71700a2430fSAndrzej Pietrasiewicz 		break;
71800a2430fSAndrzej Pietrasiewicz 	/*
71900a2430fSAndrzej Pietrasiewicz 	 * and optionally:
72000a2430fSAndrzej Pietrasiewicz 	 * case USB_CDC_SEND_ENCAPSULATED_COMMAND:
72100a2430fSAndrzej Pietrasiewicz 	 * case USB_CDC_GET_ENCAPSULATED_RESPONSE:
72200a2430fSAndrzej Pietrasiewicz 	 * case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS:
72300a2430fSAndrzej Pietrasiewicz 	 * case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER:
72400a2430fSAndrzej Pietrasiewicz 	 * case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER:
72500a2430fSAndrzej Pietrasiewicz 	 * case USB_CDC_GET_ETHERNET_STATISTIC:
72600a2430fSAndrzej Pietrasiewicz 	 */
72700a2430fSAndrzej Pietrasiewicz 
72800a2430fSAndrzej Pietrasiewicz 	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
72900a2430fSAndrzej Pietrasiewicz 		| USB_CDC_GET_NTB_PARAMETERS:
73000a2430fSAndrzej Pietrasiewicz 
73100a2430fSAndrzej Pietrasiewicz 		if (w_length == 0 || w_value != 0 || w_index != ncm->ctrl_id)
73200a2430fSAndrzej Pietrasiewicz 			goto invalid;
73300a2430fSAndrzej Pietrasiewicz 		value = w_length > sizeof ntb_parameters ?
73400a2430fSAndrzej Pietrasiewicz 			sizeof ntb_parameters : w_length;
73500a2430fSAndrzej Pietrasiewicz 		memcpy(req->buf, &ntb_parameters, value);
73600a2430fSAndrzej Pietrasiewicz 		VDBG(cdev, "Host asked NTB parameters\n");
73700a2430fSAndrzej Pietrasiewicz 		break;
73800a2430fSAndrzej Pietrasiewicz 
73900a2430fSAndrzej Pietrasiewicz 	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
74000a2430fSAndrzej Pietrasiewicz 		| USB_CDC_GET_NTB_INPUT_SIZE:
74100a2430fSAndrzej Pietrasiewicz 
74200a2430fSAndrzej Pietrasiewicz 		if (w_length < 4 || w_value != 0 || w_index != ncm->ctrl_id)
74300a2430fSAndrzej Pietrasiewicz 			goto invalid;
74400a2430fSAndrzej Pietrasiewicz 		put_unaligned_le32(ncm->port.fixed_in_len, req->buf);
74500a2430fSAndrzej Pietrasiewicz 		value = 4;
74600a2430fSAndrzej Pietrasiewicz 		VDBG(cdev, "Host asked INPUT SIZE, sending %d\n",
74700a2430fSAndrzej Pietrasiewicz 		     ncm->port.fixed_in_len);
74800a2430fSAndrzej Pietrasiewicz 		break;
74900a2430fSAndrzej Pietrasiewicz 
75000a2430fSAndrzej Pietrasiewicz 	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
75100a2430fSAndrzej Pietrasiewicz 		| USB_CDC_SET_NTB_INPUT_SIZE:
75200a2430fSAndrzej Pietrasiewicz 	{
75300a2430fSAndrzej Pietrasiewicz 		if (w_length != 4 || w_value != 0 || w_index != ncm->ctrl_id)
75400a2430fSAndrzej Pietrasiewicz 			goto invalid;
75500a2430fSAndrzej Pietrasiewicz 		req->complete = ncm_ep0out_complete;
75600a2430fSAndrzej Pietrasiewicz 		req->length = w_length;
75700a2430fSAndrzej Pietrasiewicz 		req->context = f;
75800a2430fSAndrzej Pietrasiewicz 
75900a2430fSAndrzej Pietrasiewicz 		value = req->length;
76000a2430fSAndrzej Pietrasiewicz 		break;
76100a2430fSAndrzej Pietrasiewicz 	}
76200a2430fSAndrzej Pietrasiewicz 
76300a2430fSAndrzej Pietrasiewicz 	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
76400a2430fSAndrzej Pietrasiewicz 		| USB_CDC_GET_NTB_FORMAT:
76500a2430fSAndrzej Pietrasiewicz 	{
76600a2430fSAndrzej Pietrasiewicz 		uint16_t format;
76700a2430fSAndrzej Pietrasiewicz 
76800a2430fSAndrzej Pietrasiewicz 		if (w_length < 2 || w_value != 0 || w_index != ncm->ctrl_id)
76900a2430fSAndrzej Pietrasiewicz 			goto invalid;
77000a2430fSAndrzej Pietrasiewicz 		format = (ncm->parser_opts == &ndp16_opts) ? 0x0000 : 0x0001;
77100a2430fSAndrzej Pietrasiewicz 		put_unaligned_le16(format, req->buf);
77200a2430fSAndrzej Pietrasiewicz 		value = 2;
77300a2430fSAndrzej Pietrasiewicz 		VDBG(cdev, "Host asked NTB FORMAT, sending %d\n", format);
77400a2430fSAndrzej Pietrasiewicz 		break;
77500a2430fSAndrzej Pietrasiewicz 	}
77600a2430fSAndrzej Pietrasiewicz 
77700a2430fSAndrzej Pietrasiewicz 	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
77800a2430fSAndrzej Pietrasiewicz 		| USB_CDC_SET_NTB_FORMAT:
77900a2430fSAndrzej Pietrasiewicz 	{
78000a2430fSAndrzej Pietrasiewicz 		if (w_length != 0 || w_index != ncm->ctrl_id)
78100a2430fSAndrzej Pietrasiewicz 			goto invalid;
78200a2430fSAndrzej Pietrasiewicz 		switch (w_value) {
78300a2430fSAndrzej Pietrasiewicz 		case 0x0000:
78400a2430fSAndrzej Pietrasiewicz 			ncm->parser_opts = &ndp16_opts;
78500a2430fSAndrzej Pietrasiewicz 			DBG(cdev, "NCM16 selected\n");
78600a2430fSAndrzej Pietrasiewicz 			break;
78700a2430fSAndrzej Pietrasiewicz 		case 0x0001:
78800a2430fSAndrzej Pietrasiewicz 			ncm->parser_opts = &ndp32_opts;
78900a2430fSAndrzej Pietrasiewicz 			DBG(cdev, "NCM32 selected\n");
79000a2430fSAndrzej Pietrasiewicz 			break;
79100a2430fSAndrzej Pietrasiewicz 		default:
79200a2430fSAndrzej Pietrasiewicz 			goto invalid;
79300a2430fSAndrzej Pietrasiewicz 		}
79400a2430fSAndrzej Pietrasiewicz 		value = 0;
79500a2430fSAndrzej Pietrasiewicz 		break;
79600a2430fSAndrzej Pietrasiewicz 	}
79700a2430fSAndrzej Pietrasiewicz 	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
79800a2430fSAndrzej Pietrasiewicz 		| USB_CDC_GET_CRC_MODE:
79900a2430fSAndrzej Pietrasiewicz 	{
80000a2430fSAndrzej Pietrasiewicz 		uint16_t is_crc;
80100a2430fSAndrzej Pietrasiewicz 
80200a2430fSAndrzej Pietrasiewicz 		if (w_length < 2 || w_value != 0 || w_index != ncm->ctrl_id)
80300a2430fSAndrzej Pietrasiewicz 			goto invalid;
80400a2430fSAndrzej Pietrasiewicz 		is_crc = ncm->is_crc ? 0x0001 : 0x0000;
80500a2430fSAndrzej Pietrasiewicz 		put_unaligned_le16(is_crc, req->buf);
80600a2430fSAndrzej Pietrasiewicz 		value = 2;
80700a2430fSAndrzej Pietrasiewicz 		VDBG(cdev, "Host asked CRC MODE, sending %d\n", is_crc);
80800a2430fSAndrzej Pietrasiewicz 		break;
80900a2430fSAndrzej Pietrasiewicz 	}
81000a2430fSAndrzej Pietrasiewicz 
81100a2430fSAndrzej Pietrasiewicz 	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
81200a2430fSAndrzej Pietrasiewicz 		| USB_CDC_SET_CRC_MODE:
81300a2430fSAndrzej Pietrasiewicz 	{
81400a2430fSAndrzej Pietrasiewicz 		int ndp_hdr_crc = 0;
81500a2430fSAndrzej Pietrasiewicz 
81600a2430fSAndrzej Pietrasiewicz 		if (w_length != 0 || w_index != ncm->ctrl_id)
81700a2430fSAndrzej Pietrasiewicz 			goto invalid;
81800a2430fSAndrzej Pietrasiewicz 		switch (w_value) {
81900a2430fSAndrzej Pietrasiewicz 		case 0x0000:
82000a2430fSAndrzej Pietrasiewicz 			ncm->is_crc = false;
82100a2430fSAndrzej Pietrasiewicz 			ndp_hdr_crc = NCM_NDP_HDR_NOCRC;
82200a2430fSAndrzej Pietrasiewicz 			DBG(cdev, "non-CRC mode selected\n");
82300a2430fSAndrzej Pietrasiewicz 			break;
82400a2430fSAndrzej Pietrasiewicz 		case 0x0001:
82500a2430fSAndrzej Pietrasiewicz 			ncm->is_crc = true;
82600a2430fSAndrzej Pietrasiewicz 			ndp_hdr_crc = NCM_NDP_HDR_CRC;
82700a2430fSAndrzej Pietrasiewicz 			DBG(cdev, "CRC mode selected\n");
82800a2430fSAndrzej Pietrasiewicz 			break;
82900a2430fSAndrzej Pietrasiewicz 		default:
83000a2430fSAndrzej Pietrasiewicz 			goto invalid;
83100a2430fSAndrzej Pietrasiewicz 		}
83200a2430fSAndrzej Pietrasiewicz 		ncm->ndp_sign = ncm->parser_opts->ndp_sign | ndp_hdr_crc;
83300a2430fSAndrzej Pietrasiewicz 		value = 0;
83400a2430fSAndrzej Pietrasiewicz 		break;
83500a2430fSAndrzej Pietrasiewicz 	}
83600a2430fSAndrzej Pietrasiewicz 
83700a2430fSAndrzej Pietrasiewicz 	/* and disabled in ncm descriptor: */
83800a2430fSAndrzej Pietrasiewicz 	/* case USB_CDC_GET_NET_ADDRESS: */
83900a2430fSAndrzej Pietrasiewicz 	/* case USB_CDC_SET_NET_ADDRESS: */
84000a2430fSAndrzej Pietrasiewicz 	/* case USB_CDC_GET_MAX_DATAGRAM_SIZE: */
84100a2430fSAndrzej Pietrasiewicz 	/* case USB_CDC_SET_MAX_DATAGRAM_SIZE: */
84200a2430fSAndrzej Pietrasiewicz 
84300a2430fSAndrzej Pietrasiewicz 	default:
84400a2430fSAndrzej Pietrasiewicz invalid:
84500a2430fSAndrzej Pietrasiewicz 		DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
84600a2430fSAndrzej Pietrasiewicz 			ctrl->bRequestType, ctrl->bRequest,
84700a2430fSAndrzej Pietrasiewicz 			w_value, w_index, w_length);
84800a2430fSAndrzej Pietrasiewicz 	}
84900a2430fSAndrzej Pietrasiewicz 
85000a2430fSAndrzej Pietrasiewicz 	/* respond with data transfer or status phase? */
85100a2430fSAndrzej Pietrasiewicz 	if (value >= 0) {
85200a2430fSAndrzej Pietrasiewicz 		DBG(cdev, "ncm req%02x.%02x v%04x i%04x l%d\n",
85300a2430fSAndrzej Pietrasiewicz 			ctrl->bRequestType, ctrl->bRequest,
85400a2430fSAndrzej Pietrasiewicz 			w_value, w_index, w_length);
85500a2430fSAndrzej Pietrasiewicz 		req->zero = 0;
85600a2430fSAndrzej Pietrasiewicz 		req->length = value;
85700a2430fSAndrzej Pietrasiewicz 		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
85800a2430fSAndrzej Pietrasiewicz 		if (value < 0)
85900a2430fSAndrzej Pietrasiewicz 			ERROR(cdev, "ncm req %02x.%02x response err %d\n",
86000a2430fSAndrzej Pietrasiewicz 					ctrl->bRequestType, ctrl->bRequest,
86100a2430fSAndrzej Pietrasiewicz 					value);
86200a2430fSAndrzej Pietrasiewicz 	}
86300a2430fSAndrzej Pietrasiewicz 
86400a2430fSAndrzej Pietrasiewicz 	/* device either stalls (value < 0) or reports success */
86500a2430fSAndrzej Pietrasiewicz 	return value;
86600a2430fSAndrzej Pietrasiewicz }
86700a2430fSAndrzej Pietrasiewicz 
86800a2430fSAndrzej Pietrasiewicz 
86900a2430fSAndrzej Pietrasiewicz static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
87000a2430fSAndrzej Pietrasiewicz {
87100a2430fSAndrzej Pietrasiewicz 	struct f_ncm		*ncm = func_to_ncm(f);
87200a2430fSAndrzej Pietrasiewicz 	struct usb_composite_dev *cdev = f->config->cdev;
87300a2430fSAndrzej Pietrasiewicz 
87400a2430fSAndrzej Pietrasiewicz 	/* Control interface has only altsetting 0 */
87500a2430fSAndrzej Pietrasiewicz 	if (intf == ncm->ctrl_id) {
87600a2430fSAndrzej Pietrasiewicz 		if (alt != 0)
87700a2430fSAndrzej Pietrasiewicz 			goto fail;
87800a2430fSAndrzej Pietrasiewicz 
87900a2430fSAndrzej Pietrasiewicz 		DBG(cdev, "reset ncm control %d\n", intf);
88000a2430fSAndrzej Pietrasiewicz 		usb_ep_disable(ncm->notify);
88100a2430fSAndrzej Pietrasiewicz 
88200a2430fSAndrzej Pietrasiewicz 		if (!(ncm->notify->desc)) {
88300a2430fSAndrzej Pietrasiewicz 			DBG(cdev, "init ncm ctrl %d\n", intf);
88400a2430fSAndrzej Pietrasiewicz 			if (config_ep_by_speed(cdev->gadget, f, ncm->notify))
88500a2430fSAndrzej Pietrasiewicz 				goto fail;
88600a2430fSAndrzej Pietrasiewicz 		}
88700a2430fSAndrzej Pietrasiewicz 		usb_ep_enable(ncm->notify);
88800a2430fSAndrzej Pietrasiewicz 
88900a2430fSAndrzej Pietrasiewicz 	/* Data interface has two altsettings, 0 and 1 */
89000a2430fSAndrzej Pietrasiewicz 	} else if (intf == ncm->data_id) {
89100a2430fSAndrzej Pietrasiewicz 		if (alt > 1)
89200a2430fSAndrzej Pietrasiewicz 			goto fail;
89300a2430fSAndrzej Pietrasiewicz 
8946b4012a2SRobert Baldyga 		if (ncm->port.in_ep->enabled) {
89500a2430fSAndrzej Pietrasiewicz 			DBG(cdev, "reset ncm\n");
89600a2430fSAndrzej Pietrasiewicz 			ncm->timer_stopping = true;
89700a2430fSAndrzej Pietrasiewicz 			ncm->netdev = NULL;
89800a2430fSAndrzej Pietrasiewicz 			gether_disconnect(&ncm->port);
89900a2430fSAndrzej Pietrasiewicz 			ncm_reset_values(ncm);
90000a2430fSAndrzej Pietrasiewicz 		}
90100a2430fSAndrzej Pietrasiewicz 
90200a2430fSAndrzej Pietrasiewicz 		/*
90300a2430fSAndrzej Pietrasiewicz 		 * CDC Network only sends data in non-default altsettings.
90400a2430fSAndrzej Pietrasiewicz 		 * Changing altsettings resets filters, statistics, etc.
90500a2430fSAndrzej Pietrasiewicz 		 */
90600a2430fSAndrzej Pietrasiewicz 		if (alt == 1) {
90700a2430fSAndrzej Pietrasiewicz 			struct net_device	*net;
90800a2430fSAndrzej Pietrasiewicz 
90900a2430fSAndrzej Pietrasiewicz 			if (!ncm->port.in_ep->desc ||
91000a2430fSAndrzej Pietrasiewicz 			    !ncm->port.out_ep->desc) {
91100a2430fSAndrzej Pietrasiewicz 				DBG(cdev, "init ncm\n");
91200a2430fSAndrzej Pietrasiewicz 				if (config_ep_by_speed(cdev->gadget, f,
91300a2430fSAndrzej Pietrasiewicz 						       ncm->port.in_ep) ||
91400a2430fSAndrzej Pietrasiewicz 				    config_ep_by_speed(cdev->gadget, f,
91500a2430fSAndrzej Pietrasiewicz 						       ncm->port.out_ep)) {
91600a2430fSAndrzej Pietrasiewicz 					ncm->port.in_ep->desc = NULL;
91700a2430fSAndrzej Pietrasiewicz 					ncm->port.out_ep->desc = NULL;
91800a2430fSAndrzej Pietrasiewicz 					goto fail;
91900a2430fSAndrzej Pietrasiewicz 				}
92000a2430fSAndrzej Pietrasiewicz 			}
92100a2430fSAndrzej Pietrasiewicz 
92200a2430fSAndrzej Pietrasiewicz 			/* TODO */
92300a2430fSAndrzej Pietrasiewicz 			/* Enable zlps by default for NCM conformance;
92400a2430fSAndrzej Pietrasiewicz 			 * override for musb_hdrc (avoids txdma ovhead)
92500a2430fSAndrzej Pietrasiewicz 			 */
9267a896d40SRobert Baldyga 			ncm->port.is_zlp_ok =
9277a896d40SRobert Baldyga 				gadget_is_zlp_supported(cdev->gadget);
928c4824f11SYoshihiro Shimoda 			ncm->port.no_skb_reserve =
929c4824f11SYoshihiro Shimoda 				gadget_avoids_skb_reserve(cdev->gadget);
93000a2430fSAndrzej Pietrasiewicz 			ncm->port.cdc_filter = DEFAULT_FILTER;
93100a2430fSAndrzej Pietrasiewicz 			DBG(cdev, "activate ncm\n");
93200a2430fSAndrzej Pietrasiewicz 			net = gether_connect(&ncm->port);
93300a2430fSAndrzej Pietrasiewicz 			if (IS_ERR(net))
93400a2430fSAndrzej Pietrasiewicz 				return PTR_ERR(net);
93500a2430fSAndrzej Pietrasiewicz 			ncm->netdev = net;
93600a2430fSAndrzej Pietrasiewicz 			ncm->timer_stopping = false;
93700a2430fSAndrzej Pietrasiewicz 		}
93800a2430fSAndrzej Pietrasiewicz 
93900a2430fSAndrzej Pietrasiewicz 		spin_lock(&ncm->lock);
94000a2430fSAndrzej Pietrasiewicz 		ncm_notify(ncm);
94100a2430fSAndrzej Pietrasiewicz 		spin_unlock(&ncm->lock);
94200a2430fSAndrzej Pietrasiewicz 	} else
94300a2430fSAndrzej Pietrasiewicz 		goto fail;
94400a2430fSAndrzej Pietrasiewicz 
94500a2430fSAndrzej Pietrasiewicz 	return 0;
94600a2430fSAndrzej Pietrasiewicz fail:
94700a2430fSAndrzej Pietrasiewicz 	return -EINVAL;
94800a2430fSAndrzej Pietrasiewicz }
94900a2430fSAndrzej Pietrasiewicz 
95000a2430fSAndrzej Pietrasiewicz /*
95100a2430fSAndrzej Pietrasiewicz  * Because the data interface supports multiple altsettings,
95200a2430fSAndrzej Pietrasiewicz  * this NCM function *MUST* implement a get_alt() method.
95300a2430fSAndrzej Pietrasiewicz  */
95400a2430fSAndrzej Pietrasiewicz static int ncm_get_alt(struct usb_function *f, unsigned intf)
95500a2430fSAndrzej Pietrasiewicz {
95600a2430fSAndrzej Pietrasiewicz 	struct f_ncm		*ncm = func_to_ncm(f);
95700a2430fSAndrzej Pietrasiewicz 
95800a2430fSAndrzej Pietrasiewicz 	if (intf == ncm->ctrl_id)
95900a2430fSAndrzej Pietrasiewicz 		return 0;
9606b4012a2SRobert Baldyga 	return ncm->port.in_ep->enabled ? 1 : 0;
96100a2430fSAndrzej Pietrasiewicz }
96200a2430fSAndrzej Pietrasiewicz 
96300a2430fSAndrzej Pietrasiewicz static struct sk_buff *package_for_tx(struct f_ncm *ncm)
96400a2430fSAndrzej Pietrasiewicz {
96500a2430fSAndrzej Pietrasiewicz 	__le16		*ntb_iter;
96600a2430fSAndrzej Pietrasiewicz 	struct sk_buff	*skb2 = NULL;
96700a2430fSAndrzej Pietrasiewicz 	unsigned	ndp_pad;
96800a2430fSAndrzej Pietrasiewicz 	unsigned	ndp_index;
96900a2430fSAndrzej Pietrasiewicz 	unsigned	new_len;
97000a2430fSAndrzej Pietrasiewicz 
97100a2430fSAndrzej Pietrasiewicz 	const struct ndp_parser_opts *opts = ncm->parser_opts;
97200a2430fSAndrzej Pietrasiewicz 	const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment);
97300a2430fSAndrzej Pietrasiewicz 	const int dgram_idx_len = 2 * 2 * opts->dgram_item_len;
97400a2430fSAndrzej Pietrasiewicz 
97500a2430fSAndrzej Pietrasiewicz 	/* Stop the timer */
97600a2430fSAndrzej Pietrasiewicz 	hrtimer_try_to_cancel(&ncm->task_timer);
97700a2430fSAndrzej Pietrasiewicz 
97800a2430fSAndrzej Pietrasiewicz 	ndp_pad = ALIGN(ncm->skb_tx_data->len, ndp_align) -
97900a2430fSAndrzej Pietrasiewicz 			ncm->skb_tx_data->len;
98000a2430fSAndrzej Pietrasiewicz 	ndp_index = ncm->skb_tx_data->len + ndp_pad;
98100a2430fSAndrzej Pietrasiewicz 	new_len = ndp_index + dgram_idx_len + ncm->skb_tx_ndp->len;
98200a2430fSAndrzej Pietrasiewicz 
98300a2430fSAndrzej Pietrasiewicz 	/* Set the final BlockLength and wNdpIndex */
98400a2430fSAndrzej Pietrasiewicz 	ntb_iter = (void *) ncm->skb_tx_data->data;
98500a2430fSAndrzej Pietrasiewicz 	/* Increment pointer to BlockLength */
98600a2430fSAndrzej Pietrasiewicz 	ntb_iter += 2 + 1 + 1;
98700a2430fSAndrzej Pietrasiewicz 	put_ncm(&ntb_iter, opts->block_length, new_len);
98800a2430fSAndrzej Pietrasiewicz 	put_ncm(&ntb_iter, opts->ndp_index, ndp_index);
98900a2430fSAndrzej Pietrasiewicz 
99000a2430fSAndrzej Pietrasiewicz 	/* Set the final NDP wLength */
99100a2430fSAndrzej Pietrasiewicz 	new_len = opts->ndp_size +
99200a2430fSAndrzej Pietrasiewicz 			(ncm->ndp_dgram_count * dgram_idx_len);
99300a2430fSAndrzej Pietrasiewicz 	ncm->ndp_dgram_count = 0;
99400a2430fSAndrzej Pietrasiewicz 	/* Increment from start to wLength */
99500a2430fSAndrzej Pietrasiewicz 	ntb_iter = (void *) ncm->skb_tx_ndp->data;
99600a2430fSAndrzej Pietrasiewicz 	ntb_iter += 2;
99700a2430fSAndrzej Pietrasiewicz 	put_unaligned_le16(new_len, ntb_iter);
99800a2430fSAndrzej Pietrasiewicz 
99900a2430fSAndrzej Pietrasiewicz 	/* Merge the skbs */
100000a2430fSAndrzej Pietrasiewicz 	swap(skb2, ncm->skb_tx_data);
100100a2430fSAndrzej Pietrasiewicz 	if (ncm->skb_tx_data) {
100238314e59STorsten Polle 		dev_consume_skb_any(ncm->skb_tx_data);
100300a2430fSAndrzej Pietrasiewicz 		ncm->skb_tx_data = NULL;
100400a2430fSAndrzej Pietrasiewicz 	}
100500a2430fSAndrzej Pietrasiewicz 
100600a2430fSAndrzej Pietrasiewicz 	/* Insert NDP alignment. */
1007b080db58SJohannes Berg 	ntb_iter = skb_put_zero(skb2, ndp_pad);
100800a2430fSAndrzej Pietrasiewicz 
100900a2430fSAndrzej Pietrasiewicz 	/* Copy NTB across. */
101059ae1d12SJohannes Berg 	ntb_iter = skb_put_data(skb2, ncm->skb_tx_ndp->data,
101159ae1d12SJohannes Berg 				ncm->skb_tx_ndp->len);
101238314e59STorsten Polle 	dev_consume_skb_any(ncm->skb_tx_ndp);
101300a2430fSAndrzej Pietrasiewicz 	ncm->skb_tx_ndp = NULL;
101400a2430fSAndrzej Pietrasiewicz 
101500a2430fSAndrzej Pietrasiewicz 	/* Insert zero'd datagram. */
1016b080db58SJohannes Berg 	ntb_iter = skb_put_zero(skb2, dgram_idx_len);
101700a2430fSAndrzej Pietrasiewicz 
101800a2430fSAndrzej Pietrasiewicz 	return skb2;
101900a2430fSAndrzej Pietrasiewicz }
102000a2430fSAndrzej Pietrasiewicz 
102100a2430fSAndrzej Pietrasiewicz static struct sk_buff *ncm_wrap_ntb(struct gether *port,
102200a2430fSAndrzej Pietrasiewicz 				    struct sk_buff *skb)
102300a2430fSAndrzej Pietrasiewicz {
102400a2430fSAndrzej Pietrasiewicz 	struct f_ncm	*ncm = func_to_ncm(&port->func);
102500a2430fSAndrzej Pietrasiewicz 	struct sk_buff	*skb2 = NULL;
102600a2430fSAndrzej Pietrasiewicz 	int		ncb_len = 0;
102700a2430fSAndrzej Pietrasiewicz 	__le16		*ntb_data;
102800a2430fSAndrzej Pietrasiewicz 	__le16		*ntb_ndp;
102900a2430fSAndrzej Pietrasiewicz 	int		dgram_pad;
103000a2430fSAndrzej Pietrasiewicz 
103100a2430fSAndrzej Pietrasiewicz 	unsigned	max_size = ncm->port.fixed_in_len;
103200a2430fSAndrzej Pietrasiewicz 	const struct ndp_parser_opts *opts = ncm->parser_opts;
103300a2430fSAndrzej Pietrasiewicz 	const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment);
103400a2430fSAndrzej Pietrasiewicz 	const int div = le16_to_cpu(ntb_parameters.wNdpInDivisor);
103500a2430fSAndrzej Pietrasiewicz 	const int rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder);
103600a2430fSAndrzej Pietrasiewicz 	const int dgram_idx_len = 2 * 2 * opts->dgram_item_len;
103700a2430fSAndrzej Pietrasiewicz 
103800a2430fSAndrzej Pietrasiewicz 	if (!skb && !ncm->skb_tx_data)
103900a2430fSAndrzej Pietrasiewicz 		return NULL;
104000a2430fSAndrzej Pietrasiewicz 
104100a2430fSAndrzej Pietrasiewicz 	if (skb) {
104200a2430fSAndrzej Pietrasiewicz 		/* Add the CRC if required up front */
104300a2430fSAndrzej Pietrasiewicz 		if (ncm->is_crc) {
104400a2430fSAndrzej Pietrasiewicz 			uint32_t	crc;
104500a2430fSAndrzej Pietrasiewicz 			__le16		*crc_pos;
104600a2430fSAndrzej Pietrasiewicz 
104700a2430fSAndrzej Pietrasiewicz 			crc = ~crc32_le(~0,
104800a2430fSAndrzej Pietrasiewicz 					skb->data,
104900a2430fSAndrzej Pietrasiewicz 					skb->len);
1050*4df864c1SJohannes Berg 			crc_pos = skb_put(skb, sizeof(uint32_t));
105100a2430fSAndrzej Pietrasiewicz 			put_unaligned_le32(crc, crc_pos);
105200a2430fSAndrzej Pietrasiewicz 		}
105300a2430fSAndrzej Pietrasiewicz 
105400a2430fSAndrzej Pietrasiewicz 		/* If the new skb is too big for the current NCM NTB then
105500a2430fSAndrzej Pietrasiewicz 		 * set the current stored skb to be sent now and clear it
105600a2430fSAndrzej Pietrasiewicz 		 * ready for new data.
105700a2430fSAndrzej Pietrasiewicz 		 * NOTE: Assume maximum align for speed of calculation.
105800a2430fSAndrzej Pietrasiewicz 		 */
105900a2430fSAndrzej Pietrasiewicz 		if (ncm->skb_tx_data
106000a2430fSAndrzej Pietrasiewicz 		    && (ncm->ndp_dgram_count >= TX_MAX_NUM_DPE
106100a2430fSAndrzej Pietrasiewicz 		    || (ncm->skb_tx_data->len +
106200a2430fSAndrzej Pietrasiewicz 		    div + rem + skb->len +
106300a2430fSAndrzej Pietrasiewicz 		    ncm->skb_tx_ndp->len + ndp_align + (2 * dgram_idx_len))
106400a2430fSAndrzej Pietrasiewicz 		    > max_size)) {
106500a2430fSAndrzej Pietrasiewicz 			skb2 = package_for_tx(ncm);
106600a2430fSAndrzej Pietrasiewicz 			if (!skb2)
106700a2430fSAndrzej Pietrasiewicz 				goto err;
106800a2430fSAndrzej Pietrasiewicz 		}
106900a2430fSAndrzej Pietrasiewicz 
107000a2430fSAndrzej Pietrasiewicz 		if (!ncm->skb_tx_data) {
107100a2430fSAndrzej Pietrasiewicz 			ncb_len = opts->nth_size;
107200a2430fSAndrzej Pietrasiewicz 			dgram_pad = ALIGN(ncb_len, div) + rem - ncb_len;
107300a2430fSAndrzej Pietrasiewicz 			ncb_len += dgram_pad;
107400a2430fSAndrzej Pietrasiewicz 
107500a2430fSAndrzej Pietrasiewicz 			/* Create a new skb for the NTH and datagrams. */
107600a2430fSAndrzej Pietrasiewicz 			ncm->skb_tx_data = alloc_skb(max_size, GFP_ATOMIC);
107700a2430fSAndrzej Pietrasiewicz 			if (!ncm->skb_tx_data)
107800a2430fSAndrzej Pietrasiewicz 				goto err;
107900a2430fSAndrzej Pietrasiewicz 
10809a5380c3STorsten Polle 			ncm->skb_tx_data->dev = ncm->netdev;
1081b080db58SJohannes Berg 			ntb_data = skb_put_zero(ncm->skb_tx_data, ncb_len);
108200a2430fSAndrzej Pietrasiewicz 			/* dwSignature */
108300a2430fSAndrzej Pietrasiewicz 			put_unaligned_le32(opts->nth_sign, ntb_data);
108400a2430fSAndrzej Pietrasiewicz 			ntb_data += 2;
108500a2430fSAndrzej Pietrasiewicz 			/* wHeaderLength */
108600a2430fSAndrzej Pietrasiewicz 			put_unaligned_le16(opts->nth_size, ntb_data++);
108700a2430fSAndrzej Pietrasiewicz 
108800a2430fSAndrzej Pietrasiewicz 			/* Allocate an skb for storing the NDP,
108900a2430fSAndrzej Pietrasiewicz 			 * TX_MAX_NUM_DPE should easily suffice for a
109000a2430fSAndrzej Pietrasiewicz 			 * 16k packet.
109100a2430fSAndrzej Pietrasiewicz 			 */
109200a2430fSAndrzej Pietrasiewicz 			ncm->skb_tx_ndp = alloc_skb((int)(opts->ndp_size
109300a2430fSAndrzej Pietrasiewicz 						    + opts->dpe_size
109400a2430fSAndrzej Pietrasiewicz 						    * TX_MAX_NUM_DPE),
109500a2430fSAndrzej Pietrasiewicz 						    GFP_ATOMIC);
109600a2430fSAndrzej Pietrasiewicz 			if (!ncm->skb_tx_ndp)
109700a2430fSAndrzej Pietrasiewicz 				goto err;
10989a5380c3STorsten Polle 
10999a5380c3STorsten Polle 			ncm->skb_tx_ndp->dev = ncm->netdev;
1100*4df864c1SJohannes Berg 			ntb_ndp = skb_put(ncm->skb_tx_ndp, opts->ndp_size);
110100a2430fSAndrzej Pietrasiewicz 			memset(ntb_ndp, 0, ncb_len);
110200a2430fSAndrzej Pietrasiewicz 			/* dwSignature */
110300a2430fSAndrzej Pietrasiewicz 			put_unaligned_le32(ncm->ndp_sign, ntb_ndp);
110400a2430fSAndrzej Pietrasiewicz 			ntb_ndp += 2;
110500a2430fSAndrzej Pietrasiewicz 
110600a2430fSAndrzej Pietrasiewicz 			/* There is always a zeroed entry */
110700a2430fSAndrzej Pietrasiewicz 			ncm->ndp_dgram_count = 1;
110800a2430fSAndrzej Pietrasiewicz 
110900a2430fSAndrzej Pietrasiewicz 			/* Note: we skip opts->next_ndp_index */
111000a2430fSAndrzej Pietrasiewicz 		}
111100a2430fSAndrzej Pietrasiewicz 
111200a2430fSAndrzej Pietrasiewicz 		/* Delay the timer. */
11138b0e1953SThomas Gleixner 		hrtimer_start(&ncm->task_timer, TX_TIMEOUT_NSECS,
111400a2430fSAndrzej Pietrasiewicz 			      HRTIMER_MODE_REL);
111500a2430fSAndrzej Pietrasiewicz 
111600a2430fSAndrzej Pietrasiewicz 		/* Add the datagram position entries */
1117b080db58SJohannes Berg 		ntb_ndp = skb_put_zero(ncm->skb_tx_ndp, dgram_idx_len);
111800a2430fSAndrzej Pietrasiewicz 
111900a2430fSAndrzej Pietrasiewicz 		ncb_len = ncm->skb_tx_data->len;
112000a2430fSAndrzej Pietrasiewicz 		dgram_pad = ALIGN(ncb_len, div) + rem - ncb_len;
112100a2430fSAndrzej Pietrasiewicz 		ncb_len += dgram_pad;
112200a2430fSAndrzej Pietrasiewicz 
112300a2430fSAndrzej Pietrasiewicz 		/* (d)wDatagramIndex */
112400a2430fSAndrzej Pietrasiewicz 		put_ncm(&ntb_ndp, opts->dgram_item_len, ncb_len);
112500a2430fSAndrzej Pietrasiewicz 		/* (d)wDatagramLength */
112600a2430fSAndrzej Pietrasiewicz 		put_ncm(&ntb_ndp, opts->dgram_item_len, skb->len);
112700a2430fSAndrzej Pietrasiewicz 		ncm->ndp_dgram_count++;
112800a2430fSAndrzej Pietrasiewicz 
112900a2430fSAndrzej Pietrasiewicz 		/* Add the new data to the skb */
1130b080db58SJohannes Berg 		ntb_data = skb_put_zero(ncm->skb_tx_data, dgram_pad);
113159ae1d12SJohannes Berg 		ntb_data = skb_put_data(ncm->skb_tx_data, skb->data, skb->len);
113238314e59STorsten Polle 		dev_consume_skb_any(skb);
113300a2430fSAndrzej Pietrasiewicz 		skb = NULL;
113400a2430fSAndrzej Pietrasiewicz 
113500a2430fSAndrzej Pietrasiewicz 	} else if (ncm->skb_tx_data && ncm->timer_force_tx) {
113600a2430fSAndrzej Pietrasiewicz 		/* If the tx was requested because of a timeout then send */
113700a2430fSAndrzej Pietrasiewicz 		skb2 = package_for_tx(ncm);
113800a2430fSAndrzej Pietrasiewicz 		if (!skb2)
113900a2430fSAndrzej Pietrasiewicz 			goto err;
114000a2430fSAndrzej Pietrasiewicz 	}
114100a2430fSAndrzej Pietrasiewicz 
114200a2430fSAndrzej Pietrasiewicz 	return skb2;
114300a2430fSAndrzej Pietrasiewicz 
114400a2430fSAndrzej Pietrasiewicz err:
114500a2430fSAndrzej Pietrasiewicz 	ncm->netdev->stats.tx_dropped++;
114600a2430fSAndrzej Pietrasiewicz 
114700a2430fSAndrzej Pietrasiewicz 	if (skb)
114800a2430fSAndrzej Pietrasiewicz 		dev_kfree_skb_any(skb);
114900a2430fSAndrzej Pietrasiewicz 	if (ncm->skb_tx_data)
115000a2430fSAndrzej Pietrasiewicz 		dev_kfree_skb_any(ncm->skb_tx_data);
115100a2430fSAndrzej Pietrasiewicz 	if (ncm->skb_tx_ndp)
115200a2430fSAndrzej Pietrasiewicz 		dev_kfree_skb_any(ncm->skb_tx_ndp);
115300a2430fSAndrzej Pietrasiewicz 
115400a2430fSAndrzej Pietrasiewicz 	return NULL;
115500a2430fSAndrzej Pietrasiewicz }
115600a2430fSAndrzej Pietrasiewicz 
115700a2430fSAndrzej Pietrasiewicz /*
115800a2430fSAndrzej Pietrasiewicz  * This transmits the NTB if there are frames waiting.
115900a2430fSAndrzej Pietrasiewicz  */
116000a2430fSAndrzej Pietrasiewicz static void ncm_tx_tasklet(unsigned long data)
116100a2430fSAndrzej Pietrasiewicz {
116200a2430fSAndrzej Pietrasiewicz 	struct f_ncm	*ncm = (void *)data;
116300a2430fSAndrzej Pietrasiewicz 
116400a2430fSAndrzej Pietrasiewicz 	if (ncm->timer_stopping)
116500a2430fSAndrzej Pietrasiewicz 		return;
116600a2430fSAndrzej Pietrasiewicz 
116700a2430fSAndrzej Pietrasiewicz 	/* Only send if data is available. */
116800a2430fSAndrzej Pietrasiewicz 	if (ncm->skb_tx_data) {
116900a2430fSAndrzej Pietrasiewicz 		ncm->timer_force_tx = true;
1170c2c0e8b2SDavid S. Miller 
1171c2c0e8b2SDavid S. Miller 		/* XXX This allowance of a NULL skb argument to ndo_start_xmit
1172c2c0e8b2SDavid S. Miller 		 * XXX is not sane.  The gadget layer should be redesigned so
1173c2c0e8b2SDavid S. Miller 		 * XXX that the dev->wrap() invocations to build SKBs is transparent
1174c2c0e8b2SDavid S. Miller 		 * XXX and performed in some way outside of the ndo_start_xmit
1175c2c0e8b2SDavid S. Miller 		 * XXX interface.
1176c2c0e8b2SDavid S. Miller 		 */
1177c2c0e8b2SDavid S. Miller 		ncm->netdev->netdev_ops->ndo_start_xmit(NULL, ncm->netdev);
1178c2c0e8b2SDavid S. Miller 
117900a2430fSAndrzej Pietrasiewicz 		ncm->timer_force_tx = false;
118000a2430fSAndrzej Pietrasiewicz 	}
118100a2430fSAndrzej Pietrasiewicz }
118200a2430fSAndrzej Pietrasiewicz 
118300a2430fSAndrzej Pietrasiewicz /*
118400a2430fSAndrzej Pietrasiewicz  * The transmit should only be run if no skb data has been sent
118500a2430fSAndrzej Pietrasiewicz  * for a certain duration.
118600a2430fSAndrzej Pietrasiewicz  */
118700a2430fSAndrzej Pietrasiewicz static enum hrtimer_restart ncm_tx_timeout(struct hrtimer *data)
118800a2430fSAndrzej Pietrasiewicz {
118900a2430fSAndrzej Pietrasiewicz 	struct f_ncm *ncm = container_of(data, struct f_ncm, task_timer);
119000a2430fSAndrzej Pietrasiewicz 	tasklet_schedule(&ncm->tx_tasklet);
119100a2430fSAndrzej Pietrasiewicz 	return HRTIMER_NORESTART;
119200a2430fSAndrzej Pietrasiewicz }
119300a2430fSAndrzej Pietrasiewicz 
119400a2430fSAndrzej Pietrasiewicz static int ncm_unwrap_ntb(struct gether *port,
119500a2430fSAndrzej Pietrasiewicz 			  struct sk_buff *skb,
119600a2430fSAndrzej Pietrasiewicz 			  struct sk_buff_head *list)
119700a2430fSAndrzej Pietrasiewicz {
119800a2430fSAndrzej Pietrasiewicz 	struct f_ncm	*ncm = func_to_ncm(&port->func);
119900a2430fSAndrzej Pietrasiewicz 	__le16		*tmp = (void *) skb->data;
120000a2430fSAndrzej Pietrasiewicz 	unsigned	index, index2;
120100a2430fSAndrzej Pietrasiewicz 	int		ndp_index;
120200a2430fSAndrzej Pietrasiewicz 	unsigned	dg_len, dg_len2;
120300a2430fSAndrzej Pietrasiewicz 	unsigned	ndp_len;
120400a2430fSAndrzej Pietrasiewicz 	struct sk_buff	*skb2;
120500a2430fSAndrzej Pietrasiewicz 	int		ret = -EINVAL;
120600a2430fSAndrzej Pietrasiewicz 	unsigned	max_size = le32_to_cpu(ntb_parameters.dwNtbOutMaxSize);
120700a2430fSAndrzej Pietrasiewicz 	const struct ndp_parser_opts *opts = ncm->parser_opts;
120800a2430fSAndrzej Pietrasiewicz 	unsigned	crc_len = ncm->is_crc ? sizeof(uint32_t) : 0;
120900a2430fSAndrzej Pietrasiewicz 	int		dgram_counter;
121000a2430fSAndrzej Pietrasiewicz 
121100a2430fSAndrzej Pietrasiewicz 	/* dwSignature */
121200a2430fSAndrzej Pietrasiewicz 	if (get_unaligned_le32(tmp) != opts->nth_sign) {
121300a2430fSAndrzej Pietrasiewicz 		INFO(port->func.config->cdev, "Wrong NTH SIGN, skblen %d\n",
121400a2430fSAndrzej Pietrasiewicz 			skb->len);
121500a2430fSAndrzej Pietrasiewicz 		print_hex_dump(KERN_INFO, "HEAD:", DUMP_PREFIX_ADDRESS, 32, 1,
121600a2430fSAndrzej Pietrasiewicz 			       skb->data, 32, false);
121700a2430fSAndrzej Pietrasiewicz 
121800a2430fSAndrzej Pietrasiewicz 		goto err;
121900a2430fSAndrzej Pietrasiewicz 	}
122000a2430fSAndrzej Pietrasiewicz 	tmp += 2;
122100a2430fSAndrzej Pietrasiewicz 	/* wHeaderLength */
122200a2430fSAndrzej Pietrasiewicz 	if (get_unaligned_le16(tmp++) != opts->nth_size) {
122300a2430fSAndrzej Pietrasiewicz 		INFO(port->func.config->cdev, "Wrong NTB headersize\n");
122400a2430fSAndrzej Pietrasiewicz 		goto err;
122500a2430fSAndrzej Pietrasiewicz 	}
122600a2430fSAndrzej Pietrasiewicz 	tmp++; /* skip wSequence */
122700a2430fSAndrzej Pietrasiewicz 
122800a2430fSAndrzej Pietrasiewicz 	/* (d)wBlockLength */
122900a2430fSAndrzej Pietrasiewicz 	if (get_ncm(&tmp, opts->block_length) > max_size) {
123000a2430fSAndrzej Pietrasiewicz 		INFO(port->func.config->cdev, "OUT size exceeded\n");
123100a2430fSAndrzej Pietrasiewicz 		goto err;
123200a2430fSAndrzej Pietrasiewicz 	}
123300a2430fSAndrzej Pietrasiewicz 
123400a2430fSAndrzej Pietrasiewicz 	ndp_index = get_ncm(&tmp, opts->ndp_index);
123500a2430fSAndrzej Pietrasiewicz 
123600a2430fSAndrzej Pietrasiewicz 	/* Run through all the NDP's in the NTB */
123700a2430fSAndrzej Pietrasiewicz 	do {
123800a2430fSAndrzej Pietrasiewicz 		/* NCM 3.2 */
123900a2430fSAndrzej Pietrasiewicz 		if (((ndp_index % 4) != 0) &&
124000a2430fSAndrzej Pietrasiewicz 				(ndp_index < opts->nth_size)) {
124100a2430fSAndrzej Pietrasiewicz 			INFO(port->func.config->cdev, "Bad index: %#X\n",
124200a2430fSAndrzej Pietrasiewicz 			     ndp_index);
124300a2430fSAndrzej Pietrasiewicz 			goto err;
124400a2430fSAndrzej Pietrasiewicz 		}
124500a2430fSAndrzej Pietrasiewicz 
124600a2430fSAndrzej Pietrasiewicz 		/* walk through NDP */
124700a2430fSAndrzej Pietrasiewicz 		tmp = (void *)(skb->data + ndp_index);
124800a2430fSAndrzej Pietrasiewicz 		if (get_unaligned_le32(tmp) != ncm->ndp_sign) {
124900a2430fSAndrzej Pietrasiewicz 			INFO(port->func.config->cdev, "Wrong NDP SIGN\n");
125000a2430fSAndrzej Pietrasiewicz 			goto err;
125100a2430fSAndrzej Pietrasiewicz 		}
125200a2430fSAndrzej Pietrasiewicz 		tmp += 2;
125300a2430fSAndrzej Pietrasiewicz 
125400a2430fSAndrzej Pietrasiewicz 		ndp_len = get_unaligned_le16(tmp++);
125500a2430fSAndrzej Pietrasiewicz 		/*
125600a2430fSAndrzej Pietrasiewicz 		 * NCM 3.3.1
125700a2430fSAndrzej Pietrasiewicz 		 * entry is 2 items
125800a2430fSAndrzej Pietrasiewicz 		 * item size is 16/32 bits, opts->dgram_item_len * 2 bytes
125900a2430fSAndrzej Pietrasiewicz 		 * minimal: struct usb_cdc_ncm_ndpX + normal entry + zero entry
126000a2430fSAndrzej Pietrasiewicz 		 * Each entry is a dgram index and a dgram length.
126100a2430fSAndrzej Pietrasiewicz 		 */
126200a2430fSAndrzej Pietrasiewicz 		if ((ndp_len < opts->ndp_size
126300a2430fSAndrzej Pietrasiewicz 				+ 2 * 2 * (opts->dgram_item_len * 2))
126400a2430fSAndrzej Pietrasiewicz 				|| (ndp_len % opts->ndplen_align != 0)) {
126500a2430fSAndrzej Pietrasiewicz 			INFO(port->func.config->cdev, "Bad NDP length: %#X\n",
126600a2430fSAndrzej Pietrasiewicz 			     ndp_len);
126700a2430fSAndrzej Pietrasiewicz 			goto err;
126800a2430fSAndrzej Pietrasiewicz 		}
126900a2430fSAndrzej Pietrasiewicz 		tmp += opts->reserved1;
127000a2430fSAndrzej Pietrasiewicz 		/* Check for another NDP (d)wNextNdpIndex */
127100a2430fSAndrzej Pietrasiewicz 		ndp_index = get_ncm(&tmp, opts->next_ndp_index);
127200a2430fSAndrzej Pietrasiewicz 		tmp += opts->reserved2;
127300a2430fSAndrzej Pietrasiewicz 
127400a2430fSAndrzej Pietrasiewicz 		ndp_len -= opts->ndp_size;
127500a2430fSAndrzej Pietrasiewicz 		index2 = get_ncm(&tmp, opts->dgram_item_len);
127600a2430fSAndrzej Pietrasiewicz 		dg_len2 = get_ncm(&tmp, opts->dgram_item_len);
127700a2430fSAndrzej Pietrasiewicz 		dgram_counter = 0;
127800a2430fSAndrzej Pietrasiewicz 
127900a2430fSAndrzej Pietrasiewicz 		do {
128000a2430fSAndrzej Pietrasiewicz 			index = index2;
128100a2430fSAndrzej Pietrasiewicz 			dg_len = dg_len2;
128200a2430fSAndrzej Pietrasiewicz 			if (dg_len < 14 + crc_len) { /* ethernet hdr + crc */
128300a2430fSAndrzej Pietrasiewicz 				INFO(port->func.config->cdev,
128400a2430fSAndrzej Pietrasiewicz 				     "Bad dgram length: %#X\n", dg_len);
128500a2430fSAndrzej Pietrasiewicz 				goto err;
128600a2430fSAndrzej Pietrasiewicz 			}
128700a2430fSAndrzej Pietrasiewicz 			if (ncm->is_crc) {
128800a2430fSAndrzej Pietrasiewicz 				uint32_t crc, crc2;
128900a2430fSAndrzej Pietrasiewicz 
129000a2430fSAndrzej Pietrasiewicz 				crc = get_unaligned_le32(skb->data +
129100a2430fSAndrzej Pietrasiewicz 							 index + dg_len -
129200a2430fSAndrzej Pietrasiewicz 							 crc_len);
129300a2430fSAndrzej Pietrasiewicz 				crc2 = ~crc32_le(~0,
129400a2430fSAndrzej Pietrasiewicz 						 skb->data + index,
129500a2430fSAndrzej Pietrasiewicz 						 dg_len - crc_len);
129600a2430fSAndrzej Pietrasiewicz 				if (crc != crc2) {
129700a2430fSAndrzej Pietrasiewicz 					INFO(port->func.config->cdev,
129800a2430fSAndrzej Pietrasiewicz 					     "Bad CRC\n");
129900a2430fSAndrzej Pietrasiewicz 					goto err;
130000a2430fSAndrzej Pietrasiewicz 				}
130100a2430fSAndrzej Pietrasiewicz 			}
130200a2430fSAndrzej Pietrasiewicz 
130300a2430fSAndrzej Pietrasiewicz 			index2 = get_ncm(&tmp, opts->dgram_item_len);
130400a2430fSAndrzej Pietrasiewicz 			dg_len2 = get_ncm(&tmp, opts->dgram_item_len);
130500a2430fSAndrzej Pietrasiewicz 
130600a2430fSAndrzej Pietrasiewicz 			/*
130700a2430fSAndrzej Pietrasiewicz 			 * Copy the data into a new skb.
130800a2430fSAndrzej Pietrasiewicz 			 * This ensures the truesize is correct
130900a2430fSAndrzej Pietrasiewicz 			 */
131000a2430fSAndrzej Pietrasiewicz 			skb2 = netdev_alloc_skb_ip_align(ncm->netdev,
131100a2430fSAndrzej Pietrasiewicz 							 dg_len - crc_len);
131200a2430fSAndrzej Pietrasiewicz 			if (skb2 == NULL)
131300a2430fSAndrzej Pietrasiewicz 				goto err;
131459ae1d12SJohannes Berg 			skb_put_data(skb2, skb->data + index,
131559ae1d12SJohannes Berg 				     dg_len - crc_len);
131600a2430fSAndrzej Pietrasiewicz 
131700a2430fSAndrzej Pietrasiewicz 			skb_queue_tail(list, skb2);
131800a2430fSAndrzej Pietrasiewicz 
131900a2430fSAndrzej Pietrasiewicz 			ndp_len -= 2 * (opts->dgram_item_len * 2);
132000a2430fSAndrzej Pietrasiewicz 
132100a2430fSAndrzej Pietrasiewicz 			dgram_counter++;
132200a2430fSAndrzej Pietrasiewicz 
132300a2430fSAndrzej Pietrasiewicz 			if (index2 == 0 || dg_len2 == 0)
132400a2430fSAndrzej Pietrasiewicz 				break;
132500a2430fSAndrzej Pietrasiewicz 		} while (ndp_len > 2 * (opts->dgram_item_len * 2));
132600a2430fSAndrzej Pietrasiewicz 	} while (ndp_index);
132700a2430fSAndrzej Pietrasiewicz 
132838314e59STorsten Polle 	dev_consume_skb_any(skb);
132900a2430fSAndrzej Pietrasiewicz 
133000a2430fSAndrzej Pietrasiewicz 	VDBG(port->func.config->cdev,
133100a2430fSAndrzej Pietrasiewicz 	     "Parsed NTB with %d frames\n", dgram_counter);
133200a2430fSAndrzej Pietrasiewicz 	return 0;
133300a2430fSAndrzej Pietrasiewicz err:
133400a2430fSAndrzej Pietrasiewicz 	skb_queue_purge(list);
133500a2430fSAndrzej Pietrasiewicz 	dev_kfree_skb_any(skb);
133600a2430fSAndrzej Pietrasiewicz 	return ret;
133700a2430fSAndrzej Pietrasiewicz }
133800a2430fSAndrzej Pietrasiewicz 
133900a2430fSAndrzej Pietrasiewicz static void ncm_disable(struct usb_function *f)
134000a2430fSAndrzej Pietrasiewicz {
134100a2430fSAndrzej Pietrasiewicz 	struct f_ncm		*ncm = func_to_ncm(f);
134200a2430fSAndrzej Pietrasiewicz 	struct usb_composite_dev *cdev = f->config->cdev;
134300a2430fSAndrzej Pietrasiewicz 
134400a2430fSAndrzej Pietrasiewicz 	DBG(cdev, "ncm deactivated\n");
134500a2430fSAndrzej Pietrasiewicz 
13466b4012a2SRobert Baldyga 	if (ncm->port.in_ep->enabled) {
134700a2430fSAndrzej Pietrasiewicz 		ncm->timer_stopping = true;
134800a2430fSAndrzej Pietrasiewicz 		ncm->netdev = NULL;
134900a2430fSAndrzej Pietrasiewicz 		gether_disconnect(&ncm->port);
135000a2430fSAndrzej Pietrasiewicz 	}
135100a2430fSAndrzej Pietrasiewicz 
13526b4012a2SRobert Baldyga 	if (ncm->notify->enabled) {
135300a2430fSAndrzej Pietrasiewicz 		usb_ep_disable(ncm->notify);
135400a2430fSAndrzej Pietrasiewicz 		ncm->notify->desc = NULL;
135500a2430fSAndrzej Pietrasiewicz 	}
135600a2430fSAndrzej Pietrasiewicz }
135700a2430fSAndrzej Pietrasiewicz 
135800a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
135900a2430fSAndrzej Pietrasiewicz 
136000a2430fSAndrzej Pietrasiewicz /*
136100a2430fSAndrzej Pietrasiewicz  * Callbacks let us notify the host about connect/disconnect when the
136200a2430fSAndrzej Pietrasiewicz  * net device is opened or closed.
136300a2430fSAndrzej Pietrasiewicz  *
136400a2430fSAndrzej Pietrasiewicz  * For testing, note that link states on this side include both opened
136500a2430fSAndrzej Pietrasiewicz  * and closed variants of:
136600a2430fSAndrzej Pietrasiewicz  *
136700a2430fSAndrzej Pietrasiewicz  *   - disconnected/unconfigured
136800a2430fSAndrzej Pietrasiewicz  *   - configured but inactive (data alt 0)
136900a2430fSAndrzej Pietrasiewicz  *   - configured and active (data alt 1)
137000a2430fSAndrzej Pietrasiewicz  *
137100a2430fSAndrzej Pietrasiewicz  * Each needs to be tested with unplug, rmmod, SET_CONFIGURATION, and
137200a2430fSAndrzej Pietrasiewicz  * SET_INTERFACE (altsetting).  Remember also that "configured" doesn't
137300a2430fSAndrzej Pietrasiewicz  * imply the host is actually polling the notification endpoint, and
137400a2430fSAndrzej Pietrasiewicz  * likewise that "active" doesn't imply it's actually using the data
137500a2430fSAndrzej Pietrasiewicz  * endpoints for traffic.
137600a2430fSAndrzej Pietrasiewicz  */
137700a2430fSAndrzej Pietrasiewicz 
137800a2430fSAndrzej Pietrasiewicz static void ncm_open(struct gether *geth)
137900a2430fSAndrzej Pietrasiewicz {
138000a2430fSAndrzej Pietrasiewicz 	struct f_ncm		*ncm = func_to_ncm(&geth->func);
138100a2430fSAndrzej Pietrasiewicz 
138200a2430fSAndrzej Pietrasiewicz 	DBG(ncm->port.func.config->cdev, "%s\n", __func__);
138300a2430fSAndrzej Pietrasiewicz 
138400a2430fSAndrzej Pietrasiewicz 	spin_lock(&ncm->lock);
138500a2430fSAndrzej Pietrasiewicz 	ncm->is_open = true;
138600a2430fSAndrzej Pietrasiewicz 	ncm_notify(ncm);
138700a2430fSAndrzej Pietrasiewicz 	spin_unlock(&ncm->lock);
138800a2430fSAndrzej Pietrasiewicz }
138900a2430fSAndrzej Pietrasiewicz 
139000a2430fSAndrzej Pietrasiewicz static void ncm_close(struct gether *geth)
139100a2430fSAndrzej Pietrasiewicz {
139200a2430fSAndrzej Pietrasiewicz 	struct f_ncm		*ncm = func_to_ncm(&geth->func);
139300a2430fSAndrzej Pietrasiewicz 
139400a2430fSAndrzej Pietrasiewicz 	DBG(ncm->port.func.config->cdev, "%s\n", __func__);
139500a2430fSAndrzej Pietrasiewicz 
139600a2430fSAndrzej Pietrasiewicz 	spin_lock(&ncm->lock);
139700a2430fSAndrzej Pietrasiewicz 	ncm->is_open = false;
139800a2430fSAndrzej Pietrasiewicz 	ncm_notify(ncm);
139900a2430fSAndrzej Pietrasiewicz 	spin_unlock(&ncm->lock);
140000a2430fSAndrzej Pietrasiewicz }
140100a2430fSAndrzej Pietrasiewicz 
140200a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
140300a2430fSAndrzej Pietrasiewicz 
140400a2430fSAndrzej Pietrasiewicz /* ethernet function driver setup/binding */
140500a2430fSAndrzej Pietrasiewicz 
140600a2430fSAndrzej Pietrasiewicz static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
140700a2430fSAndrzej Pietrasiewicz {
140800a2430fSAndrzej Pietrasiewicz 	struct usb_composite_dev *cdev = c->cdev;
140900a2430fSAndrzej Pietrasiewicz 	struct f_ncm		*ncm = func_to_ncm(f);
141000a2430fSAndrzej Pietrasiewicz 	struct usb_string	*us;
141100a2430fSAndrzej Pietrasiewicz 	int			status;
141200a2430fSAndrzej Pietrasiewicz 	struct usb_ep		*ep;
141300a2430fSAndrzej Pietrasiewicz 	struct f_ncm_opts	*ncm_opts;
141400a2430fSAndrzej Pietrasiewicz 
141500a2430fSAndrzej Pietrasiewicz 	if (!can_support_ecm(cdev->gadget))
141600a2430fSAndrzej Pietrasiewicz 		return -EINVAL;
141700a2430fSAndrzej Pietrasiewicz 
141800a2430fSAndrzej Pietrasiewicz 	ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst);
141900a2430fSAndrzej Pietrasiewicz 	/*
142000a2430fSAndrzej Pietrasiewicz 	 * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
142100a2430fSAndrzej Pietrasiewicz 	 * configurations are bound in sequence with list_for_each_entry,
142200a2430fSAndrzej Pietrasiewicz 	 * in each configuration its functions are bound in sequence
142300a2430fSAndrzej Pietrasiewicz 	 * with list_for_each_entry, so we assume no race condition
142400a2430fSAndrzej Pietrasiewicz 	 * with regard to ncm_opts->bound access
142500a2430fSAndrzej Pietrasiewicz 	 */
142600a2430fSAndrzej Pietrasiewicz 	if (!ncm_opts->bound) {
142700a2430fSAndrzej Pietrasiewicz 		mutex_lock(&ncm_opts->lock);
142800a2430fSAndrzej Pietrasiewicz 		gether_set_gadget(ncm_opts->net, cdev->gadget);
142900a2430fSAndrzej Pietrasiewicz 		status = gether_register_netdev(ncm_opts->net);
143000a2430fSAndrzej Pietrasiewicz 		mutex_unlock(&ncm_opts->lock);
143100a2430fSAndrzej Pietrasiewicz 		if (status)
143200a2430fSAndrzej Pietrasiewicz 			return status;
143300a2430fSAndrzej Pietrasiewicz 		ncm_opts->bound = true;
143400a2430fSAndrzej Pietrasiewicz 	}
143500a2430fSAndrzej Pietrasiewicz 	us = usb_gstrings_attach(cdev, ncm_strings,
143600a2430fSAndrzej Pietrasiewicz 				 ARRAY_SIZE(ncm_string_defs));
143700a2430fSAndrzej Pietrasiewicz 	if (IS_ERR(us))
143800a2430fSAndrzej Pietrasiewicz 		return PTR_ERR(us);
143900a2430fSAndrzej Pietrasiewicz 	ncm_control_intf.iInterface = us[STRING_CTRL_IDX].id;
144000a2430fSAndrzej Pietrasiewicz 	ncm_data_nop_intf.iInterface = us[STRING_DATA_IDX].id;
144100a2430fSAndrzej Pietrasiewicz 	ncm_data_intf.iInterface = us[STRING_DATA_IDX].id;
144200a2430fSAndrzej Pietrasiewicz 	ecm_desc.iMACAddress = us[STRING_MAC_IDX].id;
144300a2430fSAndrzej Pietrasiewicz 	ncm_iad_desc.iFunction = us[STRING_IAD_IDX].id;
144400a2430fSAndrzej Pietrasiewicz 
144500a2430fSAndrzej Pietrasiewicz 	/* allocate instance-specific interface IDs */
144600a2430fSAndrzej Pietrasiewicz 	status = usb_interface_id(c, f);
144700a2430fSAndrzej Pietrasiewicz 	if (status < 0)
144800a2430fSAndrzej Pietrasiewicz 		goto fail;
144900a2430fSAndrzej Pietrasiewicz 	ncm->ctrl_id = status;
145000a2430fSAndrzej Pietrasiewicz 	ncm_iad_desc.bFirstInterface = status;
145100a2430fSAndrzej Pietrasiewicz 
145200a2430fSAndrzej Pietrasiewicz 	ncm_control_intf.bInterfaceNumber = status;
145300a2430fSAndrzej Pietrasiewicz 	ncm_union_desc.bMasterInterface0 = status;
145400a2430fSAndrzej Pietrasiewicz 
145500a2430fSAndrzej Pietrasiewicz 	status = usb_interface_id(c, f);
145600a2430fSAndrzej Pietrasiewicz 	if (status < 0)
145700a2430fSAndrzej Pietrasiewicz 		goto fail;
145800a2430fSAndrzej Pietrasiewicz 	ncm->data_id = status;
145900a2430fSAndrzej Pietrasiewicz 
146000a2430fSAndrzej Pietrasiewicz 	ncm_data_nop_intf.bInterfaceNumber = status;
146100a2430fSAndrzej Pietrasiewicz 	ncm_data_intf.bInterfaceNumber = status;
146200a2430fSAndrzej Pietrasiewicz 	ncm_union_desc.bSlaveInterface0 = status;
146300a2430fSAndrzej Pietrasiewicz 
146400a2430fSAndrzej Pietrasiewicz 	status = -ENODEV;
146500a2430fSAndrzej Pietrasiewicz 
146600a2430fSAndrzej Pietrasiewicz 	/* allocate instance-specific endpoints */
146700a2430fSAndrzej Pietrasiewicz 	ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_in_desc);
146800a2430fSAndrzej Pietrasiewicz 	if (!ep)
146900a2430fSAndrzej Pietrasiewicz 		goto fail;
147000a2430fSAndrzej Pietrasiewicz 	ncm->port.in_ep = ep;
147100a2430fSAndrzej Pietrasiewicz 
147200a2430fSAndrzej Pietrasiewicz 	ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_out_desc);
147300a2430fSAndrzej Pietrasiewicz 	if (!ep)
147400a2430fSAndrzej Pietrasiewicz 		goto fail;
147500a2430fSAndrzej Pietrasiewicz 	ncm->port.out_ep = ep;
147600a2430fSAndrzej Pietrasiewicz 
147700a2430fSAndrzej Pietrasiewicz 	ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_notify_desc);
147800a2430fSAndrzej Pietrasiewicz 	if (!ep)
147900a2430fSAndrzej Pietrasiewicz 		goto fail;
148000a2430fSAndrzej Pietrasiewicz 	ncm->notify = ep;
148100a2430fSAndrzej Pietrasiewicz 
148200a2430fSAndrzej Pietrasiewicz 	status = -ENOMEM;
148300a2430fSAndrzej Pietrasiewicz 
148400a2430fSAndrzej Pietrasiewicz 	/* allocate notification request and buffer */
148500a2430fSAndrzej Pietrasiewicz 	ncm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
148600a2430fSAndrzej Pietrasiewicz 	if (!ncm->notify_req)
148700a2430fSAndrzej Pietrasiewicz 		goto fail;
148800a2430fSAndrzej Pietrasiewicz 	ncm->notify_req->buf = kmalloc(NCM_STATUS_BYTECOUNT, GFP_KERNEL);
148900a2430fSAndrzej Pietrasiewicz 	if (!ncm->notify_req->buf)
149000a2430fSAndrzej Pietrasiewicz 		goto fail;
149100a2430fSAndrzej Pietrasiewicz 	ncm->notify_req->context = ncm;
149200a2430fSAndrzej Pietrasiewicz 	ncm->notify_req->complete = ncm_notify_complete;
149300a2430fSAndrzej Pietrasiewicz 
149400a2430fSAndrzej Pietrasiewicz 	/*
149500a2430fSAndrzej Pietrasiewicz 	 * support all relevant hardware speeds... we expect that when
149600a2430fSAndrzej Pietrasiewicz 	 * hardware is dual speed, all bulk-capable endpoints work at
149700a2430fSAndrzej Pietrasiewicz 	 * both speeds
149800a2430fSAndrzej Pietrasiewicz 	 */
149900a2430fSAndrzej Pietrasiewicz 	hs_ncm_in_desc.bEndpointAddress = fs_ncm_in_desc.bEndpointAddress;
150000a2430fSAndrzej Pietrasiewicz 	hs_ncm_out_desc.bEndpointAddress = fs_ncm_out_desc.bEndpointAddress;
150100a2430fSAndrzej Pietrasiewicz 	hs_ncm_notify_desc.bEndpointAddress =
150200a2430fSAndrzej Pietrasiewicz 		fs_ncm_notify_desc.bEndpointAddress;
150300a2430fSAndrzej Pietrasiewicz 
150416501138SJussi Kivilinna 	ss_ncm_in_desc.bEndpointAddress = fs_ncm_in_desc.bEndpointAddress;
150516501138SJussi Kivilinna 	ss_ncm_out_desc.bEndpointAddress = fs_ncm_out_desc.bEndpointAddress;
150616501138SJussi Kivilinna 	ss_ncm_notify_desc.bEndpointAddress =
150716501138SJussi Kivilinna 		fs_ncm_notify_desc.bEndpointAddress;
150816501138SJussi Kivilinna 
150900a2430fSAndrzej Pietrasiewicz 	status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function,
151016501138SJussi Kivilinna 			ncm_ss_function, NULL);
15118b920f16SPavitrakumar Managutte 	if (status)
15128b920f16SPavitrakumar Managutte 		goto fail;
15138b920f16SPavitrakumar Managutte 
151400a2430fSAndrzej Pietrasiewicz 	/*
151500a2430fSAndrzej Pietrasiewicz 	 * NOTE:  all that is done without knowing or caring about
151600a2430fSAndrzej Pietrasiewicz 	 * the network link ... which is unavailable to this code
151700a2430fSAndrzej Pietrasiewicz 	 * until we're activated via set_alt().
151800a2430fSAndrzej Pietrasiewicz 	 */
151900a2430fSAndrzej Pietrasiewicz 
152000a2430fSAndrzej Pietrasiewicz 	ncm->port.open = ncm_open;
152100a2430fSAndrzej Pietrasiewicz 	ncm->port.close = ncm_close;
152200a2430fSAndrzej Pietrasiewicz 
152300a2430fSAndrzej Pietrasiewicz 	tasklet_init(&ncm->tx_tasklet, ncm_tx_tasklet, (unsigned long) ncm);
152400a2430fSAndrzej Pietrasiewicz 	hrtimer_init(&ncm->task_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
152500a2430fSAndrzej Pietrasiewicz 	ncm->task_timer.function = ncm_tx_timeout;
152600a2430fSAndrzej Pietrasiewicz 
152700a2430fSAndrzej Pietrasiewicz 	DBG(cdev, "CDC Network: %s speed IN/%s OUT/%s NOTIFY/%s\n",
152816501138SJussi Kivilinna 			gadget_is_superspeed(c->cdev->gadget) ? "super" :
152900a2430fSAndrzej Pietrasiewicz 			gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
153000a2430fSAndrzej Pietrasiewicz 			ncm->port.in_ep->name, ncm->port.out_ep->name,
153100a2430fSAndrzej Pietrasiewicz 			ncm->notify->name);
153200a2430fSAndrzej Pietrasiewicz 	return 0;
153300a2430fSAndrzej Pietrasiewicz 
153400a2430fSAndrzej Pietrasiewicz fail:
153500a2430fSAndrzej Pietrasiewicz 	if (ncm->notify_req) {
153600a2430fSAndrzej Pietrasiewicz 		kfree(ncm->notify_req->buf);
153700a2430fSAndrzej Pietrasiewicz 		usb_ep_free_request(ncm->notify, ncm->notify_req);
153800a2430fSAndrzej Pietrasiewicz 	}
153900a2430fSAndrzej Pietrasiewicz 
154000a2430fSAndrzej Pietrasiewicz 	ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
154100a2430fSAndrzej Pietrasiewicz 
154200a2430fSAndrzej Pietrasiewicz 	return status;
154300a2430fSAndrzej Pietrasiewicz }
154400a2430fSAndrzej Pietrasiewicz 
154500a2430fSAndrzej Pietrasiewicz static inline struct f_ncm_opts *to_f_ncm_opts(struct config_item *item)
154600a2430fSAndrzej Pietrasiewicz {
154700a2430fSAndrzej Pietrasiewicz 	return container_of(to_config_group(item), struct f_ncm_opts,
154800a2430fSAndrzej Pietrasiewicz 			    func_inst.group);
154900a2430fSAndrzej Pietrasiewicz }
155000a2430fSAndrzej Pietrasiewicz 
155100a2430fSAndrzej Pietrasiewicz /* f_ncm_item_ops */
155200a2430fSAndrzej Pietrasiewicz USB_ETHERNET_CONFIGFS_ITEM(ncm);
155300a2430fSAndrzej Pietrasiewicz 
155400a2430fSAndrzej Pietrasiewicz /* f_ncm_opts_dev_addr */
155500a2430fSAndrzej Pietrasiewicz USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ncm);
155600a2430fSAndrzej Pietrasiewicz 
155700a2430fSAndrzej Pietrasiewicz /* f_ncm_opts_host_addr */
155800a2430fSAndrzej Pietrasiewicz USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ncm);
155900a2430fSAndrzej Pietrasiewicz 
156000a2430fSAndrzej Pietrasiewicz /* f_ncm_opts_qmult */
156100a2430fSAndrzej Pietrasiewicz USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ncm);
156200a2430fSAndrzej Pietrasiewicz 
156300a2430fSAndrzej Pietrasiewicz /* f_ncm_opts_ifname */
156400a2430fSAndrzej Pietrasiewicz USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ncm);
156500a2430fSAndrzej Pietrasiewicz 
156600a2430fSAndrzej Pietrasiewicz static struct configfs_attribute *ncm_attrs[] = {
1567f9a63da3SChristoph Hellwig 	&ncm_opts_attr_dev_addr,
1568f9a63da3SChristoph Hellwig 	&ncm_opts_attr_host_addr,
1569f9a63da3SChristoph Hellwig 	&ncm_opts_attr_qmult,
1570f9a63da3SChristoph Hellwig 	&ncm_opts_attr_ifname,
157100a2430fSAndrzej Pietrasiewicz 	NULL,
157200a2430fSAndrzej Pietrasiewicz };
157300a2430fSAndrzej Pietrasiewicz 
157400a2430fSAndrzej Pietrasiewicz static struct config_item_type ncm_func_type = {
157500a2430fSAndrzej Pietrasiewicz 	.ct_item_ops	= &ncm_item_ops,
157600a2430fSAndrzej Pietrasiewicz 	.ct_attrs	= ncm_attrs,
157700a2430fSAndrzej Pietrasiewicz 	.ct_owner	= THIS_MODULE,
157800a2430fSAndrzej Pietrasiewicz };
157900a2430fSAndrzej Pietrasiewicz 
158000a2430fSAndrzej Pietrasiewicz static void ncm_free_inst(struct usb_function_instance *f)
158100a2430fSAndrzej Pietrasiewicz {
158200a2430fSAndrzej Pietrasiewicz 	struct f_ncm_opts *opts;
158300a2430fSAndrzej Pietrasiewicz 
158400a2430fSAndrzej Pietrasiewicz 	opts = container_of(f, struct f_ncm_opts, func_inst);
158500a2430fSAndrzej Pietrasiewicz 	if (opts->bound)
158600a2430fSAndrzej Pietrasiewicz 		gether_cleanup(netdev_priv(opts->net));
158700a2430fSAndrzej Pietrasiewicz 	else
158800a2430fSAndrzej Pietrasiewicz 		free_netdev(opts->net);
158900a2430fSAndrzej Pietrasiewicz 	kfree(opts);
159000a2430fSAndrzej Pietrasiewicz }
159100a2430fSAndrzej Pietrasiewicz 
159200a2430fSAndrzej Pietrasiewicz static struct usb_function_instance *ncm_alloc_inst(void)
159300a2430fSAndrzej Pietrasiewicz {
159400a2430fSAndrzej Pietrasiewicz 	struct f_ncm_opts *opts;
159500a2430fSAndrzej Pietrasiewicz 
159600a2430fSAndrzej Pietrasiewicz 	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
159700a2430fSAndrzej Pietrasiewicz 	if (!opts)
159800a2430fSAndrzej Pietrasiewicz 		return ERR_PTR(-ENOMEM);
159900a2430fSAndrzej Pietrasiewicz 	mutex_init(&opts->lock);
160000a2430fSAndrzej Pietrasiewicz 	opts->func_inst.free_func_inst = ncm_free_inst;
160100a2430fSAndrzej Pietrasiewicz 	opts->net = gether_setup_default();
160200a2430fSAndrzej Pietrasiewicz 	if (IS_ERR(opts->net)) {
160300a2430fSAndrzej Pietrasiewicz 		struct net_device *net = opts->net;
160400a2430fSAndrzej Pietrasiewicz 		kfree(opts);
160500a2430fSAndrzej Pietrasiewicz 		return ERR_CAST(net);
160600a2430fSAndrzej Pietrasiewicz 	}
160700a2430fSAndrzej Pietrasiewicz 
160800a2430fSAndrzej Pietrasiewicz 	config_group_init_type_name(&opts->func_inst.group, "", &ncm_func_type);
160900a2430fSAndrzej Pietrasiewicz 
161000a2430fSAndrzej Pietrasiewicz 	return &opts->func_inst;
161100a2430fSAndrzej Pietrasiewicz }
161200a2430fSAndrzej Pietrasiewicz 
161300a2430fSAndrzej Pietrasiewicz static void ncm_free(struct usb_function *f)
161400a2430fSAndrzej Pietrasiewicz {
161500a2430fSAndrzej Pietrasiewicz 	struct f_ncm *ncm;
161600a2430fSAndrzej Pietrasiewicz 	struct f_ncm_opts *opts;
161700a2430fSAndrzej Pietrasiewicz 
161800a2430fSAndrzej Pietrasiewicz 	ncm = func_to_ncm(f);
161900a2430fSAndrzej Pietrasiewicz 	opts = container_of(f->fi, struct f_ncm_opts, func_inst);
162000a2430fSAndrzej Pietrasiewicz 	kfree(ncm);
162100a2430fSAndrzej Pietrasiewicz 	mutex_lock(&opts->lock);
162200a2430fSAndrzej Pietrasiewicz 	opts->refcnt--;
162300a2430fSAndrzej Pietrasiewicz 	mutex_unlock(&opts->lock);
162400a2430fSAndrzej Pietrasiewicz }
162500a2430fSAndrzej Pietrasiewicz 
162600a2430fSAndrzej Pietrasiewicz static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
162700a2430fSAndrzej Pietrasiewicz {
162800a2430fSAndrzej Pietrasiewicz 	struct f_ncm *ncm = func_to_ncm(f);
162900a2430fSAndrzej Pietrasiewicz 
163000a2430fSAndrzej Pietrasiewicz 	DBG(c->cdev, "ncm unbind\n");
163100a2430fSAndrzej Pietrasiewicz 
163200a2430fSAndrzej Pietrasiewicz 	hrtimer_cancel(&ncm->task_timer);
163300a2430fSAndrzej Pietrasiewicz 	tasklet_kill(&ncm->tx_tasklet);
163400a2430fSAndrzej Pietrasiewicz 
163500a2430fSAndrzej Pietrasiewicz 	ncm_string_defs[0].id = 0;
163600a2430fSAndrzej Pietrasiewicz 	usb_free_all_descriptors(f);
163700a2430fSAndrzej Pietrasiewicz 
163800a2430fSAndrzej Pietrasiewicz 	kfree(ncm->notify_req->buf);
163900a2430fSAndrzej Pietrasiewicz 	usb_ep_free_request(ncm->notify, ncm->notify_req);
164000a2430fSAndrzej Pietrasiewicz }
164100a2430fSAndrzej Pietrasiewicz 
164200a2430fSAndrzej Pietrasiewicz static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
164300a2430fSAndrzej Pietrasiewicz {
164400a2430fSAndrzej Pietrasiewicz 	struct f_ncm		*ncm;
164500a2430fSAndrzej Pietrasiewicz 	struct f_ncm_opts	*opts;
164600a2430fSAndrzej Pietrasiewicz 	int status;
164700a2430fSAndrzej Pietrasiewicz 
164800a2430fSAndrzej Pietrasiewicz 	/* allocate and initialize one new instance */
164900a2430fSAndrzej Pietrasiewicz 	ncm = kzalloc(sizeof(*ncm), GFP_KERNEL);
165000a2430fSAndrzej Pietrasiewicz 	if (!ncm)
165100a2430fSAndrzej Pietrasiewicz 		return ERR_PTR(-ENOMEM);
165200a2430fSAndrzej Pietrasiewicz 
165300a2430fSAndrzej Pietrasiewicz 	opts = container_of(fi, struct f_ncm_opts, func_inst);
165400a2430fSAndrzej Pietrasiewicz 	mutex_lock(&opts->lock);
165500a2430fSAndrzej Pietrasiewicz 	opts->refcnt++;
165600a2430fSAndrzej Pietrasiewicz 
165700a2430fSAndrzej Pietrasiewicz 	/* export host's Ethernet address in CDC format */
165800a2430fSAndrzej Pietrasiewicz 	status = gether_get_host_addr_cdc(opts->net, ncm->ethaddr,
165900a2430fSAndrzej Pietrasiewicz 				      sizeof(ncm->ethaddr));
166000a2430fSAndrzej Pietrasiewicz 	if (status < 12) { /* strlen("01234567890a") */
166100a2430fSAndrzej Pietrasiewicz 		kfree(ncm);
166200a2430fSAndrzej Pietrasiewicz 		mutex_unlock(&opts->lock);
166300a2430fSAndrzej Pietrasiewicz 		return ERR_PTR(-EINVAL);
166400a2430fSAndrzej Pietrasiewicz 	}
166500a2430fSAndrzej Pietrasiewicz 	ncm_string_defs[STRING_MAC_IDX].s = ncm->ethaddr;
166600a2430fSAndrzej Pietrasiewicz 
166700a2430fSAndrzej Pietrasiewicz 	spin_lock_init(&ncm->lock);
166800a2430fSAndrzej Pietrasiewicz 	ncm_reset_values(ncm);
166900a2430fSAndrzej Pietrasiewicz 	ncm->port.ioport = netdev_priv(opts->net);
167000a2430fSAndrzej Pietrasiewicz 	mutex_unlock(&opts->lock);
167100a2430fSAndrzej Pietrasiewicz 	ncm->port.is_fixed = true;
167200a2430fSAndrzej Pietrasiewicz 	ncm->port.supports_multi_frame = true;
167300a2430fSAndrzej Pietrasiewicz 
167400a2430fSAndrzej Pietrasiewicz 	ncm->port.func.name = "cdc_network";
167500a2430fSAndrzej Pietrasiewicz 	/* descriptors are per-instance copies */
167600a2430fSAndrzej Pietrasiewicz 	ncm->port.func.bind = ncm_bind;
167700a2430fSAndrzej Pietrasiewicz 	ncm->port.func.unbind = ncm_unbind;
167800a2430fSAndrzej Pietrasiewicz 	ncm->port.func.set_alt = ncm_set_alt;
167900a2430fSAndrzej Pietrasiewicz 	ncm->port.func.get_alt = ncm_get_alt;
168000a2430fSAndrzej Pietrasiewicz 	ncm->port.func.setup = ncm_setup;
168100a2430fSAndrzej Pietrasiewicz 	ncm->port.func.disable = ncm_disable;
168200a2430fSAndrzej Pietrasiewicz 	ncm->port.func.free_func = ncm_free;
168300a2430fSAndrzej Pietrasiewicz 
168400a2430fSAndrzej Pietrasiewicz 	ncm->port.wrap = ncm_wrap_ntb;
168500a2430fSAndrzej Pietrasiewicz 	ncm->port.unwrap = ncm_unwrap_ntb;
168600a2430fSAndrzej Pietrasiewicz 
168700a2430fSAndrzej Pietrasiewicz 	return &ncm->port.func;
168800a2430fSAndrzej Pietrasiewicz }
168900a2430fSAndrzej Pietrasiewicz 
169000a2430fSAndrzej Pietrasiewicz DECLARE_USB_FUNCTION_INIT(ncm, ncm_alloc_inst, ncm_alloc);
169100a2430fSAndrzej Pietrasiewicz MODULE_LICENSE("GPL");
169200a2430fSAndrzej Pietrasiewicz MODULE_AUTHOR("Yauheni Kaliuta");
1693