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