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