2005-04-17 02:20:36 +04:00
/*
* ether . c - - Ethernet gadget driver , with CDC and non - CDC options
*
* Copyright ( C ) 2003 - 2005 David Brownell
* Copyright ( C ) 2003 - 2004 Robert Schwebel , Benedikt Spranger
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
2007-08-02 11:00:38 +04:00
/* #define VERBOSE_DEBUG */
2005-04-17 02:20:36 +04:00
# include <linux/kernel.h>
# include <linux/utsname.h>
# include <linux/device.h>
# include <linux/ctype.h>
2007-08-02 11:00:38 +04:00
# include <linux/etherdevice.h>
# include <linux/ethtool.h>
2005-04-17 02:20:36 +04:00
2006-12-17 02:34:53 +03:00
# include <linux/usb/ch9.h>
2006-06-13 20:57:47 +04:00
# include <linux/usb/cdc.h>
2007-10-05 05:05:17 +04:00
# include <linux/usb/gadget.h>
2005-04-17 02:20:36 +04:00
# include "gadget_chips.h"
/*-------------------------------------------------------------------------*/
/*
* Ethernet gadget driver - - with CDC and non - CDC options
* Builds on hardware support for a full duplex link .
*
* CDC Ethernet is the standard USB solution for sending Ethernet frames
* using USB . Real hardware tends to use the same framing protocol but look
* different for control features . This driver strongly prefers to use
* this USB - IF standard as its open - systems interoperability solution ;
* most host side USB stacks ( except from Microsoft ) support it .
*
* There ' s some hardware that can ' t talk CDC . We make that hardware
* implement a " minimalist " vendor - agnostic CDC core : same framing , but
2006-12-12 02:59:04 +03:00
* link - level setup only requires activating the configuration . Only the
* endpoint descriptors , and product / vendor IDs , are relevant ; no control
* operations are available . Linux supports it , but other host operating
* systems may not . ( This is a subset of CDC Ethernet . )
*
* It turns out that if you add a few descriptors to that " CDC Subset " ,
* ( Windows ) host side drivers from MCCI can treat it as one submode of
* a proprietary scheme called " SAFE " . . . without needing to know about
* specific product / vendor IDs . So we do that , making it easier to use
* those MS - Windows drivers . Those added descriptors make it resemble a
* CDC MDLM device , but they don ' t change device behavior at all . ( See
* MCCI Engineering report 950198 " SAFE Networking Functions " . )
2005-04-17 02:20:36 +04:00
*
* A third option is also in use . Rather than CDC Ethernet , or something
* simpler , Microsoft pushes their own approach : RNDIS . The published
* RNDIS specs are ambiguous and appear to be incomplete , and are also
* needlessly complex .
*/
# define DRIVER_DESC "Ethernet Gadget"
2005-04-29 00:48:09 +04:00
# define DRIVER_VERSION "May Day 2005"
2005-04-17 02:20:36 +04:00
static const char shortname [ ] = " ether " ;
static const char driver_desc [ ] = DRIVER_DESC ;
# define RX_EXTRA 20 /* guard against rx overflows */
# include "rndis.h"
2005-05-16 19:26:38 +04:00
# ifndef CONFIG_USB_ETH_RNDIS
# define rndis_uninit(x) do{}while(0)
# define rndis_deregister(c) do{}while(0)
# define rndis_exit() do{}while(0)
2005-04-17 02:20:36 +04:00
# endif
/* CDC and RNDIS support the same host-chosen outgoing packet filters. */
# define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \
2006-06-13 20:54:40 +04:00
| USB_CDC_PACKET_TYPE_ALL_MULTICAST \
| USB_CDC_PACKET_TYPE_PROMISCUOUS \
| USB_CDC_PACKET_TYPE_DIRECTED )
2005-04-17 02:20:36 +04:00
/*-------------------------------------------------------------------------*/
struct eth_dev {
spinlock_t lock ;
struct usb_gadget * gadget ;
struct usb_request * req ; /* for control responses */
struct usb_request * stat_req ; /* for cdc & rndis status */
u8 config ;
struct usb_ep * in_ep , * out_ep , * status_ep ;
const struct usb_endpoint_descriptor
* in , * out , * status ;
2006-08-22 02:26:38 +04:00
spinlock_t req_lock ;
2005-04-17 02:20:36 +04:00
struct list_head tx_reqs , rx_reqs ;
struct net_device * net ;
struct net_device_stats stats ;
atomic_t tx_qlen ;
struct work_struct work ;
unsigned zlp : 1 ;
unsigned cdc : 1 ;
unsigned rndis : 1 ;
unsigned suspended : 1 ;
u16 cdc_filter ;
unsigned long todo ;
# define WORK_RX_MEMORY 0
int rndis_config ;
u8 host_mac [ ETH_ALEN ] ;
} ;
/* This version autoconfigures as much as possible at run-time.
*
* It also ASSUMES a self - powered device , without remote wakeup ,
* although remote wakeup support would make sense .
*/
/*-------------------------------------------------------------------------*/
/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
* Instead : allocate your own , using normal USB - IF procedures .
*/
/* Thanks to NetChip Technologies for donating this product ID.
* It ' s for devices with only CDC Ethernet configurations .
*/
# define CDC_VENDOR_NUM 0x0525 /* NetChip */
# define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */
/* For hardware that can't talk CDC, we use the same vendor ID that
* ARM Linux has used for ethernet - over - usb , both with sa1100 and
* with pxa250 . We ' re protocol - compatible , if the host - side drivers
* use the endpoint descriptors . bcdDevice ( version ) is nonzero , so
* drivers that need to hard - wire endpoint numbers have a hook .
*
* The protocol is a minimal subset of CDC Ether , which works on any bulk
* hardware that ' s not deeply broken . . . even on hardware that can ' t talk
* RNDIS ( like SA - 1100 , with no interrupt endpoint , or anything that
* doesn ' t handle control - OUT ) .
*/
# define SIMPLE_VENDOR_NUM 0x049f
# define SIMPLE_PRODUCT_NUM 0x505a
/* For hardware that can talk RNDIS and either of the above protocols,
* use this ID . . . the windows INF files will know it . Unless it ' s
* used with CDC Ethernet , Linux 2.4 hosts will need updates to choose
* the non - RNDIS configuration .
*/
# define RNDIS_VENDOR_NUM 0x0525 /* NetChip */
# define RNDIS_PRODUCT_NUM 0xa4a2 /* Ethernet/RNDIS Gadget */
/* Some systems will want different product identifers published in the
* device descriptor , either numbers or strings or both . These string
* parameters are in UTF - 8 ( superset of ASCII ' s 7 bit characters ) .
*/
2006-02-18 23:31:23 +03:00
static ushort idVendor ;
2005-04-17 02:20:36 +04:00
module_param ( idVendor , ushort , S_IRUGO ) ;
MODULE_PARM_DESC ( idVendor , " USB Vendor ID " ) ;
2006-02-18 23:31:23 +03:00
static ushort idProduct ;
2005-04-17 02:20:36 +04:00
module_param ( idProduct , ushort , S_IRUGO ) ;
MODULE_PARM_DESC ( idProduct , " USB Product ID " ) ;
2006-02-18 23:31:23 +03:00
static ushort bcdDevice ;
2005-04-17 02:20:36 +04:00
module_param ( bcdDevice , ushort , S_IRUGO ) ;
MODULE_PARM_DESC ( bcdDevice , " USB Device version (BCD) " ) ;
2006-02-18 23:31:23 +03:00
static char * iManufacturer ;
2005-04-17 02:20:36 +04:00
module_param ( iManufacturer , charp , S_IRUGO ) ;
MODULE_PARM_DESC ( iManufacturer , " USB Manufacturer string " ) ;
2006-02-18 23:31:23 +03:00
static char * iProduct ;
2005-04-17 02:20:36 +04:00
module_param ( iProduct , charp , S_IRUGO ) ;
MODULE_PARM_DESC ( iProduct , " USB Product string " ) ;
2006-02-18 23:31:23 +03:00
static char * iSerialNumber ;
module_param ( iSerialNumber , charp , S_IRUGO ) ;
MODULE_PARM_DESC ( iSerialNumber , " SerialNumber " ) ;
2005-04-17 02:20:36 +04:00
/* initial value, changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx" */
2006-02-18 23:31:23 +03:00
static char * dev_addr ;
2005-04-17 02:20:36 +04:00
module_param ( dev_addr , charp , S_IRUGO ) ;
MODULE_PARM_DESC ( dev_addr , " Device Ethernet Address " ) ;
/* this address is invisible to ifconfig */
2006-02-18 23:31:23 +03:00
static char * host_addr ;
2005-04-17 02:20:36 +04:00
module_param ( host_addr , charp , S_IRUGO ) ;
MODULE_PARM_DESC ( host_addr , " Host Ethernet Address " ) ;
/*-------------------------------------------------------------------------*/
/* Include CDC support if we could run on CDC-capable hardware. */
# ifdef CONFIG_USB_GADGET_NET2280
# define DEV_CONFIG_CDC
# endif
# ifdef CONFIG_USB_GADGET_DUMMY_HCD
# define DEV_CONFIG_CDC
# endif
# ifdef CONFIG_USB_GADGET_GOKU
# define DEV_CONFIG_CDC
# endif
# ifdef CONFIG_USB_GADGET_LH7A40X
# define DEV_CONFIG_CDC
# endif
# ifdef CONFIG_USB_GADGET_MQ11XX
# define DEV_CONFIG_CDC
# endif
# ifdef CONFIG_USB_GADGET_OMAP
# define DEV_CONFIG_CDC
# endif
# ifdef CONFIG_USB_GADGET_N9604
# define DEV_CONFIG_CDC
# endif
# ifdef CONFIG_USB_GADGET_PXA27X
# define DEV_CONFIG_CDC
# endif
2006-12-12 02:59:04 +03:00
# ifdef CONFIG_USB_GADGET_S3C2410
# define DEV_CONFIG_CDC
# endif
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_USB_GADGET_AT91
# define DEV_CONFIG_CDC
# endif
2006-01-25 19:45:59 +03:00
# ifdef CONFIG_USB_GADGET_MUSBHSFC
# define DEV_CONFIG_CDC
# endif
2006-06-30 09:27:36 +04:00
# ifdef CONFIG_USB_GADGET_MUSB_HDRC
2006-01-25 19:45:59 +03:00
# define DEV_CONFIG_CDC
# endif
2007-06-14 20:01:45 +04:00
# ifdef CONFIG_USB_GADGET_ATMEL_USBA
2006-02-27 20:15:04 +03:00
# define DEV_CONFIG_CDC
# endif
2007-04-23 21:37:36 +04:00
# ifdef CONFIG_USB_GADGET_FSL_USB2
# define DEV_CONFIG_CDC
# endif
2005-04-17 02:20:36 +04:00
/* For CDC-incapable hardware, choose the simple cdc subset.
* Anything that talks bulk ( without notable bugs ) can do this .
*/
# ifdef CONFIG_USB_GADGET_PXA2XX
# define DEV_CONFIG_SUBSET
# endif
2007-05-09 08:01:30 +04:00
# ifdef CONFIG_USB_GADGET_SUPERH
2005-04-17 02:20:36 +04:00
# define DEV_CONFIG_SUBSET
# endif
# ifdef CONFIG_USB_GADGET_SA1100
/* use non-CDC for backwards compatibility */
# define DEV_CONFIG_SUBSET
# endif
2007-05-10 08:18:23 +04:00
# ifdef CONFIG_USB_GADGET_M66592
# define DEV_CONFIG_CDC
# endif
2007-07-17 08:40:54 +04:00
# ifdef CONFIG_USB_GADGET_AMD5536UDC
# define DEV_CONFIG_CDC
# endif
2005-04-17 02:20:36 +04:00
/*-------------------------------------------------------------------------*/
/* "main" config is either CDC, or its simple subset */
static inline int is_cdc ( struct eth_dev * dev )
{
# if !defined(DEV_CONFIG_SUBSET)
return 1 ; /* only cdc possible */
# elif !defined (DEV_CONFIG_CDC)
return 0 ; /* only subset possible */
# else
return dev - > cdc ; /* depends on what hardware we found */
# endif
}
/* "secondary" RNDIS config may sometimes be activated */
static inline int rndis_active ( struct eth_dev * dev )
{
# ifdef CONFIG_USB_ETH_RNDIS
return dev - > rndis ;
# else
return 0 ;
# endif
}
# define subset_active(dev) (!is_cdc(dev) && !rndis_active(dev))
# define cdc_active(dev) ( is_cdc(dev) && !rndis_active(dev))
# define DEFAULT_QLEN 2 /* double buffering by default */
/* peak bulk transfer bits-per-second */
2006-06-13 20:54:40 +04:00
# define HS_BPS (13 * 512 * 8 * 1000 * 8)
2005-04-17 02:20:36 +04:00
# define FS_BPS (19 * 64 * 1 * 1000 * 8)
# ifdef CONFIG_USB_GADGET_DUALSPEED
2005-04-29 00:48:09 +04:00
# define DEVSPEED USB_SPEED_HIGH
2005-04-17 02:20:36 +04:00
static unsigned qmult = 5 ;
module_param ( qmult , uint , S_IRUGO | S_IWUSR ) ;
/* for dual-speed hardware, use deeper queues at highspeed */
# define qlen(gadget) \
( DEFAULT_QLEN * ( ( gadget - > speed = = USB_SPEED_HIGH ) ? qmult : 1 ) )
2005-04-19 04:39:34 +04:00
static inline int BITRATE ( struct usb_gadget * g )
{
return ( g - > speed = = USB_SPEED_HIGH ) ? HS_BPS : FS_BPS ;
}
2005-04-17 02:20:36 +04:00
# else /* full speed (low speed doesn't do bulk) */
2007-08-02 11:00:38 +04:00
# define qmult 1
2005-04-29 00:48:09 +04:00
# define DEVSPEED USB_SPEED_FULL
2005-04-17 02:20:36 +04:00
# define qlen(gadget) DEFAULT_QLEN
2005-04-19 04:39:34 +04:00
static inline int BITRATE ( struct usb_gadget * g )
{
return FS_BPS ;
}
2005-04-17 02:20:36 +04:00
# endif
/*-------------------------------------------------------------------------*/
# define xprintk(d,level,fmt,args...) \
printk ( level " %s: " fmt , ( d ) - > net - > name , # # args )
# ifdef DEBUG
# undef DEBUG
# define DEBUG(dev,fmt,args...) \
xprintk ( dev , KERN_DEBUG , fmt , # # args )
# else
# define DEBUG(dev,fmt,args...) \
do { } while ( 0 )
# endif /* DEBUG */
2007-08-02 11:00:38 +04:00
# ifdef VERBOSE_DEBUG
2005-04-17 02:20:36 +04:00
# define VDEBUG DEBUG
# else
# define VDEBUG(dev,fmt,args...) \
do { } while ( 0 )
# endif /* DEBUG */
# define ERROR(dev,fmt,args...) \
xprintk ( dev , KERN_ERR , fmt , # # args )
# define WARN(dev,fmt,args...) \
xprintk ( dev , KERN_WARNING , fmt , # # args )
# define INFO(dev,fmt,args...) \
xprintk ( dev , KERN_INFO , fmt , # # args )
/*-------------------------------------------------------------------------*/
/* USB DRIVER HOOKUP (to the hardware driver, below us), mostly
* ep0 implementation : descriptors , config management , setup ( ) .
* also optional class - specific notification interrupt transfer .
*/
/*
* DESCRIPTORS . . . most are static , but strings and ( full ) configuration
* descriptors are built on demand . For now we do either full CDC , or
* our simple subset , with RNDIS as an optional second configuration .
*
* RNDIS includes some CDC ACM descriptors . . . like CDC Ethernet . But
* the class descriptors match a modem ( they ' re ignored ; it ' s really just
* Ethernet functionality ) , they don ' t need the NOP altsetting , and the
* status transfer endpoint isn ' t optional .
*/
# define STRING_MANUFACTURER 1
# define STRING_PRODUCT 2
# define STRING_ETHADDR 3
# define STRING_DATA 4
# define STRING_CONTROL 5
# define STRING_RNDIS_CONTROL 6
# define STRING_CDC 7
# define STRING_SUBSET 8
# define STRING_RNDIS 9
2006-02-18 23:31:23 +03:00
# define STRING_SERIALNUMBER 10
2005-04-17 02:20:36 +04:00
2005-04-29 00:45:25 +04:00
/* holds our biggest descriptor (or RNDIS response) */
# define USB_BUFSIZ 256
2005-04-17 02:20:36 +04:00
/*
* This device advertises one configuration , eth_config , unless RNDIS
* is enabled ( rndis_config ) on hardware supporting at least two configs .
*
* NOTE : Controllers like superh_udc should probably be able to use
* an RNDIS - only configuration .
*
* FIXME define some higher - powered configurations to make it easier
* to recharge batteries . . .
*/
# define DEV_CONFIG_VALUE 1 /* cdc or subset */
# define DEV_RNDIS_CONFIG_VALUE 2 /* rndis; optional */
static struct usb_device_descriptor
device_desc = {
. bLength = sizeof device_desc ,
. bDescriptorType = USB_DT_DEVICE ,
. bcdUSB = __constant_cpu_to_le16 ( 0x0200 ) ,
. bDeviceClass = USB_CLASS_COMM ,
. bDeviceSubClass = 0 ,
. bDeviceProtocol = 0 ,
. idVendor = __constant_cpu_to_le16 ( CDC_VENDOR_NUM ) ,
. idProduct = __constant_cpu_to_le16 ( CDC_PRODUCT_NUM ) ,
. iManufacturer = STRING_MANUFACTURER ,
. iProduct = STRING_PRODUCT ,
. bNumConfigurations = 1 ,
} ;
static struct usb_otg_descriptor
otg_descriptor = {
. bLength = sizeof otg_descriptor ,
. bDescriptorType = USB_DT_OTG ,
. bmAttributes = USB_OTG_SRP ,
} ;
static struct usb_config_descriptor
eth_config = {
. bLength = sizeof eth_config ,
. bDescriptorType = USB_DT_CONFIG ,
/* compute wTotalLength on the fly */
. bNumInterfaces = 2 ,
. bConfigurationValue = DEV_CONFIG_VALUE ,
. iConfiguration = STRING_CDC ,
. bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER ,
. bMaxPower = 50 ,
} ;
# ifdef CONFIG_USB_ETH_RNDIS
2006-06-13 20:54:40 +04:00
static struct usb_config_descriptor
2005-04-17 02:20:36 +04:00
rndis_config = {
. bLength = sizeof rndis_config ,
. bDescriptorType = USB_DT_CONFIG ,
/* compute wTotalLength on the fly */
. bNumInterfaces = 2 ,
. bConfigurationValue = DEV_RNDIS_CONFIG_VALUE ,
. iConfiguration = STRING_RNDIS ,
. bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER ,
. bMaxPower = 50 ,
} ;
# endif
/*
* Compared to the simple CDC subset , the full CDC Ethernet model adds
* three class descriptors , two interface descriptors , optional status
* endpoint . Both have a " data " interface and two bulk endpoints .
* There are also differences in how control requests are handled .
*
2006-12-12 02:59:04 +03:00
* RNDIS shares a lot with CDC - Ethernet , since it ' s a variant of the
* CDC - ACM ( modem ) spec . Unfortunately MSFT ' s RNDIS driver is buggy ; it
* may hang or oops . Since bugfixes ( or accurate specs , letting Linux
* work around those bugs ) are unlikely to ever come from MSFT , you may
* wish to avoid using RNDIS .
*
* MCCI offers an alternative to RNDIS if you need to connect to Windows
* but have hardware that can ' t support CDC Ethernet . We add descriptors
* to present the CDC Subset as a ( nonconformant ) CDC MDLM variant called
* " SAFE " . That borrows from both CDC Ethernet and CDC MDLM . You can
* get those drivers from MCCI , or bundled with various products .
2005-04-17 02:20:36 +04:00
*/
# ifdef DEV_CONFIG_CDC
static struct usb_interface_descriptor
control_intf = {
. bLength = sizeof control_intf ,
. bDescriptorType = USB_DT_INTERFACE ,
. bInterfaceNumber = 0 ,
/* status endpoint is optional; this may be patched later */
. bNumEndpoints = 1 ,
. bInterfaceClass = USB_CLASS_COMM ,
. bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET ,
. bInterfaceProtocol = USB_CDC_PROTO_NONE ,
. iInterface = STRING_CONTROL ,
} ;
# endif
# ifdef CONFIG_USB_ETH_RNDIS
static const struct usb_interface_descriptor
rndis_control_intf = {
. bLength = sizeof rndis_control_intf ,
. bDescriptorType = USB_DT_INTERFACE ,
2006-06-13 20:54:40 +04:00
2005-04-17 02:20:36 +04:00
. bInterfaceNumber = 0 ,
. bNumEndpoints = 1 ,
. bInterfaceClass = USB_CLASS_COMM ,
. bInterfaceSubClass = USB_CDC_SUBCLASS_ACM ,
. bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR ,
. iInterface = STRING_RNDIS_CONTROL ,
} ;
# endif
static const struct usb_cdc_header_desc header_desc = {
. bLength = sizeof header_desc ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
. bDescriptorSubType = USB_CDC_HEADER_TYPE ,
. bcdCDC = __constant_cpu_to_le16 ( 0x0110 ) ,
} ;
2006-12-12 02:59:04 +03:00
# if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
2005-04-17 02:20:36 +04:00
static const struct usb_cdc_union_desc union_desc = {
. bLength = sizeof union_desc ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
. bDescriptorSubType = USB_CDC_UNION_TYPE ,
. bMasterInterface0 = 0 , /* index of control interface */
. bSlaveInterface0 = 1 , /* index of DATA interface */
} ;
# endif /* CDC || RNDIS */
# ifdef CONFIG_USB_ETH_RNDIS
static const struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor = {
2006-06-13 20:54:40 +04:00
. bLength = sizeof call_mgmt_descriptor ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
. bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE ,
2005-04-17 02:20:36 +04:00
2006-06-13 20:54:40 +04:00
. bmCapabilities = 0x00 ,
. bDataInterface = 0x01 ,
2005-04-17 02:20:36 +04:00
} ;
2005-04-29 00:48:09 +04:00
static const struct usb_cdc_acm_descriptor acm_descriptor = {
2006-06-13 20:54:40 +04:00
. bLength = sizeof acm_descriptor ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
. bDescriptorSubType = USB_CDC_ACM_TYPE ,
2005-04-17 02:20:36 +04:00
2006-06-13 20:54:40 +04:00
. bmCapabilities = 0x00 ,
2005-04-17 02:20:36 +04:00
} ;
# endif
2006-12-12 02:59:04 +03:00
# ifndef DEV_CONFIG_CDC
/* "SAFE" loosely follows CDC WMC MDLM, violating the spec in various
* ways : data endpoints live in the control interface , there ' s no data
* interface , and it ' s not used to talk to a cell phone radio .
*/
static const struct usb_cdc_mdlm_desc mdlm_desc = {
. bLength = sizeof mdlm_desc ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
. bDescriptorSubType = USB_CDC_MDLM_TYPE ,
. bcdVersion = __constant_cpu_to_le16 ( 0x0100 ) ,
. bGUID = {
0x5d , 0x34 , 0xcf , 0x66 , 0x11 , 0x18 , 0x11 , 0xd6 ,
0xa2 , 0x1a , 0x00 , 0x01 , 0x02 , 0xca , 0x9a , 0x7f ,
} ,
} ;
/* since "usb_cdc_mdlm_detail_desc" is a variable length structure, we
* can ' t really use its struct . All we do here is say that we ' re using
* the submode of " SAFE " which directly matches the CDC Subset .
*/
static const u8 mdlm_detail_desc [ ] = {
6 ,
USB_DT_CS_INTERFACE ,
USB_CDC_MDLM_DETAIL_TYPE ,
0 , /* "SAFE" */
0 , /* network control capabilities (none) */
0 , /* network data capabilities ("raw" encapsulation) */
} ;
# endif
2005-04-17 02:20:36 +04:00
static const struct usb_cdc_ether_desc ether_desc = {
. bLength = sizeof ether_desc ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
. bDescriptorSubType = USB_CDC_ETHERNET_TYPE ,
/* this descriptor actually adds value, surprise! */
. iMACAddress = STRING_ETHADDR ,
. bmEthernetStatistics = __constant_cpu_to_le32 ( 0 ) , /* no statistics */
. wMaxSegmentSize = __constant_cpu_to_le16 ( ETH_FRAME_LEN ) ,
. wNumberMCFilters = __constant_cpu_to_le16 ( 0 ) ,
. bNumberPowerFilters = 0 ,
} ;
# if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
/* include the status endpoint if we can, even where it's optional.
* use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one
2005-05-04 05:07:24 +04:00
* packet , to simplify cancellation ; and a big transfer interval , to
2005-04-17 02:20:36 +04:00
* waste less bandwidth .
*
* some drivers ( like Linux 2.4 cdc - ether ! ) " need " it to exist even
* if they ignore the connect / disconnect notifications that real aether
* can provide . more advanced cdc configurations might want to support
* encapsulated commands ( vendor - specific , using control - OUT ) .
*
* RNDIS requires the status endpoint , since it uses that encapsulation
* mechanism for its funky RPC scheme .
*/
2006-06-13 20:54:40 +04:00
2005-04-17 02:20:36 +04:00
# define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */
# define STATUS_BYTECOUNT 16 /* 8 byte header + data */
static struct usb_endpoint_descriptor
fs_status_desc = {
. bLength = USB_DT_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_ENDPOINT ,
. bEndpointAddress = USB_DIR_IN ,
. bmAttributes = USB_ENDPOINT_XFER_INT ,
. wMaxPacketSize = __constant_cpu_to_le16 ( STATUS_BYTECOUNT ) ,
. bInterval = 1 < < LOG2_STATUS_INTERVAL_MSEC ,
} ;
# endif
# ifdef DEV_CONFIG_CDC
/* the default data interface has no endpoints ... */
static const struct usb_interface_descriptor
data_nop_intf = {
. bLength = sizeof data_nop_intf ,
. bDescriptorType = USB_DT_INTERFACE ,
. bInterfaceNumber = 1 ,
. bAlternateSetting = 0 ,
. bNumEndpoints = 0 ,
. bInterfaceClass = USB_CLASS_CDC_DATA ,
. bInterfaceSubClass = 0 ,
. bInterfaceProtocol = 0 ,
} ;
/* ... but the "real" data interface has two bulk endpoints */
static const struct usb_interface_descriptor
data_intf = {
. bLength = sizeof data_intf ,
. bDescriptorType = USB_DT_INTERFACE ,
. bInterfaceNumber = 1 ,
. bAlternateSetting = 1 ,
. bNumEndpoints = 2 ,
. bInterfaceClass = USB_CLASS_CDC_DATA ,
. bInterfaceSubClass = 0 ,
. bInterfaceProtocol = 0 ,
. iInterface = STRING_DATA ,
} ;
# endif
# ifdef CONFIG_USB_ETH_RNDIS
/* RNDIS doesn't activate by changing to the "real" altsetting */
static const struct usb_interface_descriptor
rndis_data_intf = {
. bLength = sizeof rndis_data_intf ,
. bDescriptorType = USB_DT_INTERFACE ,
. bInterfaceNumber = 1 ,
. bAlternateSetting = 0 ,
. bNumEndpoints = 2 ,
. bInterfaceClass = USB_CLASS_CDC_DATA ,
. bInterfaceSubClass = 0 ,
. bInterfaceProtocol = 0 ,
. iInterface = STRING_DATA ,
} ;
# endif
# ifdef DEV_CONFIG_SUBSET
/*
* " Simple " CDC - subset option is a simple vendor - neutral model that most
* full speed controllers can handle : one interface , two bulk endpoints .
2006-12-12 02:59:04 +03:00
*
* To assist host side drivers , we fancy it up a bit , and add descriptors
* so some host side drivers will understand it as a " SAFE " variant .
2005-04-17 02:20:36 +04:00
*/
static const struct usb_interface_descriptor
subset_data_intf = {
. bLength = sizeof subset_data_intf ,
. bDescriptorType = USB_DT_INTERFACE ,
. bInterfaceNumber = 0 ,
. bAlternateSetting = 0 ,
. bNumEndpoints = 2 ,
2006-12-12 02:59:04 +03:00
. bInterfaceClass = USB_CLASS_COMM ,
. bInterfaceSubClass = USB_CDC_SUBCLASS_MDLM ,
2005-04-17 02:20:36 +04:00
. bInterfaceProtocol = 0 ,
. iInterface = STRING_DATA ,
} ;
# endif /* SUBSET */
static struct usb_endpoint_descriptor
fs_source_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_sink_desc = {
. bLength = USB_DT_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_ENDPOINT ,
. bEndpointAddress = USB_DIR_OUT ,
. bmAttributes = USB_ENDPOINT_XFER_BULK ,
} ;
static const struct usb_descriptor_header * fs_eth_function [ 11 ] = {
( struct usb_descriptor_header * ) & otg_descriptor ,
# ifdef DEV_CONFIG_CDC
/* "cdc" mode descriptors */
( struct usb_descriptor_header * ) & control_intf ,
( struct usb_descriptor_header * ) & header_desc ,
( struct usb_descriptor_header * ) & union_desc ,
( struct usb_descriptor_header * ) & ether_desc ,
/* NOTE: status endpoint may need to be removed */
( struct usb_descriptor_header * ) & fs_status_desc ,
/* data interface, with altsetting */
( struct usb_descriptor_header * ) & data_nop_intf ,
( struct usb_descriptor_header * ) & data_intf ,
( struct usb_descriptor_header * ) & fs_source_desc ,
( struct usb_descriptor_header * ) & fs_sink_desc ,
NULL ,
# endif /* DEV_CONFIG_CDC */
} ;
static inline void __init fs_subset_descriptors ( void )
{
# ifdef DEV_CONFIG_SUBSET
2006-12-12 02:59:04 +03:00
/* behavior is "CDC Subset"; extra descriptors say "SAFE" */
2005-04-17 02:20:36 +04:00
fs_eth_function [ 1 ] = ( struct usb_descriptor_header * ) & subset_data_intf ;
2006-12-12 02:59:04 +03:00
fs_eth_function [ 2 ] = ( struct usb_descriptor_header * ) & header_desc ;
fs_eth_function [ 3 ] = ( struct usb_descriptor_header * ) & mdlm_desc ;
fs_eth_function [ 4 ] = ( struct usb_descriptor_header * ) & mdlm_detail_desc ;
fs_eth_function [ 5 ] = ( struct usb_descriptor_header * ) & ether_desc ;
fs_eth_function [ 6 ] = ( struct usb_descriptor_header * ) & fs_source_desc ;
fs_eth_function [ 7 ] = ( struct usb_descriptor_header * ) & fs_sink_desc ;
fs_eth_function [ 8 ] = NULL ;
2005-04-17 02:20:36 +04:00
# else
fs_eth_function [ 1 ] = NULL ;
# endif
}
# ifdef CONFIG_USB_ETH_RNDIS
static const struct usb_descriptor_header * fs_rndis_function [ ] = {
( struct usb_descriptor_header * ) & otg_descriptor ,
/* control interface matches ACM, not Ethernet */
( struct usb_descriptor_header * ) & rndis_control_intf ,
( struct usb_descriptor_header * ) & header_desc ,
( struct usb_descriptor_header * ) & call_mgmt_descriptor ,
( struct usb_descriptor_header * ) & acm_descriptor ,
( struct usb_descriptor_header * ) & union_desc ,
( struct usb_descriptor_header * ) & fs_status_desc ,
/* data interface has no altsetting */
( struct usb_descriptor_header * ) & rndis_data_intf ,
( struct usb_descriptor_header * ) & fs_source_desc ,
( struct usb_descriptor_header * ) & fs_sink_desc ,
NULL ,
} ;
# endif
/*
* usb 2.0 devices need to expose both high speed and full speed
* descriptors , unless they only run at full speed .
*/
# if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
static struct usb_endpoint_descriptor
hs_status_desc = {
. bLength = USB_DT_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_ENDPOINT ,
. bmAttributes = USB_ENDPOINT_XFER_INT ,
. wMaxPacketSize = __constant_cpu_to_le16 ( STATUS_BYTECOUNT ) ,
. bInterval = LOG2_STATUS_INTERVAL_MSEC + 4 ,
} ;
# endif /* DEV_CONFIG_CDC */
static struct usb_endpoint_descriptor
hs_source_desc = {
. bLength = USB_DT_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_ENDPOINT ,
. bmAttributes = USB_ENDPOINT_XFER_BULK ,
. wMaxPacketSize = __constant_cpu_to_le16 ( 512 ) ,
} ;
static struct usb_endpoint_descriptor
hs_sink_desc = {
. bLength = USB_DT_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_ENDPOINT ,
. bmAttributes = USB_ENDPOINT_XFER_BULK ,
. wMaxPacketSize = __constant_cpu_to_le16 ( 512 ) ,
} ;
static struct usb_qualifier_descriptor
dev_qualifier = {
. bLength = sizeof dev_qualifier ,
. bDescriptorType = USB_DT_DEVICE_QUALIFIER ,
. bcdUSB = __constant_cpu_to_le16 ( 0x0200 ) ,
. bDeviceClass = USB_CLASS_COMM ,
. bNumConfigurations = 1 ,
} ;
static const struct usb_descriptor_header * hs_eth_function [ 11 ] = {
( struct usb_descriptor_header * ) & otg_descriptor ,
# ifdef DEV_CONFIG_CDC
/* "cdc" mode descriptors */
( struct usb_descriptor_header * ) & control_intf ,
( struct usb_descriptor_header * ) & header_desc ,
( struct usb_descriptor_header * ) & union_desc ,
( struct usb_descriptor_header * ) & ether_desc ,
/* NOTE: status endpoint may need to be removed */
( struct usb_descriptor_header * ) & hs_status_desc ,
/* data interface, with altsetting */
( struct usb_descriptor_header * ) & data_nop_intf ,
( struct usb_descriptor_header * ) & data_intf ,
( struct usb_descriptor_header * ) & hs_source_desc ,
( struct usb_descriptor_header * ) & hs_sink_desc ,
NULL ,
# endif /* DEV_CONFIG_CDC */
} ;
static inline void __init hs_subset_descriptors ( void )
{
# ifdef DEV_CONFIG_SUBSET
2006-12-12 02:59:04 +03:00
/* behavior is "CDC Subset"; extra descriptors say "SAFE" */
2005-04-17 02:20:36 +04:00
hs_eth_function [ 1 ] = ( struct usb_descriptor_header * ) & subset_data_intf ;
2006-12-12 02:59:04 +03:00
hs_eth_function [ 2 ] = ( struct usb_descriptor_header * ) & header_desc ;
hs_eth_function [ 3 ] = ( struct usb_descriptor_header * ) & mdlm_desc ;
hs_eth_function [ 4 ] = ( struct usb_descriptor_header * ) & mdlm_detail_desc ;
hs_eth_function [ 5 ] = ( struct usb_descriptor_header * ) & ether_desc ;
hs_eth_function [ 6 ] = ( struct usb_descriptor_header * ) & hs_source_desc ;
hs_eth_function [ 7 ] = ( struct usb_descriptor_header * ) & hs_sink_desc ;
hs_eth_function [ 8 ] = NULL ;
2005-04-17 02:20:36 +04:00
# else
hs_eth_function [ 1 ] = NULL ;
# endif
}
# ifdef CONFIG_USB_ETH_RNDIS
static const struct usb_descriptor_header * hs_rndis_function [ ] = {
( struct usb_descriptor_header * ) & otg_descriptor ,
/* control interface matches ACM, not Ethernet */
( struct usb_descriptor_header * ) & rndis_control_intf ,
( struct usb_descriptor_header * ) & header_desc ,
( struct usb_descriptor_header * ) & call_mgmt_descriptor ,
( struct usb_descriptor_header * ) & acm_descriptor ,
( struct usb_descriptor_header * ) & union_desc ,
( struct usb_descriptor_header * ) & hs_status_desc ,
/* data interface has no altsetting */
( struct usb_descriptor_header * ) & rndis_data_intf ,
( struct usb_descriptor_header * ) & hs_source_desc ,
( struct usb_descriptor_header * ) & hs_sink_desc ,
NULL ,
} ;
# endif
/* maxpacket and other transfer characteristics vary by speed. */
2007-08-02 11:00:38 +04:00
static inline struct usb_endpoint_descriptor *
ep_desc ( struct usb_gadget * g , struct usb_endpoint_descriptor * hs ,
struct usb_endpoint_descriptor * fs )
2005-04-17 02:20:36 +04:00
{
2007-08-02 11:00:38 +04:00
if ( gadget_is_dualspeed ( g ) & & g - > speed = = USB_SPEED_HIGH )
return hs ;
return fs ;
2005-04-17 02:20:36 +04:00
}
/*-------------------------------------------------------------------------*/
/* descriptors that are built on-demand */
static char manufacturer [ 50 ] ;
static char product_desc [ 40 ] = DRIVER_DESC ;
2006-02-18 23:31:23 +03:00
static char serial_number [ 20 ] ;
2005-04-17 02:20:36 +04:00
/* address that the host will use ... usually assigned at random */
static char ethaddr [ 2 * ETH_ALEN + 1 ] ;
/* static strings, in UTF-8 */
static struct usb_string strings [ ] = {
{ STRING_MANUFACTURER , manufacturer , } ,
{ STRING_PRODUCT , product_desc , } ,
2006-02-18 23:31:23 +03:00
{ STRING_SERIALNUMBER , serial_number , } ,
2005-04-17 02:20:36 +04:00
{ STRING_DATA , " Ethernet Data " , } ,
2006-12-12 02:59:04 +03:00
{ STRING_ETHADDR , ethaddr , } ,
2005-04-17 02:20:36 +04:00
# ifdef DEV_CONFIG_CDC
{ STRING_CDC , " CDC Ethernet " , } ,
{ STRING_CONTROL , " CDC Communications Control " , } ,
# endif
# ifdef DEV_CONFIG_SUBSET
{ STRING_SUBSET , " CDC Ethernet Subset " , } ,
# endif
# ifdef CONFIG_USB_ETH_RNDIS
{ STRING_RNDIS , " RNDIS " , } ,
{ STRING_RNDIS_CONTROL , " RNDIS Communications Control " , } ,
# endif
{ } /* end of list */
} ;
static struct usb_gadget_strings stringtab = {
. language = 0x0409 , /* en-us */
. strings = strings ,
} ;
/*
* one config , two interfaces : control , data .
* complications : class descriptors , and an altsetting .
*/
static int
2007-08-02 11:00:38 +04:00
config_buf ( struct usb_gadget * g , u8 * buf , u8 type , unsigned index , int is_otg )
2005-04-17 02:20:36 +04:00
{
int len ;
const struct usb_config_descriptor * config ;
const struct usb_descriptor_header * * function ;
2007-08-02 11:00:38 +04:00
int hs = 0 ;
2005-04-17 02:20:36 +04:00
2007-08-02 11:00:38 +04:00
if ( gadget_is_dualspeed ( g ) ) {
hs = ( g - > speed = = USB_SPEED_HIGH ) ;
if ( type = = USB_DT_OTHER_SPEED_CONFIG )
hs = ! hs ;
}
2005-04-17 02:20:36 +04:00
# define which_fn(t) (hs ? hs_ ## t ## _function : fs_ ## t ## _function)
if ( index > = device_desc . bNumConfigurations )
return - EINVAL ;
# ifdef CONFIG_USB_ETH_RNDIS
/* list the RNDIS config first, to make Microsoft's drivers
* happy . DOCSIS 1.0 needs this too .
*/
if ( device_desc . bNumConfigurations = = 2 & & index = = 0 ) {
config = & rndis_config ;
function = which_fn ( rndis ) ;
} else
# endif
{
config = & eth_config ;
function = which_fn ( eth ) ;
}
/* for now, don't advertise srp-only devices */
if ( ! is_otg )
function + + ;
len = usb_gadget_config_buf ( config , buf , USB_BUFSIZ , function ) ;
if ( len < 0 )
return len ;
( ( struct usb_config_descriptor * ) buf ) - > bDescriptorType = type ;
return len ;
}
/*-------------------------------------------------------------------------*/
2005-10-21 11:21:58 +04:00
static void eth_start ( struct eth_dev * dev , gfp_t gfp_flags ) ;
static int alloc_requests ( struct eth_dev * dev , unsigned n , gfp_t gfp_flags ) ;
2005-04-17 02:20:36 +04:00
2005-04-29 00:48:09 +04:00
static int
2005-10-21 11:21:58 +04:00
set_ether_config ( struct eth_dev * dev , gfp_t gfp_flags )
2005-04-17 02:20:36 +04:00
{
2005-04-29 00:48:09 +04:00
int result = 0 ;
struct usb_gadget * gadget = dev - > gadget ;
2005-06-29 13:20:29 +04:00
# if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
2005-04-29 00:48:09 +04:00
/* status endpoint used for RNDIS and (optionally) CDC */
if ( ! subset_active ( dev ) & & dev - > status_ep ) {
dev - > status = ep_desc ( gadget , & hs_status_desc ,
& fs_status_desc ) ;
dev - > status_ep - > driver_data = dev ;
result = usb_ep_enable ( dev - > status_ep , dev - > status ) ;
if ( result ! = 0 ) {
2006-06-13 20:54:40 +04:00
DEBUG ( dev , " enable %s --> %d \n " ,
2005-04-29 00:48:09 +04:00
dev - > status_ep - > name , result ) ;
goto done ;
}
}
2005-06-29 13:20:29 +04:00
# endif
2005-04-29 00:48:09 +04:00
2006-12-12 02:59:04 +03:00
dev - > in = ep_desc ( gadget , & hs_source_desc , & fs_source_desc ) ;
2005-04-29 00:48:09 +04:00
dev - > in_ep - > driver_data = dev ;
2006-12-12 02:59:04 +03:00
dev - > out = ep_desc ( gadget , & hs_sink_desc , & fs_sink_desc ) ;
2005-04-29 00:48:09 +04:00
dev - > out_ep - > driver_data = dev ;
2005-04-17 02:20:36 +04:00
/* With CDC, the host isn't allowed to use these two data
* endpoints in the default altsetting for the interface .
* so we don ' t activate them yet . Reset from SET_INTERFACE .
*
* Strictly speaking RNDIS should work the same : activation is
* a side effect of setting a packet filter . Deactivation is
* from REMOTE_NDIS_HALT_MSG , reset from REMOTE_NDIS_RESET_MSG .
*/
2005-04-29 00:48:09 +04:00
if ( ! cdc_active ( dev ) ) {
result = usb_ep_enable ( dev - > in_ep , dev - > in ) ;
if ( result ! = 0 ) {
2006-06-13 20:54:40 +04:00
DEBUG ( dev , " enable %s --> %d \n " ,
2005-04-29 00:48:09 +04:00
dev - > in_ep - > name , result ) ;
goto done ;
2005-04-17 02:20:36 +04:00
}
2005-04-29 00:48:09 +04:00
result = usb_ep_enable ( dev - > out_ep , dev - > out ) ;
if ( result ! = 0 ) {
2006-06-13 20:54:40 +04:00
DEBUG ( dev , " enable %s --> %d \n " ,
2006-06-07 22:46:13 +04:00
dev - > out_ep - > name , result ) ;
2005-04-29 00:48:09 +04:00
goto done ;
2005-04-17 02:20:36 +04:00
}
}
2005-04-29 00:48:09 +04:00
done :
2005-04-17 02:20:36 +04:00
if ( result = = 0 )
result = alloc_requests ( dev , qlen ( gadget ) , gfp_flags ) ;
/* on error, disable any endpoints */
if ( result < 0 ) {
2007-12-19 22:27:51 +03:00
if ( ! subset_active ( dev ) & & dev - > status_ep )
2005-04-17 02:20:36 +04:00
( void ) usb_ep_disable ( dev - > status_ep ) ;
dev - > status = NULL ;
2005-04-29 00:48:09 +04:00
( void ) usb_ep_disable ( dev - > in_ep ) ;
( void ) usb_ep_disable ( dev - > out_ep ) ;
2005-04-17 02:20:36 +04:00
dev - > in = NULL ;
dev - > out = NULL ;
2007-12-19 22:27:51 +03:00
}
2005-04-17 02:20:36 +04:00
/* activate non-CDC configs right away
* this isn ' t strictly according to the RNDIS spec
*/
2007-12-19 22:27:51 +03:00
else if ( ! cdc_active ( dev ) ) {
2005-04-17 02:20:36 +04:00
netif_carrier_on ( dev - > net ) ;
if ( netif_running ( dev - > net ) ) {
spin_unlock ( & dev - > lock ) ;
eth_start ( dev , GFP_ATOMIC ) ;
spin_lock ( & dev - > lock ) ;
}
}
if ( result = = 0 )
DEBUG ( dev , " qlen %d \n " , qlen ( gadget ) ) ;
/* caller is responsible for cleanup on error */
return result ;
}
static void eth_reset_config ( struct eth_dev * dev )
{
struct usb_request * req ;
if ( dev - > config = = 0 )
return ;
DEBUG ( dev , " %s \n " , __FUNCTION__ ) ;
netif_stop_queue ( dev - > net ) ;
netif_carrier_off ( dev - > net ) ;
2005-04-29 00:45:25 +04:00
rndis_uninit ( dev - > rndis_config ) ;
2005-04-17 02:20:36 +04:00
/* disable endpoints, forcing (synchronous) completion of
* pending i / o . then free the requests .
*/
if ( dev - > in ) {
usb_ep_disable ( dev - > in_ep ) ;
2006-08-22 02:26:38 +04:00
spin_lock ( & dev - > req_lock ) ;
2005-04-17 02:20:36 +04:00
while ( likely ( ! list_empty ( & dev - > tx_reqs ) ) ) {
req = container_of ( dev - > tx_reqs . next ,
struct usb_request , list ) ;
list_del ( & req - > list ) ;
2006-08-22 02:26:38 +04:00
spin_unlock ( & dev - > req_lock ) ;
2005-04-17 02:20:36 +04:00
usb_ep_free_request ( dev - > in_ep , req ) ;
2006-08-22 02:26:38 +04:00
spin_lock ( & dev - > req_lock ) ;
2005-04-17 02:20:36 +04:00
}
2006-08-22 02:26:38 +04:00
spin_unlock ( & dev - > req_lock ) ;
2005-04-17 02:20:36 +04:00
}
if ( dev - > out ) {
usb_ep_disable ( dev - > out_ep ) ;
2006-08-22 02:26:38 +04:00
spin_lock ( & dev - > req_lock ) ;
2005-04-17 02:20:36 +04:00
while ( likely ( ! list_empty ( & dev - > rx_reqs ) ) ) {
req = container_of ( dev - > rx_reqs . next ,
struct usb_request , list ) ;
list_del ( & req - > list ) ;
2006-08-22 02:26:38 +04:00
spin_unlock ( & dev - > req_lock ) ;
2005-04-17 02:20:36 +04:00
usb_ep_free_request ( dev - > out_ep , req ) ;
2006-08-22 02:26:38 +04:00
spin_lock ( & dev - > req_lock ) ;
2005-04-17 02:20:36 +04:00
}
2006-08-22 02:26:38 +04:00
spin_unlock ( & dev - > req_lock ) ;
2005-04-17 02:20:36 +04:00
}
if ( dev - > status ) {
usb_ep_disable ( dev - > status_ep ) ;
}
2005-04-29 00:48:09 +04:00
dev - > rndis = 0 ;
dev - > cdc_filter = 0 ;
2005-04-17 02:20:36 +04:00
dev - > config = 0 ;
}
/* change our operational config. must agree with the code
* that returns config descriptors , and altsetting code .
*/
static int
2005-10-21 11:21:58 +04:00
eth_set_config ( struct eth_dev * dev , unsigned number , gfp_t gfp_flags )
2005-04-17 02:20:36 +04:00
{
int result = 0 ;
struct usb_gadget * gadget = dev - > gadget ;
if ( gadget_is_sa1100 ( gadget )
& & dev - > config
& & atomic_read ( & dev - > tx_qlen ) ! = 0 ) {
/* tx fifo is full, but we can't clear it...*/
INFO ( dev , " can't change configurations \n " ) ;
return - ESPIPE ;
}
eth_reset_config ( dev ) ;
switch ( number ) {
case DEV_CONFIG_VALUE :
result = set_ether_config ( dev , gfp_flags ) ;
break ;
# ifdef CONFIG_USB_ETH_RNDIS
case DEV_RNDIS_CONFIG_VALUE :
dev - > rndis = 1 ;
result = set_ether_config ( dev , gfp_flags ) ;
break ;
# endif
default :
result = - EINVAL ;
/* FALL THROUGH */
case 0 :
break ;
}
if ( result ) {
if ( number )
eth_reset_config ( dev ) ;
usb_gadget_vbus_draw ( dev - > gadget ,
2007-08-02 11:00:38 +04:00
gadget_is_otg ( dev - > gadget ) ? 8 : 100 ) ;
2005-04-17 02:20:36 +04:00
} else {
char * speed ;
unsigned power ;
power = 2 * eth_config . bMaxPower ;
usb_gadget_vbus_draw ( dev - > gadget , power ) ;
switch ( gadget - > speed ) {
case USB_SPEED_FULL : speed = " full " ; break ;
# ifdef CONFIG_USB_GADGET_DUALSPEED
case USB_SPEED_HIGH : speed = " high " ; break ;
# endif
2006-06-13 20:54:40 +04:00
default : speed = " ? " ; break ;
2005-04-17 02:20:36 +04:00
}
dev - > config = number ;
INFO ( dev , " %s speed config #%d: %d mA, %s, using %s \n " ,
speed , number , power , driver_desc ,
2005-05-16 19:26:38 +04:00
rndis_active ( dev )
2005-04-17 02:20:36 +04:00
? " RNDIS "
2005-05-16 19:26:38 +04:00
: ( cdc_active ( dev )
2005-04-17 02:20:36 +04:00
? " CDC Ethernet "
: " CDC Ethernet Subset " ) ) ;
}
return result ;
}
/*-------------------------------------------------------------------------*/
# ifdef DEV_CONFIG_CDC
2005-04-29 00:48:09 +04:00
/* The interrupt endpoint is used in CDC networking models (Ethernet, ATM)
* only to notify the host about link status changes ( which we support ) or
* report completion of some encapsulated command ( as used in RNDIS ) . Since
* we want this CDC Ethernet code to be vendor - neutral , we don ' t use that
* command mechanism ; and only one status request is ever queued .
*/
2005-04-17 02:20:36 +04:00
static void eth_status_complete ( struct usb_ep * ep , struct usb_request * req )
{
struct usb_cdc_notification * event = req - > buf ;
int value = req - > status ;
struct eth_dev * dev = ep - > driver_data ;
/* issue the second notification if host reads the first */
if ( event - > bNotificationType = = USB_CDC_NOTIFY_NETWORK_CONNECTION
& & value = = 0 ) {
__le32 * data = req - > buf + sizeof * event ;
event - > bmRequestType = 0xA1 ;
event - > bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE ;
event - > wValue = __constant_cpu_to_le16 ( 0 ) ;
event - > wIndex = __constant_cpu_to_le16 ( 1 ) ;
event - > wLength = __constant_cpu_to_le16 ( 8 ) ;
/* SPEED_CHANGE data is up/down speeds in bits/sec */
data [ 0 ] = data [ 1 ] = cpu_to_le32 ( BITRATE ( dev - > gadget ) ) ;
req - > length = STATUS_BYTECOUNT ;
value = usb_ep_queue ( ep , req , GFP_ATOMIC ) ;
DEBUG ( dev , " send SPEED_CHANGE --> %d \n " , value ) ;
if ( value = = 0 )
return ;
} else if ( value ! = - ECONNRESET )
DEBUG ( dev , " event %02x --> %d \n " ,
event - > bNotificationType , value ) ;
2005-04-29 00:48:09 +04:00
req - > context = NULL ;
2005-04-17 02:20:36 +04:00
}
static void issue_start_status ( struct eth_dev * dev )
{
struct usb_request * req = dev - > stat_req ;
struct usb_cdc_notification * event ;
int value ;
2006-06-13 20:54:40 +04:00
2005-04-17 02:20:36 +04:00
DEBUG ( dev , " %s, flush old status first \n " , __FUNCTION__ ) ;
/* flush old status
*
* FIXME ugly idiom , maybe we ' d be better with just
* a " cancel the whole queue " primitive since any
* unlink - one primitive has way too many error modes .
* here , we " know " toggle is already clear . . .
2005-04-29 00:48:09 +04:00
*
* FIXME iff req - > context ! = null just dequeue it
2005-04-17 02:20:36 +04:00
*/
usb_ep_disable ( dev - > status_ep ) ;
usb_ep_enable ( dev - > status_ep , dev - > status ) ;
/* 3.8.1 says to issue first NETWORK_CONNECTION, then
* a SPEED_CHANGE . could be useful in some configs .
*/
event = req - > buf ;
event - > bmRequestType = 0xA1 ;
event - > bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION ;
event - > wValue = __constant_cpu_to_le16 ( 1 ) ; /* connected */
event - > wIndex = __constant_cpu_to_le16 ( 1 ) ;
event - > wLength = 0 ;
req - > length = sizeof * event ;
req - > complete = eth_status_complete ;
2005-04-29 00:48:09 +04:00
req - > context = dev ;
2005-04-17 02:20:36 +04:00
value = usb_ep_queue ( dev - > status_ep , req , GFP_ATOMIC ) ;
if ( value < 0 )
DEBUG ( dev , " status buf queue --> %d \n " , value ) ;
}
# endif
/*-------------------------------------------------------------------------*/
static void eth_setup_complete ( struct usb_ep * ep , struct usb_request * req )
{
if ( req - > status | | req - > actual ! = req - > length )
DEBUG ( ( struct eth_dev * ) ep - > driver_data ,
" setup complete --> %d, %d/%d \n " ,
req - > status , req - > actual , req - > length ) ;
}
# ifdef CONFIG_USB_ETH_RNDIS
static void rndis_response_complete ( struct usb_ep * ep , struct usb_request * req )
{
if ( req - > status | | req - > actual ! = req - > length )
DEBUG ( ( struct eth_dev * ) ep - > driver_data ,
" rndis response complete --> %d, %d/%d \n " ,
req - > status , req - > actual , req - > length ) ;
/* done sending after USB_CDC_GET_ENCAPSULATED_RESPONSE */
}
static void rndis_command_complete ( struct usb_ep * ep , struct usb_request * req )
{
struct eth_dev * dev = ep - > driver_data ;
int status ;
2006-06-13 20:54:40 +04:00
2005-04-17 02:20:36 +04:00
/* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
spin_lock ( & dev - > lock ) ;
status = rndis_msg_parser ( dev - > rndis_config , ( u8 * ) req - > buf ) ;
if ( status < 0 )
ERROR ( dev , " %s: rndis parse error %d \n " , __FUNCTION__ , status ) ;
spin_unlock ( & dev - > lock ) ;
}
# endif /* RNDIS */
/*
* The setup ( ) callback implements all the ep0 functionality that ' s not
* handled lower down . CDC has a number of less - common features :
*
* - two interfaces : control , and ethernet data
* - Ethernet data interface has two altsettings : default , and active
* - class - specific descriptors for the control interface
* - class - specific control requests
*/
static int
eth_setup ( struct usb_gadget * gadget , const struct usb_ctrlrequest * ctrl )
{
struct eth_dev * dev = get_gadget_data ( gadget ) ;
struct usb_request * req = dev - > req ;
int value = - EOPNOTSUPP ;
2005-05-08 00:05:13 +04:00
u16 wIndex = le16_to_cpu ( ctrl - > wIndex ) ;
u16 wValue = le16_to_cpu ( ctrl - > wValue ) ;
u16 wLength = le16_to_cpu ( ctrl - > wLength ) ;
2005-04-17 02:20:36 +04:00
/* descriptors just go into the pre-allocated ep0 buffer,
* while config change events may enable network traffic .
*/
req - > complete = eth_setup_complete ;
switch ( ctrl - > bRequest ) {
case USB_REQ_GET_DESCRIPTOR :
if ( ctrl - > bRequestType ! = USB_DIR_IN )
break ;
switch ( wValue > > 8 ) {
case USB_DT_DEVICE :
value = min ( wLength , ( u16 ) sizeof device_desc ) ;
memcpy ( req - > buf , & device_desc , value ) ;
break ;
case USB_DT_DEVICE_QUALIFIER :
2007-08-02 11:00:38 +04:00
if ( ! gadget_is_dualspeed ( gadget ) )
2005-04-17 02:20:36 +04:00
break ;
value = min ( wLength , ( u16 ) sizeof dev_qualifier ) ;
memcpy ( req - > buf , & dev_qualifier , value ) ;
break ;
case USB_DT_OTHER_SPEED_CONFIG :
2007-08-02 11:00:38 +04:00
if ( ! gadget_is_dualspeed ( gadget ) )
2005-04-17 02:20:36 +04:00
break ;
// FALLTHROUGH
case USB_DT_CONFIG :
2007-08-02 11:00:38 +04:00
value = config_buf ( gadget , req - > buf ,
2005-04-17 02:20:36 +04:00
wValue > > 8 ,
wValue & 0xff ,
2007-08-02 11:00:38 +04:00
gadget_is_otg ( gadget ) ) ;
2005-04-17 02:20:36 +04:00
if ( value > = 0 )
value = min ( wLength , ( u16 ) value ) ;
break ;
case USB_DT_STRING :
value = usb_gadget_get_string ( & stringtab ,
wValue & 0xff , req - > buf ) ;
if ( value > = 0 )
value = min ( wLength , ( u16 ) value ) ;
break ;
}
break ;
case USB_REQ_SET_CONFIGURATION :
if ( ctrl - > bRequestType ! = 0 )
break ;
if ( gadget - > a_hnp_support )
DEBUG ( dev , " HNP available \n " ) ;
else if ( gadget - > a_alt_hnp_support )
DEBUG ( dev , " HNP needs a different root port \n " ) ;
spin_lock ( & dev - > lock ) ;
value = eth_set_config ( dev , wValue , GFP_ATOMIC ) ;
spin_unlock ( & dev - > lock ) ;
break ;
case USB_REQ_GET_CONFIGURATION :
if ( ctrl - > bRequestType ! = USB_DIR_IN )
break ;
* ( u8 * ) req - > buf = dev - > config ;
value = min ( wLength , ( u16 ) 1 ) ;
break ;
case USB_REQ_SET_INTERFACE :
if ( ctrl - > bRequestType ! = USB_RECIP_INTERFACE
| | ! dev - > config
| | wIndex > 1 )
break ;
2005-05-16 19:26:38 +04:00
if ( ! cdc_active ( dev ) & & wIndex ! = 0 )
2005-04-17 02:20:36 +04:00
break ;
spin_lock ( & dev - > lock ) ;
/* PXA hardware partially handles SET_INTERFACE;
* we need to kluge around that interference .
*/
if ( gadget_is_pxa ( gadget ) ) {
value = eth_set_config ( dev , DEV_CONFIG_VALUE ,
GFP_ATOMIC ) ;
goto done_set_intf ;
}
# ifdef DEV_CONFIG_CDC
switch ( wIndex ) {
case 0 : /* control/master intf */
if ( wValue ! = 0 )
break ;
if ( dev - > status ) {
usb_ep_disable ( dev - > status_ep ) ;
usb_ep_enable ( dev - > status_ep , dev - > status ) ;
}
value = 0 ;
break ;
case 1 : /* data intf */
if ( wValue > 1 )
break ;
usb_ep_disable ( dev - > in_ep ) ;
usb_ep_disable ( dev - > out_ep ) ;
/* CDC requires the data transfers not be done from
* the default interface setting . . . also , setting
2005-04-29 00:48:09 +04:00
* the non - default interface resets filters etc .
2005-04-17 02:20:36 +04:00
*/
if ( wValue = = 1 ) {
2005-04-29 00:48:09 +04:00
if ( ! cdc_active ( dev ) )
break ;
2005-04-17 02:20:36 +04:00
usb_ep_enable ( dev - > in_ep , dev - > in ) ;
usb_ep_enable ( dev - > out_ep , dev - > out ) ;
dev - > cdc_filter = DEFAULT_FILTER ;
netif_carrier_on ( dev - > net ) ;
if ( dev - > status )
issue_start_status ( dev ) ;
if ( netif_running ( dev - > net ) ) {
spin_unlock ( & dev - > lock ) ;
eth_start ( dev , GFP_ATOMIC ) ;
spin_lock ( & dev - > lock ) ;
}
} else {
netif_stop_queue ( dev - > net ) ;
netif_carrier_off ( dev - > net ) ;
}
value = 0 ;
break ;
}
# else
/* FIXME this is wrong, as is the assumption that
* all non - PXA hardware talks real CDC . . .
*/
dev_warn ( & gadget - > dev , " set_interface ignored! \n " ) ;
# endif /* DEV_CONFIG_CDC */
done_set_intf :
spin_unlock ( & dev - > lock ) ;
break ;
case USB_REQ_GET_INTERFACE :
if ( ctrl - > bRequestType ! = ( USB_DIR_IN | USB_RECIP_INTERFACE )
| | ! dev - > config
| | wIndex > 1 )
break ;
2005-05-16 19:26:38 +04:00
if ( ! ( cdc_active ( dev ) | | rndis_active ( dev ) ) & & wIndex ! = 0 )
2005-04-17 02:20:36 +04:00
break ;
/* for CDC, iff carrier is on, data interface is active. */
2005-05-16 19:26:38 +04:00
if ( rndis_active ( dev ) | | wIndex ! = 1 )
2005-04-17 02:20:36 +04:00
* ( u8 * ) req - > buf = 0 ;
else
* ( u8 * ) req - > buf = netif_carrier_ok ( dev - > net ) ? 1 : 0 ;
value = min ( wLength , ( u16 ) 1 ) ;
break ;
# ifdef DEV_CONFIG_CDC
case USB_CDC_SET_ETHERNET_PACKET_FILTER :
/* see 6.2.30: no data, wIndex = interface,
* wValue = packet filter bitmap
*/
if ( ctrl - > bRequestType ! = ( USB_TYPE_CLASS | USB_RECIP_INTERFACE )
2005-05-16 19:26:38 +04:00
| | ! cdc_active ( dev )
2005-04-17 02:20:36 +04:00
| | wLength ! = 0
| | wIndex > 1 )
break ;
DEBUG ( dev , " packet filter %02x \n " , wValue ) ;
dev - > cdc_filter = wValue ;
value = 0 ;
break ;
/* and potentially:
* 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 :
*/
# endif /* DEV_CONFIG_CDC */
2006-06-13 20:54:40 +04:00
# ifdef CONFIG_USB_ETH_RNDIS
2005-04-17 02:20:36 +04:00
/* RNDIS uses the CDC command encapsulation mechanism to implement
* an RPC scheme , with much getting / setting of attributes by OID .
*/
case USB_CDC_SEND_ENCAPSULATED_COMMAND :
if ( ctrl - > bRequestType ! = ( USB_TYPE_CLASS | USB_RECIP_INTERFACE )
2005-05-16 19:26:38 +04:00
| | ! rndis_active ( dev )
2005-04-17 02:20:36 +04:00
| | wLength > USB_BUFSIZ
| | wValue
| | rndis_control_intf . bInterfaceNumber
! = wIndex )
break ;
/* read the request, then process it */
value = wLength ;
req - > complete = rndis_command_complete ;
/* later, rndis_control_ack () sends a notification */
break ;
2006-06-13 20:54:40 +04:00
2005-04-17 02:20:36 +04:00
case USB_CDC_GET_ENCAPSULATED_RESPONSE :
if ( ( USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE )
= = ctrl - > bRequestType
2005-05-16 19:26:38 +04:00
& & rndis_active ( dev )
2005-04-17 02:20:36 +04:00
// && wLength >= 0x0400
& & ! wValue
& & rndis_control_intf . bInterfaceNumber
= = wIndex ) {
u8 * buf ;
2007-08-02 11:00:38 +04:00
u32 n ;
2005-04-17 02:20:36 +04:00
/* return the result */
2007-08-02 11:00:38 +04:00
buf = rndis_get_next_response ( dev - > rndis_config , & n ) ;
2005-04-17 02:20:36 +04:00
if ( buf ) {
2007-08-02 11:00:38 +04:00
memcpy ( req - > buf , buf , n ) ;
2005-04-17 02:20:36 +04:00
req - > complete = rndis_response_complete ;
rndis_free_response ( dev - > rndis_config , buf ) ;
2008-02-19 03:44:50 +03:00
value = n ;
2005-04-17 02:20:36 +04:00
}
/* else stalls ... spec says to avoid that */
}
break ;
# endif /* RNDIS */
default :
VDEBUG ( dev ,
" unknown control req%02x.%02x v%04x i%04x l%d \n " ,
ctrl - > bRequestType , ctrl - > bRequest ,
wValue , wIndex , wLength ) ;
}
/* respond with data transfer before status phase? */
if ( value > = 0 ) {
req - > length = value ;
req - > zero = value < wLength
& & ( value % gadget - > ep0 - > maxpacket ) = = 0 ;
value = usb_ep_queue ( gadget - > ep0 , req , GFP_ATOMIC ) ;
if ( value < 0 ) {
DEBUG ( dev , " ep_queue --> %d \n " , value ) ;
req - > status = 0 ;
eth_setup_complete ( gadget - > ep0 , req ) ;
}
}
/* host either stalls (value < 0) or reports success */
return value ;
}
static void
eth_disconnect ( struct usb_gadget * gadget )
{
struct eth_dev * dev = get_gadget_data ( gadget ) ;
unsigned long flags ;
spin_lock_irqsave ( & dev - > lock , flags ) ;
netif_stop_queue ( dev - > net ) ;
netif_carrier_off ( dev - > net ) ;
eth_reset_config ( dev ) ;
spin_unlock_irqrestore ( & dev - > lock , flags ) ;
/* FIXME RNDIS should enter RNDIS_UNINITIALIZED */
/* next we may get setup() calls to enumerate new connections;
* or an unbind ( ) during shutdown ( including removing module ) .
*/
}
/*-------------------------------------------------------------------------*/
/* NETWORK DRIVER HOOKUP (to the layer above this driver) */
static int eth_change_mtu ( struct net_device * net , int new_mtu )
{
struct eth_dev * dev = netdev_priv ( net ) ;
2006-01-22 21:33:27 +03:00
if ( dev - > rndis )
return - EBUSY ;
2005-04-17 02:20:36 +04:00
if ( new_mtu < = ETH_HLEN | | new_mtu > ETH_FRAME_LEN )
return - ERANGE ;
/* no zero-length packet read wanted after mtu-sized packets */
if ( ( ( new_mtu + sizeof ( struct ethhdr ) ) % dev - > in_ep - > maxpacket ) = = 0 )
return - EDOM ;
net - > mtu = new_mtu ;
return 0 ;
}
static struct net_device_stats * eth_get_stats ( struct net_device * net )
{
return & ( ( struct eth_dev * ) netdev_priv ( net ) ) - > stats ;
}
static void eth_get_drvinfo ( struct net_device * net , struct ethtool_drvinfo * p )
{
struct eth_dev * dev = netdev_priv ( net ) ;
strlcpy ( p - > driver , shortname , sizeof p - > driver ) ;
strlcpy ( p - > version , DRIVER_VERSION , sizeof p - > version ) ;
strlcpy ( p - > fw_version , dev - > gadget - > name , sizeof p - > fw_version ) ;
strlcpy ( p - > bus_info , dev - > gadget - > dev . bus_id , sizeof p - > bus_info ) ;
}
static u32 eth_get_link ( struct net_device * net )
{
struct eth_dev * dev = netdev_priv ( net ) ;
return dev - > gadget - > speed ! = USB_SPEED_UNKNOWN ;
}
static struct ethtool_ops ops = {
. get_drvinfo = eth_get_drvinfo ,
. get_link = eth_get_link
} ;
static void defer_kevent ( struct eth_dev * dev , int flag )
{
if ( test_and_set_bit ( flag , & dev - > todo ) )
return ;
if ( ! schedule_work ( & dev - > work ) )
ERROR ( dev , " kevent %d may have been dropped \n " , flag ) ;
else
DEBUG ( dev , " kevent %d scheduled \n " , flag ) ;
}
static void rx_complete ( struct usb_ep * ep , struct usb_request * req ) ;
static int
2005-10-21 11:21:58 +04:00
rx_submit ( struct eth_dev * dev , struct usb_request * req , gfp_t gfp_flags )
2005-04-17 02:20:36 +04:00
{
struct sk_buff * skb ;
int retval = - ENOMEM ;
size_t size ;
/* Padding up to RX_EXTRA handles minor disagreements with host.
* Normally we use the USB " terminate on short read " convention ;
* so allow up to ( N * maxpacket ) , since that memory is normally
* already allocated . Some hardware doesn ' t deal well with short
* reads ( e . g . DMA must be N * maxpacket ) , so for now don ' t trim a
* byte off the end ( to force hardware errors on overflow ) .
*
* RNDIS uses internal framing , and explicitly allows senders to
* pad to end - of - packet . That ' s potentially nice for speed ,
* but means receivers can ' t recover synch on their own .
*/
size = ( sizeof ( struct ethhdr ) + dev - > net - > mtu + RX_EXTRA ) ;
size + = dev - > out_ep - > maxpacket - 1 ;
2005-04-29 00:48:09 +04:00
if ( rndis_active ( dev ) )
2005-04-17 02:20:36 +04:00
size + = sizeof ( struct rndis_packet_msg_type ) ;
size - = size % dev - > out_ep - > maxpacket ;
2007-07-30 23:31:07 +04:00
skb = alloc_skb ( size + NET_IP_ALIGN , gfp_flags ) ;
if ( skb = = NULL ) {
2005-04-17 02:20:36 +04:00
DEBUG ( dev , " no rx skb \n " ) ;
goto enomem ;
}
2006-06-13 20:54:40 +04:00
2005-04-17 02:20:36 +04:00
/* Some platforms perform better when IP packets are aligned,
* but on at least one , checksumming fails otherwise . Note :
2005-04-19 04:39:34 +04:00
* RNDIS headers involve variable numbers of LE32 values .
2005-04-17 02:20:36 +04:00
*/
skb_reserve ( skb , NET_IP_ALIGN ) ;
req - > buf = skb - > data ;
req - > length = size ;
req - > complete = rx_complete ;
req - > context = skb ;
retval = usb_ep_queue ( dev - > out_ep , req , gfp_flags ) ;
if ( retval = = - ENOMEM )
enomem :
defer_kevent ( dev , WORK_RX_MEMORY ) ;
if ( retval ) {
DEBUG ( dev , " rx submit --> %d \n " , retval ) ;
2007-04-23 21:50:15 +04:00
if ( skb )
dev_kfree_skb_any ( skb ) ;
2006-08-22 02:26:38 +04:00
spin_lock ( & dev - > req_lock ) ;
2005-04-17 02:20:36 +04:00
list_add ( & req - > list , & dev - > rx_reqs ) ;
2006-08-22 02:26:38 +04:00
spin_unlock ( & dev - > req_lock ) ;
2005-04-17 02:20:36 +04:00
}
return retval ;
}
static void rx_complete ( struct usb_ep * ep , struct usb_request * req )
{
struct sk_buff * skb = req - > context ;
struct eth_dev * dev = ep - > driver_data ;
int status = req - > status ;
switch ( status ) {
/* normal completion */
case 0 :
skb_put ( skb , req - > actual ) ;
/* we know MaxPacketsPerTransfer == 1 here */
2005-05-16 19:26:38 +04:00
if ( rndis_active ( dev ) )
2005-04-19 04:39:34 +04:00
status = rndis_rm_hdr ( skb ) ;
if ( status < 0
| | ETH_HLEN > skb - > len
| | skb - > len > ETH_FRAME_LEN ) {
2005-04-17 02:20:36 +04:00
dev - > stats . rx_errors + + ;
dev - > stats . rx_length_errors + + ;
DEBUG ( dev , " rx length %d \n " , skb - > len ) ;
break ;
}
skb - > protocol = eth_type_trans ( skb , dev - > net ) ;
dev - > stats . rx_packets + + ;
dev - > stats . rx_bytes + = skb - > len ;
/* no buffer copies needed, unless hardware can't
* use skb buffers .
*/
status = netif_rx ( skb ) ;
skb = NULL ;
break ;
/* software-driven interface shutdown */
case - ECONNRESET : // unlink
case - ESHUTDOWN : // disconnect etc
VDEBUG ( dev , " rx shutdown, code %d \n " , status ) ;
goto quiesce ;
/* for hardware automagic (such as pxa) */
case - ECONNABORTED : // endpoint reset
DEBUG ( dev , " rx %s reset \n " , ep - > name ) ;
defer_kevent ( dev , WORK_RX_MEMORY ) ;
quiesce :
dev_kfree_skb_any ( skb ) ;
goto clean ;
/* data overrun */
case - EOVERFLOW :
dev - > stats . rx_over_errors + + ;
// FALLTHROUGH
2006-06-13 20:54:40 +04:00
2005-04-17 02:20:36 +04:00
default :
dev - > stats . rx_errors + + ;
DEBUG ( dev , " rx status %d \n " , status ) ;
break ;
}
if ( skb )
dev_kfree_skb_any ( skb ) ;
if ( ! netif_running ( dev - > net ) ) {
clean :
2006-08-22 02:26:38 +04:00
spin_lock ( & dev - > req_lock ) ;
2005-04-17 02:20:36 +04:00
list_add ( & req - > list , & dev - > rx_reqs ) ;
2006-08-22 02:26:38 +04:00
spin_unlock ( & dev - > req_lock ) ;
2005-04-17 02:20:36 +04:00
req = NULL ;
}
if ( req )
rx_submit ( dev , req , GFP_ATOMIC ) ;
}
static int prealloc ( struct list_head * list , struct usb_ep * ep ,
2005-10-21 11:21:58 +04:00
unsigned n , gfp_t gfp_flags )
2005-04-17 02:20:36 +04:00
{
unsigned i ;
struct usb_request * req ;
if ( ! n )
return - ENOMEM ;
/* queue/recycle up to N requests */
i = n ;
list_for_each_entry ( req , list , list ) {
if ( i - - = = 0 )
goto extra ;
}
while ( i - - ) {
req = usb_ep_alloc_request ( ep , gfp_flags ) ;
if ( ! req )
return list_empty ( list ) ? - ENOMEM : 0 ;
list_add ( & req - > list , list ) ;
}
return 0 ;
extra :
/* free extras */
for ( ; ; ) {
struct list_head * next ;
next = req - > list . next ;
list_del ( & req - > list ) ;
usb_ep_free_request ( ep , req ) ;
if ( next = = list )
break ;
req = container_of ( next , struct usb_request , list ) ;
}
return 0 ;
}
2005-10-21 11:21:58 +04:00
static int alloc_requests ( struct eth_dev * dev , unsigned n , gfp_t gfp_flags )
2005-04-17 02:20:36 +04:00
{
int status ;
2006-08-22 02:26:38 +04:00
spin_lock ( & dev - > req_lock ) ;
2005-04-17 02:20:36 +04:00
status = prealloc ( & dev - > tx_reqs , dev - > in_ep , n , gfp_flags ) ;
if ( status < 0 )
goto fail ;
status = prealloc ( & dev - > rx_reqs , dev - > out_ep , n , gfp_flags ) ;
if ( status < 0 )
goto fail ;
2006-08-22 02:26:38 +04:00
goto done ;
2005-04-17 02:20:36 +04:00
fail :
DEBUG ( dev , " can't alloc requests \n " ) ;
2006-08-22 02:26:38 +04:00
done :
spin_unlock ( & dev - > req_lock ) ;
2005-04-17 02:20:36 +04:00
return status ;
}
2005-10-21 11:21:58 +04:00
static void rx_fill ( struct eth_dev * dev , gfp_t gfp_flags )
2005-04-17 02:20:36 +04:00
{
struct usb_request * req ;
unsigned long flags ;
/* fill unused rxq slots with some skb */
2006-08-22 02:26:38 +04:00
spin_lock_irqsave ( & dev - > req_lock , flags ) ;
2005-04-17 02:20:36 +04:00
while ( ! list_empty ( & dev - > rx_reqs ) ) {
req = container_of ( dev - > rx_reqs . next ,
struct usb_request , list ) ;
list_del_init ( & req - > list ) ;
2006-08-22 02:26:38 +04:00
spin_unlock_irqrestore ( & dev - > req_lock , flags ) ;
2005-04-17 02:20:36 +04:00
if ( rx_submit ( dev , req , gfp_flags ) < 0 ) {
defer_kevent ( dev , WORK_RX_MEMORY ) ;
return ;
}
2006-08-22 02:26:38 +04:00
spin_lock_irqsave ( & dev - > req_lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
2006-08-22 02:26:38 +04:00
spin_unlock_irqrestore ( & dev - > req_lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
2006-11-22 17:57:56 +03:00
static void eth_work ( struct work_struct * work )
2005-04-17 02:20:36 +04:00
{
2006-11-22 17:57:56 +03:00
struct eth_dev * dev = container_of ( work , struct eth_dev , work ) ;
2005-04-17 02:20:36 +04:00
2005-04-29 00:48:09 +04:00
if ( test_and_clear_bit ( WORK_RX_MEMORY , & dev - > todo ) ) {
2005-04-17 02:20:36 +04:00
if ( netif_running ( dev - > net ) )
rx_fill ( dev , GFP_KERNEL ) ;
}
if ( dev - > todo )
DEBUG ( dev , " work done, flags = 0x%lx \n " , dev - > todo ) ;
}
static void tx_complete ( struct usb_ep * ep , struct usb_request * req )
{
struct sk_buff * skb = req - > context ;
struct eth_dev * dev = ep - > driver_data ;
switch ( req - > status ) {
default :
dev - > stats . tx_errors + + ;
VDEBUG ( dev , " tx err %d \n " , req - > status ) ;
/* FALLTHROUGH */
case - ECONNRESET : // unlink
case - ESHUTDOWN : // disconnect etc
break ;
case 0 :
dev - > stats . tx_bytes + = skb - > len ;
}
dev - > stats . tx_packets + + ;
2006-08-22 02:26:38 +04:00
spin_lock ( & dev - > req_lock ) ;
2005-04-17 02:20:36 +04:00
list_add ( & req - > list , & dev - > tx_reqs ) ;
2006-08-22 02:26:38 +04:00
spin_unlock ( & dev - > req_lock ) ;
2005-04-17 02:20:36 +04:00
dev_kfree_skb_any ( skb ) ;
atomic_dec ( & dev - > tx_qlen ) ;
if ( netif_carrier_ok ( dev - > net ) )
netif_wake_queue ( dev - > net ) ;
}
static inline int eth_is_promisc ( struct eth_dev * dev )
{
/* no filters for the CDC subset; always promisc */
if ( subset_active ( dev ) )
return 1 ;
return dev - > cdc_filter & USB_CDC_PACKET_TYPE_PROMISCUOUS ;
}
static int eth_start_xmit ( struct sk_buff * skb , struct net_device * net )
{
struct eth_dev * dev = netdev_priv ( net ) ;
int length = skb - > len ;
int retval ;
struct usb_request * req = NULL ;
unsigned long flags ;
/* apply outgoing CDC or RNDIS filters */
if ( ! eth_is_promisc ( dev ) ) {
u8 * dest = skb - > data ;
2006-11-13 05:09:44 +03:00
if ( is_multicast_ether_addr ( dest ) ) {
2005-04-17 02:20:36 +04:00
u16 type ;
/* ignores USB_CDC_PACKET_TYPE_MULTICAST and host
* SET_ETHERNET_MULTICAST_FILTERS requests
*/
2006-11-13 05:09:44 +03:00
if ( is_broadcast_ether_addr ( dest ) )
2005-04-17 02:20:36 +04:00
type = USB_CDC_PACKET_TYPE_BROADCAST ;
else
type = USB_CDC_PACKET_TYPE_ALL_MULTICAST ;
if ( ! ( dev - > cdc_filter & type ) ) {
dev_kfree_skb_any ( skb ) ;
return 0 ;
}
}
/* ignores USB_CDC_PACKET_TYPE_DIRECTED */
}
2006-08-22 02:26:38 +04:00
spin_lock_irqsave ( & dev - > req_lock , flags ) ;
2007-10-03 01:40:48 +04:00
/*
* this freelist can be empty if an interrupt triggered disconnect ( )
* and reconfigured the gadget ( shutting down this queue ) after the
* network stack decided to xmit but before we got the spinlock .
*/
if ( list_empty ( & dev - > tx_reqs ) ) {
spin_unlock_irqrestore ( & dev - > req_lock , flags ) ;
return 1 ;
}
2005-04-17 02:20:36 +04:00
req = container_of ( dev - > tx_reqs . next , struct usb_request , list ) ;
list_del ( & req - > list ) ;
2007-10-03 01:40:48 +04:00
/* temporarily stop TX queue when the freelist empties */
2005-04-17 02:20:36 +04:00
if ( list_empty ( & dev - > tx_reqs ) )
netif_stop_queue ( net ) ;
2006-08-22 02:26:38 +04:00
spin_unlock_irqrestore ( & dev - > req_lock , flags ) ;
2005-04-17 02:20:36 +04:00
/* no buffer copies needed, unless the network stack did it
* or the hardware can ' t use skb buffers .
* or there ' s not enough space for any RNDIS headers we need
*/
2005-05-16 19:26:38 +04:00
if ( rndis_active ( dev ) ) {
2005-04-17 02:20:36 +04:00
struct sk_buff * skb_rndis ;
skb_rndis = skb_realloc_headroom ( skb ,
sizeof ( struct rndis_packet_msg_type ) ) ;
if ( ! skb_rndis )
goto drop ;
2006-06-13 20:54:40 +04:00
2005-04-17 02:20:36 +04:00
dev_kfree_skb_any ( skb ) ;
skb = skb_rndis ;
rndis_add_hdr ( skb ) ;
length = skb - > len ;
}
req - > buf = skb - > data ;
req - > context = skb ;
req - > complete = tx_complete ;
/* use zlp framing on tx for strict CDC-Ether conformance,
* though any robust network rx path ignores extra padding .
* and some hardware doesn ' t like to write zlps .
*/
req - > zero = 1 ;
if ( ! dev - > zlp & & ( length % dev - > in_ep - > maxpacket ) = = 0 )
length + + ;
req - > length = length ;
/* throttle highspeed IRQ rate back slightly */
2007-08-02 11:00:38 +04:00
if ( gadget_is_dualspeed ( dev - > gadget ) )
req - > no_interrupt = ( dev - > gadget - > speed = = USB_SPEED_HIGH )
? ( ( atomic_read ( & dev - > tx_qlen ) % qmult ) ! = 0 )
: 0 ;
2005-04-17 02:20:36 +04:00
retval = usb_ep_queue ( dev - > in_ep , req , GFP_ATOMIC ) ;
switch ( retval ) {
default :
DEBUG ( dev , " tx queue err %d \n " , retval ) ;
break ;
case 0 :
net - > trans_start = jiffies ;
atomic_inc ( & dev - > tx_qlen ) ;
}
if ( retval ) {
drop :
dev - > stats . tx_dropped + + ;
dev_kfree_skb_any ( skb ) ;
2006-08-22 02:26:38 +04:00
spin_lock_irqsave ( & dev - > req_lock , flags ) ;
2005-04-17 02:20:36 +04:00
if ( list_empty ( & dev - > tx_reqs ) )
netif_start_queue ( net ) ;
list_add ( & req - > list , & dev - > tx_reqs ) ;
2006-08-22 02:26:38 +04:00
spin_unlock_irqrestore ( & dev - > req_lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
/*-------------------------------------------------------------------------*/
# ifdef CONFIG_USB_ETH_RNDIS
2005-04-29 00:48:09 +04:00
/* The interrupt endpoint is used in RNDIS to notify the host when messages
* other than data packets are available . . . notably the REMOTE_NDIS_ * _CMPLT
* messages , but also REMOTE_NDIS_INDICATE_STATUS_MSG and potentially even
* REMOTE_NDIS_KEEPALIVE_MSG .
*
* The RNDIS control queue is processed by GET_ENCAPSULATED_RESPONSE , and
* normally just one notification will be queued .
*/
2005-10-21 11:21:58 +04:00
static struct usb_request * eth_req_alloc ( struct usb_ep * , unsigned , gfp_t ) ;
2005-04-29 00:48:09 +04:00
static void eth_req_free ( struct usb_ep * ep , struct usb_request * req ) ;
2005-04-17 02:20:36 +04:00
static void
rndis_control_ack_complete ( struct usb_ep * ep , struct usb_request * req )
{
2005-04-29 00:48:09 +04:00
struct eth_dev * dev = ep - > driver_data ;
2005-04-17 02:20:36 +04:00
if ( req - > status | | req - > actual ! = req - > length )
2005-04-29 00:48:09 +04:00
DEBUG ( dev ,
2005-04-17 02:20:36 +04:00
" rndis control ack complete --> %d, %d/%d \n " ,
req - > status , req - > actual , req - > length ) ;
2005-04-29 00:48:09 +04:00
req - > context = NULL ;
if ( req ! = dev - > stat_req )
eth_req_free ( ep , req ) ;
2005-04-17 02:20:36 +04:00
}
static int rndis_control_ack ( struct net_device * net )
{
struct eth_dev * dev = netdev_priv ( net ) ;
2006-08-22 02:31:05 +04:00
int length ;
2005-04-19 04:39:34 +04:00
struct usb_request * resp = dev - > stat_req ;
2006-06-13 20:54:40 +04:00
2005-04-17 02:20:36 +04:00
/* in case RNDIS calls this after disconnect */
2005-04-19 04:39:34 +04:00
if ( ! dev - > status ) {
2005-04-17 02:20:36 +04:00
DEBUG ( dev , " status ENODEV \n " ) ;
return - ENODEV ;
}
2005-04-29 00:48:09 +04:00
/* in case queue length > 1 */
if ( resp - > context ) {
resp = eth_req_alloc ( dev - > status_ep , 8 , GFP_ATOMIC ) ;
if ( ! resp )
return - ENOMEM ;
}
2005-04-17 02:20:36 +04:00
/* Send RNDIS RESPONSE_AVAILABLE notification;
* USB_CDC_NOTIFY_RESPONSE_AVAILABLE should work too
*/
resp - > length = 8 ;
resp - > complete = rndis_control_ack_complete ;
2005-04-29 00:48:09 +04:00
resp - > context = dev ;
2006-06-13 20:54:40 +04:00
2005-04-17 02:20:36 +04:00
* ( ( __le32 * ) resp - > buf ) = __constant_cpu_to_le32 ( 1 ) ;
* ( ( __le32 * ) resp - > buf + 1 ) = __constant_cpu_to_le32 ( 0 ) ;
2006-06-13 20:54:40 +04:00
2005-04-17 02:20:36 +04:00
length = usb_ep_queue ( dev - > status_ep , resp , GFP_ATOMIC ) ;
if ( length < 0 ) {
resp - > status = 0 ;
rndis_control_ack_complete ( dev - > status_ep , resp ) ;
}
2006-06-13 20:54:40 +04:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-05-16 19:26:38 +04:00
# else
# define rndis_control_ack NULL
2005-04-17 02:20:36 +04:00
# endif /* RNDIS */
2005-10-21 11:21:58 +04:00
static void eth_start ( struct eth_dev * dev , gfp_t gfp_flags )
2005-04-17 02:20:36 +04:00
{
DEBUG ( dev , " %s \n " , __FUNCTION__ ) ;
/* fill the rx queue */
rx_fill ( dev , gfp_flags ) ;
2006-06-13 20:54:40 +04:00
/* and open the tx floodgates */
2005-04-17 02:20:36 +04:00
atomic_set ( & dev - > tx_qlen , 0 ) ;
netif_wake_queue ( dev - > net ) ;
2005-05-16 19:26:38 +04:00
if ( rndis_active ( dev ) ) {
2005-04-17 02:20:36 +04:00
rndis_set_param_medium ( dev - > rndis_config ,
NDIS_MEDIUM_802_3 ,
2005-04-19 04:39:34 +04:00
BITRATE ( dev - > gadget ) / 100 ) ;
2005-04-29 00:48:09 +04:00
( void ) rndis_signal_connect ( dev - > rndis_config ) ;
2005-04-17 02:20:36 +04:00
}
}
static int eth_open ( struct net_device * net )
{
struct eth_dev * dev = netdev_priv ( net ) ;
DEBUG ( dev , " %s \n " , __FUNCTION__ ) ;
if ( netif_carrier_ok ( dev - > net ) )
eth_start ( dev , GFP_KERNEL ) ;
return 0 ;
}
static int eth_stop ( struct net_device * net )
{
struct eth_dev * dev = netdev_priv ( net ) ;
VDEBUG ( dev , " %s \n " , __FUNCTION__ ) ;
netif_stop_queue ( net ) ;
DEBUG ( dev , " stop stats: rx/tx %ld/%ld, errs %ld/%ld \n " ,
2006-06-13 20:54:40 +04:00
dev - > stats . rx_packets , dev - > stats . tx_packets ,
2005-04-17 02:20:36 +04:00
dev - > stats . rx_errors , dev - > stats . tx_errors
) ;
/* ensure there are no more active requests */
if ( dev - > config ) {
usb_ep_disable ( dev - > in_ep ) ;
usb_ep_disable ( dev - > out_ep ) ;
if ( netif_carrier_ok ( dev - > net ) ) {
DEBUG ( dev , " host still using in/out endpoints \n " ) ;
// FIXME idiom may leave toggle wrong here
usb_ep_enable ( dev - > in_ep , dev - > in ) ;
usb_ep_enable ( dev - > out_ep , dev - > out ) ;
}
if ( dev - > status_ep ) {
usb_ep_disable ( dev - > status_ep ) ;
usb_ep_enable ( dev - > status_ep , dev - > status ) ;
}
}
2006-06-13 20:54:40 +04:00
2005-05-16 19:26:38 +04:00
if ( rndis_active ( dev ) ) {
2007-08-02 11:00:38 +04:00
rndis_set_param_medium ( dev - > rndis_config , NDIS_MEDIUM_802_3 , 0 ) ;
2005-04-29 00:48:09 +04:00
( void ) rndis_signal_disconnect ( dev - > rndis_config ) ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
/*-------------------------------------------------------------------------*/
2005-04-29 00:48:09 +04:00
static struct usb_request *
2005-10-21 11:21:58 +04:00
eth_req_alloc ( struct usb_ep * ep , unsigned size , gfp_t gfp_flags )
2005-04-17 02:20:36 +04:00
{
struct usb_request * req ;
2005-04-29 00:48:09 +04:00
req = usb_ep_alloc_request ( ep , gfp_flags ) ;
2005-04-17 02:20:36 +04:00
if ( ! req )
return NULL ;
2005-04-29 00:48:09 +04:00
req - > buf = kmalloc ( size , gfp_flags ) ;
2005-04-17 02:20:36 +04:00
if ( ! req - > buf ) {
usb_ep_free_request ( ep , req ) ;
req = NULL ;
}
return req ;
}
static void
eth_req_free ( struct usb_ep * ep , struct usb_request * req )
{
kfree ( req - > buf ) ;
usb_ep_free_request ( ep , req ) ;
}
2006-07-07 02:48:53 +04:00
static void /* __init_or_exit */
2005-04-17 02:20:36 +04:00
eth_unbind ( struct usb_gadget * gadget )
{
struct eth_dev * dev = get_gadget_data ( gadget ) ;
DEBUG ( dev , " unbind \n " ) ;
rndis_deregister ( dev - > rndis_config ) ;
rndis_exit ( ) ;
/* we've already been disconnected ... no i/o is active */
if ( dev - > req ) {
eth_req_free ( gadget - > ep0 , dev - > req ) ;
dev - > req = NULL ;
}
if ( dev - > stat_req ) {
eth_req_free ( dev - > status_ep , dev - > stat_req ) ;
dev - > stat_req = NULL ;
}
unregister_netdev ( dev - > net ) ;
free_netdev ( dev - > net ) ;
/* assuming we used keventd, it must quiesce too */
flush_scheduled_work ( ) ;
set_gadget_data ( gadget , NULL ) ;
}
2006-07-07 02:48:53 +04:00
static u8 __devinit nibble ( unsigned char c )
2005-04-17 02:20:36 +04:00
{
if ( likely ( isdigit ( c ) ) )
return c - ' 0 ' ;
c = toupper ( c ) ;
if ( likely ( isxdigit ( c ) ) )
return 10 + c - ' A ' ;
return 0 ;
}
2006-07-07 02:48:53 +04:00
static int __devinit get_ether_addr ( const char * str , u8 * dev_addr )
2005-04-17 02:20:36 +04:00
{
if ( str ) {
unsigned i ;
for ( i = 0 ; i < 6 ; i + + ) {
unsigned char num ;
if ( ( * str = = ' . ' ) | | ( * str = = ' : ' ) )
str + + ;
num = nibble ( * str + + ) < < 4 ;
num | = ( nibble ( * str + + ) ) ;
dev_addr [ i ] = num ;
}
if ( is_valid_ether_addr ( dev_addr ) )
2006-02-18 23:31:23 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
random_ether_addr ( dev_addr ) ;
2006-02-18 23:31:23 +03:00
return 1 ;
2005-04-17 02:20:36 +04:00
}
2006-07-07 02:48:53 +04:00
static int __devinit
2005-04-17 02:20:36 +04:00
eth_bind ( struct usb_gadget * gadget )
{
struct eth_dev * dev ;
struct net_device * net ;
u8 cdc = 1 , zlp = 1 , rndis = 1 ;
struct usb_ep * in_ep , * out_ep , * status_ep = NULL ;
int status = - ENOMEM ;
2005-07-14 02:18:30 +04:00
int gcnum ;
2005-04-17 02:20:36 +04:00
/* these flags are only ever cleared; compiler take note */
# ifndef DEV_CONFIG_CDC
cdc = 0 ;
# endif
# ifndef CONFIG_USB_ETH_RNDIS
rndis = 0 ;
# endif
/* Because most host side USB stacks handle CDC Ethernet, that
* standard protocol is _strongly_ preferred for interop purposes .
* ( By everyone except Microsoft . )
*/
2005-07-14 02:18:30 +04:00
if ( gadget_is_pxa ( gadget ) ) {
2005-04-17 02:20:36 +04:00
/* pxa doesn't support altsettings */
cdc = 0 ;
2006-08-31 00:24:56 +04:00
} else if ( gadget_is_musbhdrc ( gadget ) ) {
/* reduce tx dma overhead by avoiding special cases */
zlp = 0 ;
2005-04-17 02:20:36 +04:00
} else if ( gadget_is_sh ( gadget ) ) {
/* sh doesn't support multiple interfaces or configs */
cdc = 0 ;
rndis = 0 ;
} else if ( gadget_is_sa1100 ( gadget ) ) {
/* hardware can't write zlps */
zlp = 0 ;
/* sa1100 CAN do CDC, without status endpoint ... we use
* non - CDC to be compatible with ARM Linux - 2.4 " usb-eth " .
*/
cdc = 0 ;
2005-07-14 02:18:30 +04:00
}
gcnum = usb_gadget_controller_number ( gadget ) ;
if ( gcnum > = 0 )
device_desc . bcdDevice = cpu_to_le16 ( 0x0200 + gcnum ) ;
else {
2005-04-17 02:20:36 +04:00
/* can't assume CDC works. don't want to default to
* anything less functional on CDC - capable hardware ,
* so we fail in this case .
*/
dev_err ( & gadget - > dev ,
" controller '%s' not recognized \n " ,
gadget - > name ) ;
return - ENODEV ;
}
snprintf ( manufacturer , sizeof manufacturer , " %s %s/%s " ,
2006-10-02 13:18:13 +04:00
init_utsname ( ) - > sysname , init_utsname ( ) - > release ,
2005-04-17 02:20:36 +04:00
gadget - > name ) ;
/* If there's an RNDIS configuration, that's what Windows wants to
* be using . . . so use these product IDs here and in the " linux.inf "
* needed to install MSFT drivers . Current Linux kernels will use
* the second configuration if it ' s CDC Ethernet , and need some help
* to choose the right configuration otherwise .
*/
if ( rndis ) {
device_desc . idVendor =
__constant_cpu_to_le16 ( RNDIS_VENDOR_NUM ) ;
device_desc . idProduct =
__constant_cpu_to_le16 ( RNDIS_PRODUCT_NUM ) ;
snprintf ( product_desc , sizeof product_desc ,
" RNDIS/%s " , driver_desc ) ;
/* CDC subset ... recognized by Linux since 2.4.10, but Windows
2006-12-12 02:59:04 +03:00
* drivers aren ' t widely available . ( That may be improved by
* supporting one submode of the " SAFE " variant of MDLM . )
2005-04-17 02:20:36 +04:00
*/
} else if ( ! cdc ) {
device_desc . idVendor =
__constant_cpu_to_le16 ( SIMPLE_VENDOR_NUM ) ;
device_desc . idProduct =
__constant_cpu_to_le16 ( SIMPLE_PRODUCT_NUM ) ;
}
/* support optional vendor/distro customization */
if ( idVendor ) {
if ( ! idProduct ) {
dev_err ( & gadget - > dev , " idVendor needs idProduct! \n " ) ;
return - ENODEV ;
}
device_desc . idVendor = cpu_to_le16 ( idVendor ) ;
device_desc . idProduct = cpu_to_le16 ( idProduct ) ;
if ( bcdDevice )
device_desc . bcdDevice = cpu_to_le16 ( bcdDevice ) ;
}
if ( iManufacturer )
strlcpy ( manufacturer , iManufacturer , sizeof manufacturer ) ;
if ( iProduct )
strlcpy ( product_desc , iProduct , sizeof product_desc ) ;
2006-02-18 23:31:23 +03:00
if ( iSerialNumber ) {
device_desc . iSerialNumber = STRING_SERIALNUMBER ,
strlcpy ( serial_number , iSerialNumber , sizeof serial_number ) ;
}
2005-04-17 02:20:36 +04:00
/* all we really need is bulk IN/OUT */
usb_ep_autoconfig_reset ( gadget ) ;
in_ep = usb_ep_autoconfig ( gadget , & fs_source_desc ) ;
if ( ! in_ep ) {
autoconf_fail :
dev_err ( & gadget - > dev ,
" can't autoconfigure on %s \n " ,
gadget - > name ) ;
return - ENODEV ;
}
in_ep - > driver_data = in_ep ; /* claim */
2006-06-13 20:54:40 +04:00
2005-04-17 02:20:36 +04:00
out_ep = usb_ep_autoconfig ( gadget , & fs_sink_desc ) ;
if ( ! out_ep )
goto autoconf_fail ;
out_ep - > driver_data = out_ep ; /* claim */
# if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
/* CDC Ethernet control interface doesn't require a status endpoint.
* Since some hosts expect one , try to allocate one anyway .
*/
if ( cdc | | rndis ) {
status_ep = usb_ep_autoconfig ( gadget , & fs_status_desc ) ;
if ( status_ep ) {
status_ep - > driver_data = status_ep ; /* claim */
} else if ( rndis ) {
dev_err ( & gadget - > dev ,
" can't run RNDIS on %s \n " ,
gadget - > name ) ;
return - ENODEV ;
# ifdef DEV_CONFIG_CDC
/* pxa25x only does CDC subset; often used with RNDIS */
} else if ( cdc ) {
control_intf . bNumEndpoints = 0 ;
/* FIXME remove endpoint from descriptor list */
# endif
}
}
# endif
/* one config: cdc, else minimal subset */
if ( ! cdc ) {
eth_config . bNumInterfaces = 1 ;
eth_config . iConfiguration = STRING_SUBSET ;
2006-12-12 02:59:04 +03:00
/* use functions to set these up, in case we're built to work
* with multiple controllers and must override CDC Ethernet .
*/
2005-04-17 02:20:36 +04:00
fs_subset_descriptors ( ) ;
hs_subset_descriptors ( ) ;
}
2006-04-02 22:20:43 +04:00
device_desc . bMaxPacketSize0 = gadget - > ep0 - > maxpacket ;
usb_gadget_set_selfpowered ( gadget ) ;
2005-04-17 02:20:36 +04:00
/* For now RNDIS is always a second config */
if ( rndis )
device_desc . bNumConfigurations = 2 ;
2007-08-02 11:00:38 +04:00
if ( gadget_is_dualspeed ( gadget ) ) {
if ( rndis )
dev_qualifier . bNumConfigurations = 2 ;
else if ( ! cdc )
dev_qualifier . bDeviceClass = USB_CLASS_VENDOR_SPEC ;
2005-04-17 02:20:36 +04:00
2007-08-02 11:00:38 +04:00
/* assumes ep0 uses the same value for both speeds ... */
dev_qualifier . bMaxPacketSize0 = device_desc . bMaxPacketSize0 ;
2005-04-17 02:20:36 +04:00
2007-08-02 11:00:38 +04:00
/* and that all endpoints are dual-speed */
hs_source_desc . bEndpointAddress =
fs_source_desc . bEndpointAddress ;
hs_sink_desc . bEndpointAddress =
fs_sink_desc . bEndpointAddress ;
2005-04-17 02:20:36 +04:00
# if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
2007-08-02 11:00:38 +04:00
if ( status_ep )
hs_status_desc . bEndpointAddress =
fs_status_desc . bEndpointAddress ;
2005-04-17 02:20:36 +04:00
# endif
2007-08-02 11:00:38 +04:00
}
2005-04-17 02:20:36 +04:00
2007-08-02 11:00:38 +04:00
if ( gadget_is_otg ( gadget ) ) {
2005-04-17 02:20:36 +04:00
otg_descriptor . bmAttributes | = USB_OTG_HNP ,
eth_config . bmAttributes | = USB_CONFIG_ATT_WAKEUP ;
eth_config . bMaxPower = 4 ;
# ifdef CONFIG_USB_ETH_RNDIS
rndis_config . bmAttributes | = USB_CONFIG_ATT_WAKEUP ;
rndis_config . bMaxPower = 4 ;
# endif
}
2006-06-13 20:54:40 +04:00
net = alloc_etherdev ( sizeof * dev ) ;
if ( ! net )
2005-04-17 02:20:36 +04:00
return status ;
dev = netdev_priv ( net ) ;
spin_lock_init ( & dev - > lock ) ;
2006-08-22 02:26:38 +04:00
spin_lock_init ( & dev - > req_lock ) ;
2006-11-22 17:57:56 +03:00
INIT_WORK ( & dev - > work , eth_work ) ;
2005-04-17 02:20:36 +04:00
INIT_LIST_HEAD ( & dev - > tx_reqs ) ;
INIT_LIST_HEAD ( & dev - > rx_reqs ) ;
/* network device setup */
dev - > net = net ;
strcpy ( net - > name , " usb%d " ) ;
dev - > cdc = cdc ;
dev - > zlp = zlp ;
dev - > in_ep = in_ep ;
dev - > out_ep = out_ep ;
dev - > status_ep = status_ep ;
/* Module params for these addresses should come from ID proms.
* The host side address is used with CDC and RNDIS , and commonly
2006-12-12 02:59:04 +03:00
* ends up in a persistent config database . It ' s not clear if
* host side code for the SAFE thing cares - - its original BLAN
* thing didn ' t , Sharp never assigned those addresses on Zaurii .
2005-04-17 02:20:36 +04:00
*/
2006-02-18 23:31:23 +03:00
if ( get_ether_addr ( dev_addr , net - > dev_addr ) )
dev_warn ( & gadget - > dev ,
" using random %s ethernet address \n " , " self " ) ;
2006-12-12 02:59:04 +03:00
if ( get_ether_addr ( host_addr , dev - > host_mac ) )
dev_warn ( & gadget - > dev ,
" using random %s ethernet address \n " , " host " ) ;
snprintf ( ethaddr , sizeof ethaddr , " %02X%02X%02X%02X%02X%02X " ,
dev - > host_mac [ 0 ] , dev - > host_mac [ 1 ] ,
dev - > host_mac [ 2 ] , dev - > host_mac [ 3 ] ,
dev - > host_mac [ 4 ] , dev - > host_mac [ 5 ] ) ;
2005-04-17 02:20:36 +04:00
if ( rndis ) {
status = rndis_init ( ) ;
if ( status < 0 ) {
dev_err ( & gadget - > dev , " can't init RNDIS, %d \n " ,
status ) ;
goto fail ;
}
}
net - > change_mtu = eth_change_mtu ;
net - > get_stats = eth_get_stats ;
net - > hard_start_xmit = eth_start_xmit ;
net - > open = eth_open ;
net - > stop = eth_stop ;
// watchdog_timeo, tx_timeout ...
// set_multicast_list
SET_ETHTOOL_OPS ( net , & ops ) ;
/* preallocate control message data and buffer */
2005-04-29 00:48:09 +04:00
dev - > req = eth_req_alloc ( gadget - > ep0 , USB_BUFSIZ , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! dev - > req )
goto fail ;
dev - > req - > complete = eth_setup_complete ;
/* ... and maybe likewise for status transfer */
2005-06-29 13:15:32 +04:00
# if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
2005-04-17 02:20:36 +04:00
if ( dev - > status_ep ) {
dev - > stat_req = eth_req_alloc ( dev - > status_ep ,
2005-04-29 00:48:09 +04:00
STATUS_BYTECOUNT , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! dev - > stat_req ) {
eth_req_free ( gadget - > ep0 , dev - > req ) ;
goto fail ;
}
2005-04-29 00:48:09 +04:00
dev - > stat_req - > context = NULL ;
2005-04-17 02:20:36 +04:00
}
2005-06-13 17:55:03 +04:00
# endif
2005-04-17 02:20:36 +04:00
/* finish hookup to lower layer ... */
dev - > gadget = gadget ;
set_gadget_data ( gadget , dev ) ;
gadget - > ep0 - > driver_data = dev ;
2006-06-13 20:54:40 +04:00
2005-04-17 02:20:36 +04:00
/* two kinds of host-initiated state changes:
* - iff DATA transfer is active , carrier is " on "
* - tx queueing enabled if open * and * carrier is " on "
*/
netif_stop_queue ( dev - > net ) ;
netif_carrier_off ( dev - > net ) ;
2006-06-13 20:54:40 +04:00
SET_NETDEV_DEV ( dev - > net , & gadget - > dev ) ;
status = register_netdev ( dev - > net ) ;
2005-04-17 02:20:36 +04:00
if ( status < 0 )
goto fail1 ;
INFO ( dev , " %s, version: " DRIVER_VERSION " \n " , driver_desc ) ;
INFO ( dev , " using %s, OUT %s IN %s%s%s \n " , gadget - > name ,
2005-04-29 00:48:09 +04:00
out_ep - > name , in_ep - > name ,
status_ep ? " STATUS " : " " ,
status_ep ? status_ep - > name : " "
2005-04-17 02:20:36 +04:00
) ;
INFO ( dev , " MAC %02x:%02x:%02x:%02x:%02x:%02x \n " ,
net - > dev_addr [ 0 ] , net - > dev_addr [ 1 ] ,
net - > dev_addr [ 2 ] , net - > dev_addr [ 3 ] ,
net - > dev_addr [ 4 ] , net - > dev_addr [ 5 ] ) ;
if ( cdc | | rndis )
INFO ( dev , " HOST MAC %02x:%02x:%02x:%02x:%02x:%02x \n " ,
dev - > host_mac [ 0 ] , dev - > host_mac [ 1 ] ,
dev - > host_mac [ 2 ] , dev - > host_mac [ 3 ] ,
dev - > host_mac [ 4 ] , dev - > host_mac [ 5 ] ) ;
if ( rndis ) {
u32 vendorID = 0 ;
/* FIXME RNDIS vendor id == "vendor NIC code" == ? */
2006-06-13 20:54:40 +04:00
2005-04-17 02:20:36 +04:00
dev - > rndis_config = rndis_register ( rndis_control_ack ) ;
if ( dev - > rndis_config < 0 ) {
fail0 :
unregister_netdev ( dev - > net ) ;
status = - ENODEV ;
goto fail ;
}
2006-06-13 20:54:40 +04:00
2005-04-17 02:20:36 +04:00
/* these set up a lot of the OIDs that RNDIS needs */
rndis_set_host_mac ( dev - > rndis_config , dev - > host_mac ) ;
if ( rndis_set_param_dev ( dev - > rndis_config , dev - > net ,
2005-04-29 00:45:25 +04:00
& dev - > stats , & dev - > cdc_filter ) )
2005-04-17 02:20:36 +04:00
goto fail0 ;
2007-08-02 11:00:38 +04:00
if ( rndis_set_param_vendor ( dev - > rndis_config , vendorID ,
manufacturer ) )
2005-04-17 02:20:36 +04:00
goto fail0 ;
2007-08-02 11:00:38 +04:00
if ( rndis_set_param_medium ( dev - > rndis_config ,
NDIS_MEDIUM_802_3 , 0 ) )
2005-04-17 02:20:36 +04:00
goto fail0 ;
INFO ( dev , " RNDIS ready \n " ) ;
}
return status ;
fail1 :
dev_dbg ( & gadget - > dev , " register_netdev failed, %d \n " , status ) ;
fail :
eth_unbind ( gadget ) ;
return status ;
}
/*-------------------------------------------------------------------------*/
static void
eth_suspend ( struct usb_gadget * gadget )
{
struct eth_dev * dev = get_gadget_data ( gadget ) ;
DEBUG ( dev , " suspend \n " ) ;
dev - > suspended = 1 ;
}
static void
eth_resume ( struct usb_gadget * gadget )
{
struct eth_dev * dev = get_gadget_data ( gadget ) ;
DEBUG ( dev , " resume \n " ) ;
dev - > suspended = 0 ;
}
/*-------------------------------------------------------------------------*/
static struct usb_gadget_driver eth_driver = {
2005-04-29 00:48:09 +04:00
. speed = DEVSPEED ,
2005-04-17 02:20:36 +04:00
. function = ( char * ) driver_desc ,
. bind = eth_bind ,
2006-06-30 09:27:36 +04:00
. unbind = eth_unbind ,
2005-04-17 02:20:36 +04:00
. setup = eth_setup ,
. disconnect = eth_disconnect ,
. suspend = eth_suspend ,
. resume = eth_resume ,
2006-06-13 20:54:40 +04:00
. driver = {
2005-04-17 02:20:36 +04:00
. name = ( char * ) shortname ,
2005-10-10 13:52:33 +04:00
. owner = THIS_MODULE ,
2005-04-17 02:20:36 +04:00
} ,
} ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_AUTHOR ( " David Brownell, Benedikt Spanger " ) ;
MODULE_LICENSE ( " GPL " ) ;
static int __init init ( void )
{
return usb_gadget_register_driver ( & eth_driver ) ;
}
module_init ( init ) ;
static void __exit cleanup ( void )
{
usb_gadget_unregister_driver ( & eth_driver ) ;
}
module_exit ( cleanup ) ;