2010-12-08 13:12:05 +02:00
/*
* 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
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*/
# include <linux/kernel.h>
2013-05-23 09:22:06 +02:00
# include <linux/module.h>
2010-12-08 13:12:05 +02:00
# include <linux/device.h>
# include <linux/etherdevice.h>
# include <linux/crc32.h>
# include <linux/usb/cdc.h>
# include "u_ether.h"
2013-05-23 10:32:02 +02:00
# include "u_ether_configfs.h"
2013-05-23 09:22:06 +02:00
# include "u_ncm.h"
2010-12-08 13:12:05 +02:00
/*
* 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_MASK 0x01000000
# define NCM_NDP_HDR_CRC 0x01000000
# define NCM_NDP_HDR_NOCRC 0x00000000
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 ;
bool is_open ;
2012-11-22 19:50:34 +01:00
const struct ndp_parser_opts * parser_opts ;
2010-12-08 13:12:05 +02:00
bool is_crc ;
2012-11-22 19:50:34 +01:00
u32 ndp_sign ;
2010-12-08 13:12:05 +02:00
/*
* for notification , it is accessed from both
* callback and ethernet open / close
*/
spinlock_t lock ;
2014-07-07 18:33:18 +01:00
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 ;
bool timer_force_tx ;
struct tasklet_struct tx_tasklet ;
struct hrtimer task_timer ;
bool timer_stopping ;
2010-12-08 13:12:05 +02:00
} ;
static inline struct f_ncm * func_to_ncm ( struct usb_function * f )
{
return container_of ( f , struct f_ncm , port . func ) ;
}
/* peak (theoretical) bulk transfer rate in bits-per-second */
static inline unsigned ncm_bitrate ( struct usb_gadget * g )
{
2016-08-12 17:28:05 +03:00
if ( gadget_is_superspeed ( g ) & & g - > speed = = USB_SPEED_SUPER )
return 13 * 1024 * 8 * 1000 * 8 ;
else if ( gadget_is_dualspeed ( g ) & & g - > speed = = USB_SPEED_HIGH )
2010-12-08 13:12:05 +02:00
return 13 * 512 * 8 * 1000 * 8 ;
else
return 19 * 64 * 1 * 1000 * 8 ;
}
/*-------------------------------------------------------------------------*/
/*
* 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 , 16 K is selected ,
* because it ' s used by default by the current linux host driver
*/
2014-07-07 18:33:18 +01:00
# define NTB_DEFAULT_IN_SIZE 16384
2010-12-08 13:12:05 +02:00
# define NTB_OUT_SIZE 16384
2014-07-07 18:33:18 +01:00
/* Allocation for storing the NDP, 32 should suffice for a
* 16 k packet . This allows a maximum of 32 * 507 Byte packets to
* be transmitted in a single 16 kB 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 .
2010-12-08 13:12:05 +02:00
*/
2014-07-07 18:33:18 +01:00
# define TX_MAX_NUM_DPE 32
2010-12-08 13:12:05 +02:00
2014-07-07 18:33:18 +01:00
/* Delay for the transmit to wait before sending an unfilled NTB frame. */
# define TX_TIMEOUT_NSECS 300000
2010-12-08 13:12:05 +02:00
# define FORMATS_SUPPORTED (USB_CDC_NCM_NTB16_SUPPORTED | \
USB_CDC_NCM_NTB32_SUPPORTED )
static struct usb_cdc_ncm_ntb_parameters ntb_parameters = {
2012-10-05 01:44:04 +03:00
. wLength = cpu_to_le16 ( sizeof ( ntb_parameters ) ) ,
2010-12-08 13:12:05 +02:00
. 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 .
*/
2012-10-22 22:14:57 +02:00
# define NCM_STATUS_INTERVAL_MS 32
2010-12-08 13:12:05 +02:00
# define NCM_STATUS_BYTECOUNT 16 /* 8 byte header + data */
2013-05-23 09:22:06 +02:00
static struct usb_interface_assoc_descriptor ncm_iad_desc = {
2010-12-08 13:12:05 +02:00
. 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: */
2013-05-23 09:22:06 +02:00
static struct usb_interface_descriptor ncm_control_intf = {
2010-12-08 13:12:05 +02:00
. 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 */
} ;
2013-05-23 09:22:06 +02:00
static struct usb_cdc_header_desc ncm_header_desc = {
2010-12-08 13:12:05 +02:00
. bLength = sizeof ncm_header_desc ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
. bDescriptorSubType = USB_CDC_HEADER_TYPE ,
. bcdCDC = cpu_to_le16 ( 0x0110 ) ,
} ;
2013-05-23 09:22:06 +02:00
static struct usb_cdc_union_desc ncm_union_desc = {
2010-12-08 13:12:05 +02:00
. bLength = sizeof ( ncm_union_desc ) ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
. bDescriptorSubType = USB_CDC_UNION_TYPE ,
/* .bMasterInterface0 = DYNAMIC */
/* .bSlaveInterface0 = DYNAMIC */
} ;
2013-05-23 09:22:06 +02:00
static struct usb_cdc_ether_desc ecm_desc = {
2010-12-08 13:12:05 +02:00
. 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 */
. wMaxSegmentSize = cpu_to_le16 ( ETH_FRAME_LEN ) ,
. wNumberMCFilters = cpu_to_le16 ( 0 ) ,
. bNumberPowerFilters = 0 ,
} ;
# define NCAPS (USB_CDC_NCM_NCAP_ETH_FILTER | USB_CDC_NCM_NCAP_CRC_MODE)
2013-05-23 09:22:06 +02:00
static struct usb_cdc_ncm_desc ncm_desc = {
2010-12-08 13:12:05 +02:00
. 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 ... */
2013-05-23 09:22:06 +02:00
static struct usb_interface_descriptor ncm_data_nop_intf = {
2010-12-08 13:12:05 +02:00
. 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 */
2013-05-23 09:22:06 +02:00
static struct usb_interface_descriptor ncm_data_intf = {
2010-12-08 13:12:05 +02:00
. 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: */
2013-05-23 09:22:06 +02:00
static struct usb_endpoint_descriptor fs_ncm_notify_desc = {
2010-12-08 13:12:05 +02:00
. 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 ) ,
2012-10-22 22:14:57 +02:00
. bInterval = NCM_STATUS_INTERVAL_MS ,
2010-12-08 13:12:05 +02:00
} ;
2013-05-23 09:22:06 +02:00
static struct usb_endpoint_descriptor fs_ncm_in_desc = {
2010-12-08 13:12:05 +02:00
. bLength = USB_DT_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_ENDPOINT ,
. bEndpointAddress = USB_DIR_IN ,
. bmAttributes = USB_ENDPOINT_XFER_BULK ,
} ;
2013-05-23 09:22:06 +02:00
static struct usb_endpoint_descriptor fs_ncm_out_desc = {
2010-12-08 13:12:05 +02:00
. bLength = USB_DT_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_ENDPOINT ,
. bEndpointAddress = USB_DIR_OUT ,
. bmAttributes = USB_ENDPOINT_XFER_BULK ,
} ;
2013-05-23 09:22:06 +02:00
static struct usb_descriptor_header * ncm_fs_function [ ] = {
2010-12-08 13:12:05 +02:00
( 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: */
2013-05-23 09:22:06 +02:00
static struct usb_endpoint_descriptor hs_ncm_notify_desc = {
2010-12-08 13:12:05 +02:00
. 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 ) ,
2012-10-22 22:14:57 +02:00
. bInterval = USB_MS_TO_HS_INTERVAL ( NCM_STATUS_INTERVAL_MS ) ,
2010-12-08 13:12:05 +02:00
} ;
2013-05-23 09:22:06 +02:00
static struct usb_endpoint_descriptor hs_ncm_in_desc = {
2010-12-08 13:12:05 +02:00
. bLength = USB_DT_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_ENDPOINT ,
. bEndpointAddress = USB_DIR_IN ,
. bmAttributes = USB_ENDPOINT_XFER_BULK ,
. wMaxPacketSize = cpu_to_le16 ( 512 ) ,
} ;
2013-05-23 09:22:06 +02:00
static struct usb_endpoint_descriptor hs_ncm_out_desc = {
2010-12-08 13:12:05 +02:00
. bLength = USB_DT_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_ENDPOINT ,
. bEndpointAddress = USB_DIR_OUT ,
. bmAttributes = USB_ENDPOINT_XFER_BULK ,
. wMaxPacketSize = cpu_to_le16 ( 512 ) ,
} ;
2013-05-23 09:22:06 +02:00
static struct usb_descriptor_header * ncm_hs_function [ ] = {
2010-12-08 13:12:05 +02:00
( 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 ,
} ;
2016-08-12 17:28:05 +03:00
/* 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 = 0, */
/* .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 ,
} ;
2010-12-08 13:12:05 +02:00
/* 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) " ,
2012-10-22 22:15:10 +02:00
[ STRING_MAC_IDX ] . s = " " ,
2010-12-08 13:12:05 +02:00
[ 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 16 bit word wide ,
* in NDP32 - - 2 16 bit 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 ;
2014-07-07 18:33:18 +01:00
unsigned dpe_size ;
2010-12-08 13:12:05 +02:00
unsigned ndplen_align ;
/* sizes in u16 units */
unsigned dgram_item_len ; /* index or length */
unsigned block_length ;
2014-07-07 18:33:18 +01:00
unsigned ndp_index ;
2010-12-08 13:12:05 +02:00
unsigned reserved1 ;
unsigned reserved2 ;
2014-07-07 18:33:18 +01:00
unsigned next_ndp_index ;
2010-12-08 13:12:05 +02:00
} ;
# define INIT_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 ) , \
2014-07-07 18:33:18 +01:00
. dpe_size = sizeof ( struct usb_cdc_ncm_dpe16 ) , \
2010-12-08 13:12:05 +02:00
. ndplen_align = 4 , \
. dgram_item_len = 1 , \
. block_length = 1 , \
2014-07-07 18:33:18 +01:00
. ndp_index = 1 , \
2010-12-08 13:12:05 +02:00
. reserved1 = 0 , \
. reserved2 = 0 , \
2014-07-07 18:33:18 +01:00
. next_ndp_index = 1 , \
2010-12-08 13:12:05 +02:00
}
# define INIT_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 ) , \
2014-07-07 18:33:18 +01:00
. dpe_size = sizeof ( struct usb_cdc_ncm_dpe32 ) , \
2010-12-08 13:12:05 +02:00
. ndplen_align = 8 , \
. dgram_item_len = 2 , \
. block_length = 2 , \
2014-07-07 18:33:18 +01:00
. ndp_index = 2 , \
2010-12-08 13:12:05 +02:00
. reserved1 = 1 , \
. reserved2 = 2 , \
2014-07-07 18:33:18 +01:00
. next_ndp_index = 2 , \
2010-12-08 13:12:05 +02:00
}
2012-11-22 19:50:34 +01:00
static const struct ndp_parser_opts ndp16_opts = INIT_NDP16_OPTS ;
static const struct ndp_parser_opts ndp32_opts = INIT_NDP32_OPTS ;
2010-12-08 13:12:05 +02:00
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 )
{
unsigned tmp ;
switch ( size ) {
case 1 :
tmp = get_unaligned_le16 ( * p ) ;
break ;
case 2 :
tmp = get_unaligned_le32 ( * p ) ;
break ;
default :
BUG ( ) ;
}
* p + = size ;
return tmp ;
}
/*-------------------------------------------------------------------------*/
static inline void ncm_reset_values ( struct f_ncm * ncm )
{
ncm - > parser_opts = & ndp16_opts ;
ncm - > is_crc = false ;
ncm - > port . cdc_filter = DEFAULT_FILTER ;
/* doesn't make sense for ncm, fixed size used */
ncm - > port . header_len = 0 ;
ncm - > port . fixed_out_len = le32_to_cpu ( ntb_parameters . dwNtbOutMaxSize ) ;
ncm - > port . fixed_in_len = NTB_DEFAULT_IN_SIZE ;
}
/*
* Context : ncm - > lock held
*/
static void ncm_do_notify ( struct f_ncm * ncm )
{
struct usb_request * req = ncm - > notify_req ;
struct usb_cdc_notification * event ;
struct usb_composite_dev * cdev = ncm - > port . func . config - > cdev ;
__le32 * data ;
int status ;
/* notification already in flight? */
if ( ! req )
return ;
event = req - > buf ;
switch ( ncm - > notify_state ) {
case NCM_NOTIFY_NONE :
return ;
case NCM_NOTIFY_CONNECT :
event - > bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION ;
if ( ncm - > is_open )
event - > wValue = cpu_to_le16 ( 1 ) ;
else
event - > wValue = cpu_to_le16 ( 0 ) ;
event - > wLength = 0 ;
req - > length = sizeof * event ;
DBG ( cdev , " notify connect %s \n " ,
ncm - > is_open ? " true " : " false " ) ;
ncm - > notify_state = NCM_NOTIFY_NONE ;
break ;
case NCM_NOTIFY_SPEED :
event - > bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE ;
event - > wValue = cpu_to_le16 ( 0 ) ;
event - > wLength = cpu_to_le16 ( 8 ) ;
req - > length = NCM_STATUS_BYTECOUNT ;
/* SPEED_CHANGE data is up/down speeds in bits/sec */
data = req - > buf + sizeof * event ;
data [ 0 ] = cpu_to_le32 ( ncm_bitrate ( cdev - > gadget ) ) ;
data [ 1 ] = data [ 0 ] ;
DBG ( cdev , " notify speed %d \n " , ncm_bitrate ( cdev - > gadget ) ) ;
ncm - > notify_state = NCM_NOTIFY_CONNECT ;
break ;
}
event - > bmRequestType = 0xA1 ;
event - > wIndex = cpu_to_le16 ( ncm - > ctrl_id ) ;
ncm - > notify_req = NULL ;
/*
* In double buffering if there is a space in FIFO ,
* completion callback can be called right after the call ,
* so unlocking
*/
spin_unlock ( & ncm - > lock ) ;
status = usb_ep_queue ( ncm - > notify , req , GFP_ATOMIC ) ;
spin_lock ( & ncm - > lock ) ;
if ( status < 0 ) {
ncm - > notify_req = req ;
DBG ( cdev , " notify --> %d \n " , status ) ;
}
}
/*
* Context : ncm - > lock held
*/
static void ncm_notify ( struct f_ncm * ncm )
{
/*
* NOTE on most versions of Linux , host side cdc - ethernet
* won ' t listen for notifications until its netdevice opens .
* The first notification then sits in the FIFO for a long
* time , and the second one is queued .
*
* If ncm_notify ( ) is called before the second ( CONNECT )
* notification is sent , then it will reset to send the SPEED
* notificaion again ( and again , and again ) , but it ' s not a problem
*/
ncm - > notify_state = NCM_NOTIFY_SPEED ;
ncm_do_notify ( ncm ) ;
}
static void ncm_notify_complete ( struct usb_ep * ep , struct usb_request * req )
{
struct f_ncm * ncm = req - > context ;
struct usb_composite_dev * cdev = ncm - > port . func . config - > cdev ;
struct usb_cdc_notification * event = req - > buf ;
spin_lock ( & ncm - > lock ) ;
switch ( req - > status ) {
case 0 :
VDBG ( cdev , " Notification %02x sent \n " ,
event - > bNotificationType ) ;
break ;
case - ECONNRESET :
case - ESHUTDOWN :
ncm - > notify_state = NCM_NOTIFY_NONE ;
break ;
default :
DBG ( cdev , " event %02x --> %d \n " ,
event - > bNotificationType , req - > status ) ;
break ;
}
ncm - > notify_req = req ;
ncm_do_notify ( ncm ) ;
spin_unlock ( & ncm - > lock ) ;
}
static void ncm_ep0out_complete ( struct usb_ep * ep , struct usb_request * req )
{
/* now for SET_NTB_INPUT_SIZE only */
unsigned in_size ;
struct usb_function * f = req - > context ;
struct f_ncm * ncm = func_to_ncm ( f ) ;
2015-09-16 12:10:40 +02:00
struct usb_composite_dev * cdev = f - > config - > cdev ;
2010-12-08 13:12:05 +02:00
req - > context = NULL ;
if ( req - > status | | req - > actual ! = req - > length ) {
DBG ( cdev , " Bad control-OUT transfer \n " ) ;
goto invalid ;
}
in_size = get_unaligned_le32 ( req - > buf ) ;
if ( in_size < USB_CDC_NCM_NTB_MIN_IN_SIZE | |
in_size > le32_to_cpu ( ntb_parameters . dwNtbInMaxSize ) ) {
DBG ( cdev , " Got wrong INPUT SIZE (%d) from host \n " , in_size ) ;
goto invalid ;
}
ncm - > port . fixed_in_len = in_size ;
VDBG ( cdev , " Set NTB INPUT SIZE %d \n " , in_size ) ;
return ;
invalid :
usb_ep_set_halt ( ep ) ;
return ;
}
static int ncm_setup ( struct usb_function * f , const struct usb_ctrlrequest * ctrl )
{
struct f_ncm * ncm = func_to_ncm ( f ) ;
struct usb_composite_dev * cdev = f - > config - > cdev ;
struct usb_request * req = cdev - > req ;
int value = - EOPNOTSUPP ;
u16 w_index = le16_to_cpu ( ctrl - > wIndex ) ;
u16 w_value = le16_to_cpu ( ctrl - > wValue ) ;
u16 w_length = le16_to_cpu ( ctrl - > wLength ) ;
/*
* composite driver infrastructure handles everything except
* CDC class messages ; interface activation uses set_alt ( ) .
*/
switch ( ( ctrl - > bRequestType < < 8 ) | ctrl - > bRequest ) {
case ( ( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE ) < < 8 )
| USB_CDC_SET_ETHERNET_PACKET_FILTER :
/*
* see 6.2 .30 : no data , wIndex = interface ,
* wValue = packet filter bitmap
*/
if ( w_length ! = 0 | | w_index ! = ncm - > ctrl_id )
goto invalid ;
DBG ( cdev , " packet filter %02x \n " , w_value ) ;
/*
* REVISIT locking of cdc_filter . This assumes the UDC
* driver won ' t have a concurrent packet TX irq running on
* another CPU ; or that if it does , this write is atomic . . .
*/
ncm - > port . cdc_filter = w_value ;
value = 0 ;
break ;
/*
* and optionally :
* case USB_CDC_SEND_ENCAPSULATED_COMMAND :
* case USB_CDC_GET_ENCAPSULATED_RESPONSE :
* case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS :
* case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER :
* case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER :
* case USB_CDC_GET_ETHERNET_STATISTIC :
*/
case ( ( USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE ) < < 8 )
| USB_CDC_GET_NTB_PARAMETERS :
if ( w_length = = 0 | | w_value ! = 0 | | w_index ! = ncm - > ctrl_id )
goto invalid ;
value = w_length > sizeof ntb_parameters ?
sizeof ntb_parameters : w_length ;
memcpy ( req - > buf , & ntb_parameters , value ) ;
VDBG ( cdev , " Host asked NTB parameters \n " ) ;
break ;
case ( ( USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE ) < < 8 )
| USB_CDC_GET_NTB_INPUT_SIZE :
if ( w_length < 4 | | w_value ! = 0 | | w_index ! = ncm - > ctrl_id )
goto invalid ;
put_unaligned_le32 ( ncm - > port . fixed_in_len , req - > buf ) ;
value = 4 ;
VDBG ( cdev , " Host asked INPUT SIZE, sending %d \n " ,
ncm - > port . fixed_in_len ) ;
break ;
case ( ( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE ) < < 8 )
| USB_CDC_SET_NTB_INPUT_SIZE :
{
if ( w_length ! = 4 | | w_value ! = 0 | | w_index ! = ncm - > ctrl_id )
goto invalid ;
req - > complete = ncm_ep0out_complete ;
req - > length = w_length ;
req - > context = f ;
value = req - > length ;
break ;
}
case ( ( USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE ) < < 8 )
| USB_CDC_GET_NTB_FORMAT :
{
uint16_t format ;
if ( w_length < 2 | | w_value ! = 0 | | w_index ! = ncm - > ctrl_id )
goto invalid ;
format = ( ncm - > parser_opts = = & ndp16_opts ) ? 0x0000 : 0x0001 ;
put_unaligned_le16 ( format , req - > buf ) ;
value = 2 ;
VDBG ( cdev , " Host asked NTB FORMAT, sending %d \n " , format ) ;
break ;
}
case ( ( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE ) < < 8 )
| USB_CDC_SET_NTB_FORMAT :
{
if ( w_length ! = 0 | | w_index ! = ncm - > ctrl_id )
goto invalid ;
switch ( w_value ) {
case 0x0000 :
ncm - > parser_opts = & ndp16_opts ;
DBG ( cdev , " NCM16 selected \n " ) ;
break ;
case 0x0001 :
ncm - > parser_opts = & ndp32_opts ;
DBG ( cdev , " NCM32 selected \n " ) ;
break ;
default :
goto invalid ;
}
value = 0 ;
break ;
}
case ( ( USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE ) < < 8 )
| USB_CDC_GET_CRC_MODE :
{
uint16_t is_crc ;
if ( w_length < 2 | | w_value ! = 0 | | w_index ! = ncm - > ctrl_id )
goto invalid ;
is_crc = ncm - > is_crc ? 0x0001 : 0x0000 ;
put_unaligned_le16 ( is_crc , req - > buf ) ;
value = 2 ;
VDBG ( cdev , " Host asked CRC MODE, sending %d \n " , is_crc ) ;
break ;
}
case ( ( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE ) < < 8 )
| USB_CDC_SET_CRC_MODE :
{
int ndp_hdr_crc = 0 ;
if ( w_length ! = 0 | | w_index ! = ncm - > ctrl_id )
goto invalid ;
switch ( w_value ) {
case 0x0000 :
ncm - > is_crc = false ;
ndp_hdr_crc = NCM_NDP_HDR_NOCRC ;
DBG ( cdev , " non-CRC mode selected \n " ) ;
break ;
case 0x0001 :
ncm - > is_crc = true ;
ndp_hdr_crc = NCM_NDP_HDR_CRC ;
DBG ( cdev , " CRC mode selected \n " ) ;
break ;
default :
goto invalid ;
}
2012-11-22 19:50:34 +01:00
ncm - > ndp_sign = ncm - > parser_opts - > ndp_sign | ndp_hdr_crc ;
2010-12-08 13:12:05 +02:00
value = 0 ;
break ;
}
/* and disabled in ncm descriptor: */
/* case USB_CDC_GET_NET_ADDRESS: */
/* case USB_CDC_SET_NET_ADDRESS: */
/* case USB_CDC_GET_MAX_DATAGRAM_SIZE: */
/* case USB_CDC_SET_MAX_DATAGRAM_SIZE: */
default :
invalid :
DBG ( cdev , " invalid control req%02x.%02x v%04x i%04x l%d \n " ,
ctrl - > bRequestType , ctrl - > bRequest ,
w_value , w_index , w_length ) ;
}
/* respond with data transfer or status phase? */
if ( value > = 0 ) {
DBG ( cdev , " ncm req%02x.%02x v%04x i%04x l%d \n " ,
ctrl - > bRequestType , ctrl - > bRequest ,
w_value , w_index , w_length ) ;
req - > zero = 0 ;
req - > length = value ;
value = usb_ep_queue ( cdev - > gadget - > ep0 , req , GFP_ATOMIC ) ;
if ( value < 0 )
ERROR ( cdev , " ncm req %02x.%02x response err %d \n " ,
ctrl - > bRequestType , ctrl - > bRequest ,
value ) ;
}
/* device either stalls (value < 0) or reports success */
return value ;
}
static int ncm_set_alt ( struct usb_function * f , unsigned intf , unsigned alt )
{
struct f_ncm * ncm = func_to_ncm ( f ) ;
struct usb_composite_dev * cdev = f - > config - > cdev ;
/* Control interface has only altsetting 0 */
if ( intf = = ncm - > ctrl_id ) {
if ( alt ! = 0 )
goto fail ;
2015-09-16 12:10:50 +02:00
DBG ( cdev , " reset ncm control %d \n " , intf ) ;
usb_ep_disable ( ncm - > notify ) ;
2011-06-28 16:33:50 +03:00
if ( ! ( ncm - > notify - > desc ) ) {
2010-12-08 13:12:05 +02:00
DBG ( cdev , " init ncm ctrl %d \n " , intf ) ;
2011-06-28 16:33:50 +03:00
if ( config_ep_by_speed ( cdev - > gadget , f , ncm - > notify ) )
goto fail ;
2010-12-08 13:12:05 +02:00
}
2011-06-28 16:33:48 +03:00
usb_ep_enable ( ncm - > notify ) ;
2010-12-08 13:12:05 +02:00
/* Data interface has two altsettings, 0 and 1 */
} else if ( intf = = ncm - > data_id ) {
if ( alt > 1 )
goto fail ;
2015-09-16 12:10:50 +02:00
if ( ncm - > port . in_ep - > enabled ) {
2010-12-08 13:12:05 +02:00
DBG ( cdev , " reset ncm \n " ) ;
2014-07-07 18:33:18 +01:00
ncm - > timer_stopping = true ;
ncm - > netdev = NULL ;
2010-12-08 13:12:05 +02:00
gether_disconnect ( & ncm - > port ) ;
ncm_reset_values ( ncm ) ;
}
/*
* CDC Network only sends data in non - default altsettings .
* Changing altsettings resets filters , statistics , etc .
*/
if ( alt = = 1 ) {
struct net_device * net ;
2011-06-28 16:33:50 +03:00
if ( ! ncm - > port . in_ep - > desc | |
! ncm - > port . out_ep - > desc ) {
2010-12-08 13:12:05 +02:00
DBG ( cdev , " init ncm \n " ) ;
2011-06-28 16:33:50 +03:00
if ( config_ep_by_speed ( cdev - > gadget , f ,
ncm - > port . in_ep ) | |
config_ep_by_speed ( cdev - > gadget , f ,
ncm - > port . out_ep ) ) {
ncm - > port . in_ep - > desc = NULL ;
ncm - > port . out_ep - > desc = NULL ;
goto fail ;
}
2010-12-08 13:12:05 +02:00
}
/* TODO */
/* Enable zlps by default for NCM conformance;
* override for musb_hdrc ( avoids txdma ovhead )
*/
2015-07-28 07:20:02 +02:00
ncm - > port . is_zlp_ok =
gadget_is_zlp_supported ( cdev - > gadget ) ;
2016-08-22 17:48:27 +09:00
ncm - > port . no_skb_reserve =
gadget_avoids_skb_reserve ( cdev - > gadget ) ;
2010-12-08 13:12:05 +02:00
ncm - > port . cdc_filter = DEFAULT_FILTER ;
DBG ( cdev , " activate ncm \n " ) ;
net = gether_connect ( & ncm - > port ) ;
if ( IS_ERR ( net ) )
return PTR_ERR ( net ) ;
2014-07-07 18:33:18 +01:00
ncm - > netdev = net ;
ncm - > timer_stopping = false ;
2010-12-08 13:12:05 +02:00
}
spin_lock ( & ncm - > lock ) ;
ncm_notify ( ncm ) ;
spin_unlock ( & ncm - > lock ) ;
} else
goto fail ;
return 0 ;
fail :
return - EINVAL ;
}
/*
* Because the data interface supports multiple altsettings ,
* this NCM function * MUST * implement a get_alt ( ) method .
*/
static int ncm_get_alt ( struct usb_function * f , unsigned intf )
{
struct f_ncm * ncm = func_to_ncm ( f ) ;
if ( intf = = ncm - > ctrl_id )
return 0 ;
2015-09-16 12:10:50 +02:00
return ncm - > port . in_ep - > enabled ? 1 : 0 ;
2010-12-08 13:12:05 +02:00
}
2014-07-07 18:33:18 +01:00
static struct sk_buff * package_for_tx ( struct f_ncm * ncm )
{
__le16 * ntb_iter ;
struct sk_buff * skb2 = NULL ;
unsigned ndp_pad ;
unsigned ndp_index ;
unsigned new_len ;
const struct ndp_parser_opts * opts = ncm - > parser_opts ;
const int ndp_align = le16_to_cpu ( ntb_parameters . wNdpInAlignment ) ;
const int dgram_idx_len = 2 * 2 * opts - > dgram_item_len ;
/* Stop the timer */
hrtimer_try_to_cancel ( & ncm - > task_timer ) ;
ndp_pad = ALIGN ( ncm - > skb_tx_data - > len , ndp_align ) -
ncm - > skb_tx_data - > len ;
ndp_index = ncm - > skb_tx_data - > len + ndp_pad ;
new_len = ndp_index + dgram_idx_len + ncm - > skb_tx_ndp - > len ;
/* Set the final BlockLength and wNdpIndex */
ntb_iter = ( void * ) ncm - > skb_tx_data - > data ;
/* Increment pointer to BlockLength */
ntb_iter + = 2 + 1 + 1 ;
put_ncm ( & ntb_iter , opts - > block_length , new_len ) ;
put_ncm ( & ntb_iter , opts - > ndp_index , ndp_index ) ;
/* Set the final NDP wLength */
new_len = opts - > ndp_size +
( ncm - > ndp_dgram_count * dgram_idx_len ) ;
ncm - > ndp_dgram_count = 0 ;
/* Increment from start to wLength */
ntb_iter = ( void * ) ncm - > skb_tx_ndp - > data ;
ntb_iter + = 2 ;
put_unaligned_le16 ( new_len , ntb_iter ) ;
/* Merge the skbs */
swap ( skb2 , ncm - > skb_tx_data ) ;
if ( ncm - > skb_tx_data ) {
dev_kfree_skb_any ( ncm - > skb_tx_data ) ;
ncm - > skb_tx_data = NULL ;
}
/* Insert NDP alignment. */
ntb_iter = ( void * ) skb_put ( skb2 , ndp_pad ) ;
memset ( ntb_iter , 0 , ndp_pad ) ;
/* Copy NTB across. */
ntb_iter = ( void * ) skb_put ( skb2 , ncm - > skb_tx_ndp - > len ) ;
memcpy ( ntb_iter , ncm - > skb_tx_ndp - > data , ncm - > skb_tx_ndp - > len ) ;
dev_kfree_skb_any ( ncm - > skb_tx_ndp ) ;
ncm - > skb_tx_ndp = NULL ;
/* Insert zero'd datagram. */
ntb_iter = ( void * ) skb_put ( skb2 , dgram_idx_len ) ;
memset ( ntb_iter , 0 , dgram_idx_len ) ;
return skb2 ;
}
2010-12-08 13:12:05 +02:00
static struct sk_buff * ncm_wrap_ntb ( struct gether * port ,
struct sk_buff * skb )
{
struct f_ncm * ncm = func_to_ncm ( & port - > func ) ;
2014-07-07 18:33:18 +01:00
struct sk_buff * skb2 = NULL ;
2010-12-08 13:12:05 +02:00
int ncb_len = 0 ;
2014-07-07 18:33:18 +01:00
__le16 * ntb_data ;
__le16 * ntb_ndp ;
int dgram_pad ;
2010-12-08 13:12:05 +02:00
unsigned max_size = ncm - > port . fixed_in_len ;
2012-11-22 19:50:34 +01:00
const struct ndp_parser_opts * opts = ncm - > parser_opts ;
2014-07-07 18:33:18 +01:00
const int ndp_align = le16_to_cpu ( ntb_parameters . wNdpInAlignment ) ;
const int div = le16_to_cpu ( ntb_parameters . wNdpInDivisor ) ;
const int rem = le16_to_cpu ( ntb_parameters . wNdpInPayloadRemainder ) ;
const int dgram_idx_len = 2 * 2 * opts - > dgram_item_len ;
2010-12-08 13:12:05 +02:00
2014-07-07 18:33:18 +01:00
if ( ! skb & & ! ncm - > skb_tx_data )
2010-12-08 13:12:05 +02:00
return NULL ;
2014-07-07 18:33:18 +01:00
if ( skb ) {
/* Add the CRC if required up front */
if ( ncm - > is_crc ) {
uint32_t crc ;
__le16 * crc_pos ;
crc = ~ crc32_le ( ~ 0 ,
skb - > data ,
skb - > len ) ;
crc_pos = ( void * ) skb_put ( skb , sizeof ( uint32_t ) ) ;
put_unaligned_le32 ( crc , crc_pos ) ;
}
2010-12-08 13:12:05 +02:00
2014-07-07 18:33:18 +01:00
/* If the new skb is too big for the current NCM NTB then
* set the current stored skb to be sent now and clear it
* ready for new data .
* NOTE : Assume maximum align for speed of calculation .
*/
if ( ncm - > skb_tx_data
& & ( ncm - > ndp_dgram_count > = TX_MAX_NUM_DPE
| | ( ncm - > skb_tx_data - > len +
div + rem + skb - > len +
ncm - > skb_tx_ndp - > len + ndp_align + ( 2 * dgram_idx_len ) )
> max_size ) ) {
skb2 = package_for_tx ( ncm ) ;
if ( ! skb2 )
goto err ;
}
2010-12-08 13:12:05 +02:00
2014-07-07 18:33:18 +01:00
if ( ! ncm - > skb_tx_data ) {
ncb_len = opts - > nth_size ;
dgram_pad = ALIGN ( ncb_len , div ) + rem - ncb_len ;
ncb_len + = dgram_pad ;
2010-12-08 13:12:05 +02:00
2014-07-07 18:33:18 +01:00
/* Create a new skb for the NTH and datagrams. */
ncm - > skb_tx_data = alloc_skb ( max_size , GFP_ATOMIC ) ;
if ( ! ncm - > skb_tx_data )
goto err ;
2010-12-08 13:12:05 +02:00
2014-07-07 18:33:18 +01:00
ntb_data = ( void * ) skb_put ( ncm - > skb_tx_data , ncb_len ) ;
memset ( ntb_data , 0 , ncb_len ) ;
/* dwSignature */
put_unaligned_le32 ( opts - > nth_sign , ntb_data ) ;
ntb_data + = 2 ;
/* wHeaderLength */
put_unaligned_le16 ( opts - > nth_size , ntb_data + + ) ;
/* Allocate an skb for storing the NDP,
* TX_MAX_NUM_DPE should easily suffice for a
* 16 k packet .
*/
ncm - > skb_tx_ndp = alloc_skb ( ( int ) ( opts - > ndp_size
+ opts - > dpe_size
* TX_MAX_NUM_DPE ) ,
GFP_ATOMIC ) ;
if ( ! ncm - > skb_tx_ndp )
goto err ;
ntb_ndp = ( void * ) skb_put ( ncm - > skb_tx_ndp ,
opts - > ndp_size ) ;
memset ( ntb_ndp , 0 , ncb_len ) ;
/* dwSignature */
put_unaligned_le32 ( ncm - > ndp_sign , ntb_ndp ) ;
ntb_ndp + = 2 ;
2010-12-08 13:12:05 +02:00
2014-07-07 18:33:18 +01:00
/* There is always a zeroed entry */
ncm - > ndp_dgram_count = 1 ;
2010-12-08 13:12:05 +02:00
2014-07-07 18:33:18 +01:00
/* Note: we skip opts->next_ndp_index */
}
2010-12-08 13:12:05 +02:00
2014-07-07 18:33:18 +01:00
/* Delay the timer. */
hrtimer_start ( & ncm - > task_timer ,
ktime_set ( 0 , TX_TIMEOUT_NSECS ) ,
HRTIMER_MODE_REL ) ;
/* Add the datagram position entries */
ntb_ndp = ( void * ) skb_put ( ncm - > skb_tx_ndp , dgram_idx_len ) ;
memset ( ntb_ndp , 0 , dgram_idx_len ) ;
ncb_len = ncm - > skb_tx_data - > len ;
dgram_pad = ALIGN ( ncb_len , div ) + rem - ncb_len ;
ncb_len + = dgram_pad ;
/* (d)wDatagramIndex */
put_ncm ( & ntb_ndp , opts - > dgram_item_len , ncb_len ) ;
/* (d)wDatagramLength */
put_ncm ( & ntb_ndp , opts - > dgram_item_len , skb - > len ) ;
ncm - > ndp_dgram_count + + ;
/* Add the new data to the skb */
ntb_data = ( void * ) skb_put ( ncm - > skb_tx_data , dgram_pad ) ;
memset ( ntb_data , 0 , dgram_pad ) ;
ntb_data = ( void * ) skb_put ( ncm - > skb_tx_data , skb - > len ) ;
memcpy ( ntb_data , skb - > data , skb - > len ) ;
dev_kfree_skb_any ( skb ) ;
skb = NULL ;
2010-12-08 13:12:05 +02:00
2014-07-07 18:33:18 +01:00
} else if ( ncm - > skb_tx_data & & ncm - > timer_force_tx ) {
/* If the tx was requested because of a timeout then send */
skb2 = package_for_tx ( ncm ) ;
if ( ! skb2 )
goto err ;
2010-12-08 13:12:05 +02:00
}
2014-07-07 18:33:18 +01:00
return skb2 ;
err :
ncm - > netdev - > stats . tx_dropped + + ;
if ( skb )
dev_kfree_skb_any ( skb ) ;
if ( ncm - > skb_tx_data )
dev_kfree_skb_any ( ncm - > skb_tx_data ) ;
if ( ncm - > skb_tx_ndp )
dev_kfree_skb_any ( ncm - > skb_tx_ndp ) ;
return NULL ;
}
/*
* This transmits the NTB if there are frames waiting .
*/
static void ncm_tx_tasklet ( unsigned long data )
{
struct f_ncm * ncm = ( void * ) data ;
2010-12-08 13:12:05 +02:00
2014-07-07 18:33:18 +01:00
if ( ncm - > timer_stopping )
return ;
/* Only send if data is available. */
if ( ncm - > skb_tx_data ) {
ncm - > timer_force_tx = true ;
2014-08-27 17:05:53 -07:00
/* XXX This allowance of a NULL skb argument to ndo_start_xmit
* XXX is not sane . The gadget layer should be redesigned so
* XXX that the dev - > wrap ( ) invocations to build SKBs is transparent
* XXX and performed in some way outside of the ndo_start_xmit
* XXX interface .
*/
ncm - > netdev - > netdev_ops - > ndo_start_xmit ( NULL , ncm - > netdev ) ;
2014-07-07 18:33:18 +01:00
ncm - > timer_force_tx = false ;
}
}
2010-12-08 13:12:05 +02:00
2014-07-07 18:33:18 +01:00
/*
* The transmit should only be run if no skb data has been sent
* for a certain duration .
*/
static enum hrtimer_restart ncm_tx_timeout ( struct hrtimer * data )
{
struct f_ncm * ncm = container_of ( data , struct f_ncm , task_timer ) ;
tasklet_schedule ( & ncm - > tx_tasklet ) ;
return HRTIMER_NORESTART ;
2010-12-08 13:12:05 +02:00
}
static int ncm_unwrap_ntb ( struct gether * port ,
struct sk_buff * skb ,
struct sk_buff_head * list )
{
struct f_ncm * ncm = func_to_ncm ( & port - > func ) ;
__le16 * tmp = ( void * ) skb - > data ;
unsigned index , index2 ;
2014-07-07 18:33:17 +01:00
int ndp_index ;
2010-12-08 13:12:05 +02:00
unsigned dg_len , dg_len2 ;
unsigned ndp_len ;
struct sk_buff * skb2 ;
int ret = - EINVAL ;
unsigned max_size = le32_to_cpu ( ntb_parameters . dwNtbOutMaxSize ) ;
2012-11-22 19:50:34 +01:00
const struct ndp_parser_opts * opts = ncm - > parser_opts ;
2010-12-08 13:12:05 +02:00
unsigned crc_len = ncm - > is_crc ? sizeof ( uint32_t ) : 0 ;
int dgram_counter ;
/* dwSignature */
if ( get_unaligned_le32 ( tmp ) ! = opts - > nth_sign ) {
INFO ( port - > func . config - > cdev , " Wrong NTH SIGN, skblen %d \n " ,
skb - > len ) ;
print_hex_dump ( KERN_INFO , " HEAD: " , DUMP_PREFIX_ADDRESS , 32 , 1 ,
skb - > data , 32 , false ) ;
goto err ;
}
tmp + = 2 ;
/* wHeaderLength */
if ( get_unaligned_le16 ( tmp + + ) ! = opts - > nth_size ) {
INFO ( port - > func . config - > cdev , " Wrong NTB headersize \n " ) ;
goto err ;
}
tmp + + ; /* skip wSequence */
/* (d)wBlockLength */
if ( get_ncm ( & tmp , opts - > block_length ) > max_size ) {
INFO ( port - > func . config - > cdev , " OUT size exceeded \n " ) ;
goto err ;
}
2014-07-07 18:33:18 +01:00
ndp_index = get_ncm ( & tmp , opts - > ndp_index ) ;
2010-12-08 13:12:05 +02:00
2014-07-07 18:33:17 +01:00
/* Run through all the NDP's in the NTB */
2010-12-08 13:12:05 +02:00
do {
2014-07-07 18:33:17 +01:00
/* NCM 3.2 */
if ( ( ( ndp_index % 4 ) ! = 0 ) & &
( ndp_index < opts - > nth_size ) ) {
INFO ( port - > func . config - > cdev , " Bad index: %#X \n " ,
ndp_index ) ;
2010-12-08 13:12:05 +02:00
goto err ;
}
2014-07-07 18:33:17 +01:00
/* walk through NDP */
tmp = ( void * ) ( skb - > data + ndp_index ) ;
if ( get_unaligned_le32 ( tmp ) ! = ncm - > ndp_sign ) {
INFO ( port - > func . config - > cdev , " Wrong NDP SIGN \n " ) ;
goto err ;
2010-12-08 13:12:05 +02:00
}
2014-07-07 18:33:17 +01:00
tmp + = 2 ;
2010-12-08 13:12:05 +02:00
2014-07-07 18:33:17 +01:00
ndp_len = get_unaligned_le16 ( tmp + + ) ;
/*
* NCM 3.3 .1
* entry is 2 items
* item size is 16 / 32 bits , opts - > dgram_item_len * 2 bytes
* minimal : struct usb_cdc_ncm_ndpX + normal entry + zero entry
* Each entry is a dgram index and a dgram length .
*/
if ( ( ndp_len < opts - > ndp_size
+ 2 * 2 * ( opts - > dgram_item_len * 2 ) )
| | ( ndp_len % opts - > ndplen_align ! = 0 ) ) {
INFO ( port - > func . config - > cdev , " Bad NDP length: %#X \n " ,
ndp_len ) ;
goto err ;
}
tmp + = opts - > reserved1 ;
/* Check for another NDP (d)wNextNdpIndex */
2014-07-07 18:33:18 +01:00
ndp_index = get_ncm ( & tmp , opts - > next_ndp_index ) ;
2014-07-07 18:33:17 +01:00
tmp + = opts - > reserved2 ;
ndp_len - = opts - > ndp_size ;
2010-12-08 13:12:05 +02:00
index2 = get_ncm ( & tmp , opts - > dgram_item_len ) ;
dg_len2 = get_ncm ( & tmp , opts - > dgram_item_len ) ;
2014-07-07 18:33:17 +01:00
dgram_counter = 0 ;
do {
index = index2 ;
dg_len = dg_len2 ;
if ( dg_len < 14 + crc_len ) { /* ethernet hdr + crc */
INFO ( port - > func . config - > cdev ,
" Bad dgram length: %#X \n " , dg_len ) ;
goto err ;
}
if ( ncm - > is_crc ) {
uint32_t crc , crc2 ;
crc = get_unaligned_le32 ( skb - > data +
index + dg_len -
crc_len ) ;
crc2 = ~ crc32_le ( ~ 0 ,
skb - > data + index ,
dg_len - crc_len ) ;
if ( crc ! = crc2 ) {
INFO ( port - > func . config - > cdev ,
" Bad CRC \n " ) ;
goto err ;
}
}
index2 = get_ncm ( & tmp , opts - > dgram_item_len ) ;
dg_len2 = get_ncm ( & tmp , opts - > dgram_item_len ) ;
2010-12-08 13:12:05 +02:00
2014-07-07 18:33:19 +01:00
/*
* Copy the data into a new skb .
* This ensures the truesize is correct
*/
skb2 = netdev_alloc_skb_ip_align ( ncm - > netdev ,
dg_len - crc_len ) ;
2010-12-08 13:12:05 +02:00
if ( skb2 = = NULL )
goto err ;
2014-07-07 18:33:19 +01:00
memcpy ( skb_put ( skb2 , dg_len - crc_len ) ,
skb - > data + index , dg_len - crc_len ) ;
2010-12-08 13:12:05 +02:00
2014-07-07 18:33:17 +01:00
skb_queue_tail ( list , skb2 ) ;
2010-12-08 13:12:05 +02:00
2014-07-07 18:33:17 +01:00
ndp_len - = 2 * ( opts - > dgram_item_len * 2 ) ;
2010-12-08 13:12:05 +02:00
2014-07-07 18:33:17 +01:00
dgram_counter + + ;
2010-12-08 13:12:05 +02:00
2014-07-07 18:33:17 +01:00
if ( index2 = = 0 | | dg_len2 = = 0 )
break ;
} while ( ndp_len > 2 * ( opts - > dgram_item_len * 2 ) ) ;
} while ( ndp_index ) ;
dev_kfree_skb_any ( skb ) ;
2010-12-08 13:12:05 +02:00
VDBG ( port - > func . config - > cdev ,
" Parsed NTB with %d frames \n " , dgram_counter ) ;
return 0 ;
err :
skb_queue_purge ( list ) ;
dev_kfree_skb_any ( skb ) ;
return ret ;
}
static void ncm_disable ( struct usb_function * f )
{
struct f_ncm * ncm = func_to_ncm ( f ) ;
struct usb_composite_dev * cdev = f - > config - > cdev ;
DBG ( cdev , " ncm deactivated \n " ) ;
2015-09-16 12:10:50 +02:00
if ( ncm - > port . in_ep - > enabled ) {
2014-07-07 18:33:18 +01:00
ncm - > timer_stopping = true ;
ncm - > netdev = NULL ;
2010-12-08 13:12:05 +02:00
gether_disconnect ( & ncm - > port ) ;
2014-07-07 18:33:18 +01:00
}
2010-12-08 13:12:05 +02:00
2015-09-16 12:10:50 +02:00
if ( ncm - > notify - > enabled ) {
2010-12-08 13:12:05 +02:00
usb_ep_disable ( ncm - > notify ) ;
2011-06-28 16:33:48 +03:00
ncm - > notify - > desc = NULL ;
2010-12-08 13:12:05 +02:00
}
}
/*-------------------------------------------------------------------------*/
/*
* Callbacks let us notify the host about connect / disconnect when the
* net device is opened or closed .
*
* For testing , note that link states on this side include both opened
* and closed variants of :
*
* - disconnected / unconfigured
* - configured but inactive ( data alt 0 )
* - configured and active ( data alt 1 )
*
* Each needs to be tested with unplug , rmmod , SET_CONFIGURATION , and
* SET_INTERFACE ( altsetting ) . Remember also that " configured " doesn ' t
* imply the host is actually polling the notification endpoint , and
* likewise that " active " doesn ' t imply it ' s actually using the data
* endpoints for traffic .
*/
static void ncm_open ( struct gether * geth )
{
struct f_ncm * ncm = func_to_ncm ( & geth - > func ) ;
DBG ( ncm - > port . func . config - > cdev , " %s \n " , __func__ ) ;
spin_lock ( & ncm - > lock ) ;
ncm - > is_open = true ;
ncm_notify ( ncm ) ;
spin_unlock ( & ncm - > lock ) ;
}
static void ncm_close ( struct gether * geth )
{
struct f_ncm * ncm = func_to_ncm ( & geth - > func ) ;
DBG ( ncm - > port . func . config - > cdev , " %s \n " , __func__ ) ;
spin_lock ( & ncm - > lock ) ;
ncm - > is_open = false ;
ncm_notify ( ncm ) ;
spin_unlock ( & ncm - > lock ) ;
}
/*-------------------------------------------------------------------------*/
/* ethernet function driver setup/binding */
2013-05-23 09:22:06 +02:00
static int ncm_bind ( struct usb_configuration * c , struct usb_function * f )
2010-12-08 13:12:05 +02:00
{
struct usb_composite_dev * cdev = c - > cdev ;
struct f_ncm * ncm = func_to_ncm ( f ) ;
2013-05-23 09:22:09 +02:00
struct usb_string * us ;
2010-12-08 13:12:05 +02:00
int status ;
struct usb_ep * ep ;
2013-05-23 09:22:06 +02:00
struct f_ncm_opts * ncm_opts ;
if ( ! can_support_ecm ( cdev - > gadget ) )
return - EINVAL ;
ncm_opts = container_of ( f - > fi , struct f_ncm_opts , func_inst ) ;
/*
* in drivers / usb / gadget / configfs . c : configfs_composite_bind ( )
* configurations are bound in sequence with list_for_each_entry ,
* in each configuration its functions are bound in sequence
* with list_for_each_entry , so we assume no race condition
* with regard to ncm_opts - > bound access
*/
if ( ! ncm_opts - > bound ) {
2013-05-23 09:22:10 +02:00
mutex_lock ( & ncm_opts - > lock ) ;
2013-05-23 09:22:06 +02:00
gether_set_gadget ( ncm_opts - > net , cdev - > gadget ) ;
status = gether_register_netdev ( ncm_opts - > net ) ;
2013-05-23 09:22:10 +02:00
mutex_unlock ( & ncm_opts - > lock ) ;
2013-05-23 09:22:06 +02:00
if ( status )
return status ;
ncm_opts - > bound = true ;
}
2013-05-23 09:22:09 +02:00
us = usb_gstrings_attach ( cdev , ncm_strings ,
ARRAY_SIZE ( ncm_string_defs ) ) ;
if ( IS_ERR ( us ) )
return PTR_ERR ( us ) ;
ncm_control_intf . iInterface = us [ STRING_CTRL_IDX ] . id ;
ncm_data_nop_intf . iInterface = us [ STRING_DATA_IDX ] . id ;
ncm_data_intf . iInterface = us [ STRING_DATA_IDX ] . id ;
ecm_desc . iMACAddress = us [ STRING_MAC_IDX ] . id ;
ncm_iad_desc . iFunction = us [ STRING_IAD_IDX ] . id ;
2013-05-23 09:22:06 +02:00
2010-12-08 13:12:05 +02:00
/* allocate instance-specific interface IDs */
status = usb_interface_id ( c , f ) ;
if ( status < 0 )
goto fail ;
ncm - > ctrl_id = status ;
ncm_iad_desc . bFirstInterface = status ;
ncm_control_intf . bInterfaceNumber = status ;
ncm_union_desc . bMasterInterface0 = status ;
status = usb_interface_id ( c , f ) ;
if ( status < 0 )
goto fail ;
ncm - > data_id = status ;
ncm_data_nop_intf . bInterfaceNumber = status ;
ncm_data_intf . bInterfaceNumber = status ;
ncm_union_desc . bSlaveInterface0 = status ;
status = - ENODEV ;
/* allocate instance-specific endpoints */
ep = usb_ep_autoconfig ( cdev - > gadget , & fs_ncm_in_desc ) ;
if ( ! ep )
goto fail ;
ncm - > port . in_ep = ep ;
ep = usb_ep_autoconfig ( cdev - > gadget , & fs_ncm_out_desc ) ;
if ( ! ep )
goto fail ;
ncm - > port . out_ep = ep ;
ep = usb_ep_autoconfig ( cdev - > gadget , & fs_ncm_notify_desc ) ;
if ( ! ep )
goto fail ;
ncm - > notify = ep ;
status = - ENOMEM ;
/* allocate notification request and buffer */
ncm - > notify_req = usb_ep_alloc_request ( ep , GFP_KERNEL ) ;
if ( ! ncm - > notify_req )
goto fail ;
ncm - > notify_req - > buf = kmalloc ( NCM_STATUS_BYTECOUNT , GFP_KERNEL ) ;
if ( ! ncm - > notify_req - > buf )
goto fail ;
ncm - > notify_req - > context = ncm ;
ncm - > notify_req - > complete = ncm_notify_complete ;
/*
* support all relevant hardware speeds . . . we expect that when
* hardware is dual speed , all bulk - capable endpoints work at
* both speeds
*/
2012-10-22 22:15:06 +02:00
hs_ncm_in_desc . bEndpointAddress = fs_ncm_in_desc . bEndpointAddress ;
hs_ncm_out_desc . bEndpointAddress = fs_ncm_out_desc . bEndpointAddress ;
hs_ncm_notify_desc . bEndpointAddress =
fs_ncm_notify_desc . bEndpointAddress ;
2010-12-08 13:12:05 +02:00
2016-08-12 17:28:05 +03:00
ss_ncm_in_desc . bEndpointAddress = fs_ncm_in_desc . bEndpointAddress ;
ss_ncm_out_desc . bEndpointAddress = fs_ncm_out_desc . bEndpointAddress ;
ss_ncm_notify_desc . bEndpointAddress =
fs_ncm_notify_desc . bEndpointAddress ;
2012-10-22 22:15:06 +02:00
status = usb_assign_descriptors ( f , ncm_fs_function , ncm_hs_function ,
2016-08-12 17:28:05 +03:00
ncm_ss_function , NULL ) ;
2014-10-27 22:49:26 +05:30
if ( status )
goto fail ;
2010-12-08 13:12:05 +02:00
/*
* NOTE : all that is done without knowing or caring about
* the network link . . . which is unavailable to this code
* until we ' re activated via set_alt ( ) .
*/
ncm - > port . open = ncm_open ;
ncm - > port . close = ncm_close ;
2014-07-07 18:33:18 +01:00
tasklet_init ( & ncm - > tx_tasklet , ncm_tx_tasklet , ( unsigned long ) ncm ) ;
hrtimer_init ( & ncm - > task_timer , CLOCK_MONOTONIC , HRTIMER_MODE_REL ) ;
ncm - > task_timer . function = ncm_tx_timeout ;
2010-12-08 13:12:05 +02:00
DBG ( cdev , " CDC Network: %s speed IN/%s OUT/%s NOTIFY/%s \n " ,
2016-08-12 17:28:05 +03:00
gadget_is_superspeed ( c - > cdev - > gadget ) ? " super " :
2010-12-08 13:12:05 +02:00
gadget_is_dualspeed ( c - > cdev - > gadget ) ? " dual " : " full " ,
ncm - > port . in_ep - > name , ncm - > port . out_ep - > name ,
ncm - > notify - > name ) ;
return 0 ;
fail :
if ( ncm - > notify_req ) {
kfree ( ncm - > notify_req - > buf ) ;
usb_ep_free_request ( ncm - > notify , ncm - > notify_req ) ;
}
ERROR ( cdev , " %s: can't bind, err %d \n " , f - > name , status ) ;
return status ;
}
2013-05-23 09:22:10 +02:00
static inline struct f_ncm_opts * to_f_ncm_opts ( struct config_item * item )
{
return container_of ( to_config_group ( item ) , struct f_ncm_opts ,
func_inst . group ) ;
}
2013-05-23 10:32:02 +02:00
/* f_ncm_item_ops */
USB_ETHERNET_CONFIGFS_ITEM ( ncm ) ;
2013-05-23 09:22:10 +02:00
2013-05-23 10:32:02 +02:00
/* f_ncm_opts_dev_addr */
USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR ( ncm ) ;
2013-05-23 09:22:10 +02:00
2013-05-23 10:32:02 +02:00
/* f_ncm_opts_host_addr */
USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR ( ncm ) ;
2013-05-23 09:22:10 +02:00
2013-05-23 10:32:02 +02:00
/* f_ncm_opts_qmult */
USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT ( ncm ) ;
2013-05-23 09:22:10 +02:00
2013-05-23 10:32:02 +02:00
/* f_ncm_opts_ifname */
USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME ( ncm ) ;
2013-05-23 09:22:10 +02:00
static struct configfs_attribute * ncm_attrs [ ] = {
2015-10-03 15:32:42 +02:00
& ncm_opts_attr_dev_addr ,
& ncm_opts_attr_host_addr ,
& ncm_opts_attr_qmult ,
& ncm_opts_attr_ifname ,
2013-05-23 09:22:10 +02:00
NULL ,
} ;
static struct config_item_type ncm_func_type = {
. ct_item_ops = & ncm_item_ops ,
. ct_attrs = ncm_attrs ,
. ct_owner = THIS_MODULE ,
} ;
2013-05-23 09:22:06 +02:00
static void ncm_free_inst ( struct usb_function_instance * f )
{
struct f_ncm_opts * opts ;
opts = container_of ( f , struct f_ncm_opts , func_inst ) ;
if ( opts - > bound )
gether_cleanup ( netdev_priv ( opts - > net ) ) ;
else
free_netdev ( opts - > net ) ;
kfree ( opts ) ;
}
static struct usb_function_instance * ncm_alloc_inst ( void )
{
struct f_ncm_opts * opts ;
opts = kzalloc ( sizeof ( * opts ) , GFP_KERNEL ) ;
if ( ! opts )
return ERR_PTR ( - ENOMEM ) ;
2013-05-23 09:22:10 +02:00
mutex_init ( & opts - > lock ) ;
2013-05-23 09:22:06 +02:00
opts - > func_inst . free_func_inst = ncm_free_inst ;
opts - > net = gether_setup_default ( ) ;
2013-07-25 09:13:18 +02:00
if ( IS_ERR ( opts - > net ) ) {
struct net_device * net = opts - > net ;
kfree ( opts ) ;
return ERR_CAST ( net ) ;
}
2013-05-23 09:22:06 +02:00
2013-05-23 09:22:10 +02:00
config_group_init_type_name ( & opts - > func_inst . group , " " , & ncm_func_type ) ;
2013-05-23 09:22:06 +02:00
return & opts - > func_inst ;
}
static void ncm_free ( struct usb_function * f )
{
struct f_ncm * ncm ;
2013-05-23 09:22:10 +02:00
struct f_ncm_opts * opts ;
2013-05-23 09:22:06 +02:00
ncm = func_to_ncm ( f ) ;
2013-05-23 09:22:10 +02:00
opts = container_of ( f - > fi , struct f_ncm_opts , func_inst ) ;
2013-05-23 09:22:06 +02:00
kfree ( ncm ) ;
2013-05-23 09:22:10 +02:00
mutex_lock ( & opts - > lock ) ;
opts - > refcnt - - ;
mutex_unlock ( & opts - > lock ) ;
2013-05-23 09:22:06 +02:00
}
static void ncm_unbind ( struct usb_configuration * c , struct usb_function * f )
{
struct f_ncm * ncm = func_to_ncm ( f ) ;
DBG ( c - > cdev , " ncm unbind \n " ) ;
2014-07-07 18:33:18 +01:00
hrtimer_cancel ( & ncm - > task_timer ) ;
tasklet_kill ( & ncm - > tx_tasklet ) ;
ncm_string_defs [ 0 ] . id = 0 ;
2013-05-23 09:22:06 +02:00
usb_free_all_descriptors ( f ) ;
kfree ( ncm - > notify_req - > buf ) ;
usb_ep_free_request ( ncm - > notify , ncm - > notify_req ) ;
}
2013-12-16 18:43:32 +09:00
static struct usb_function * ncm_alloc ( struct usb_function_instance * fi )
2013-05-23 09:22:06 +02:00
{
struct f_ncm * ncm ;
struct f_ncm_opts * opts ;
int status ;
/* allocate and initialize one new instance */
ncm = kzalloc ( sizeof ( * ncm ) , GFP_KERNEL ) ;
if ( ! ncm )
return ERR_PTR ( - ENOMEM ) ;
opts = container_of ( fi , struct f_ncm_opts , func_inst ) ;
2013-05-23 09:22:10 +02:00
mutex_lock ( & opts - > lock ) ;
opts - > refcnt + + ;
2013-05-23 09:22:06 +02:00
/* export host's Ethernet address in CDC format */
status = gether_get_host_addr_cdc ( opts - > net , ncm - > ethaddr ,
sizeof ( ncm - > ethaddr ) ) ;
if ( status < 12 ) { /* strlen("01234567890a") */
kfree ( ncm ) ;
2013-06-18 11:43:29 +08:00
mutex_unlock ( & opts - > lock ) ;
2013-05-23 09:22:06 +02:00
return ERR_PTR ( - EINVAL ) ;
}
ncm_string_defs [ STRING_MAC_IDX ] . s = ncm - > ethaddr ;
spin_lock_init ( & ncm - > lock ) ;
ncm_reset_values ( ncm ) ;
ncm - > port . ioport = netdev_priv ( opts - > net ) ;
2013-05-23 09:22:10 +02:00
mutex_unlock ( & opts - > lock ) ;
2013-05-23 09:22:06 +02:00
ncm - > port . is_fixed = true ;
2014-07-07 18:33:18 +01:00
ncm - > port . supports_multi_frame = true ;
2013-05-23 09:22:06 +02:00
ncm - > port . func . name = " cdc_network " ;
/* descriptors are per-instance copies */
ncm - > port . func . bind = ncm_bind ;
ncm - > port . func . unbind = ncm_unbind ;
ncm - > port . func . set_alt = ncm_set_alt ;
ncm - > port . func . get_alt = ncm_get_alt ;
ncm - > port . func . setup = ncm_setup ;
ncm - > port . func . disable = ncm_disable ;
ncm - > port . func . free_func = ncm_free ;
ncm - > port . wrap = ncm_wrap_ntb ;
ncm - > port . unwrap = ncm_unwrap_ntb ;
return & ncm - > port . func ;
}
DECLARE_USB_FUNCTION_INIT ( ncm , ncm_alloc_inst , ncm_alloc ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Yauheni Kaliuta " ) ;