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