2005-08-31 20:54:36 +04:00
/*
* Host Side support for RNDIS Networking Links
* Copyright ( C ) 2005 by David Brownell
*
* 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
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/ethtool.h>
# include <linux/workqueue.h>
# include <linux/mii.h>
# include <linux/usb.h>
2006-06-13 20:57:47 +04:00
# include <linux/usb/cdc.h>
2008-01-26 01:51:45 +03:00
# include <linux/usb/usbnet.h>
# include <linux/usb/rndis_host.h>
2005-08-31 20:54:36 +04:00
/*
* RNDIS is NDIS remoted over USB . It ' s a MSFT variant of CDC ACM . . . of
* course ACM was intended for modems , not Ethernet links ! USB ' s standard
* for Ethernet links is " CDC Ethernet " , which is significantly simpler .
2006-04-02 22:19:08 +04:00
*
* NOTE that Microsoft ' s " RNDIS 1.0 " specification is incomplete . Issues
* include :
* - Power management in particular relies on information that ' s scattered
* through other documentation , and which is incomplete or incorrect even
* there .
* - There are various undocumented protocol requirements , such as the
* need to send unused garbage in control - OUT messages .
* - In some cases , MS - Windows will emit undocumented requests ; this
* matters more to peripheral implementations than host ones .
*
2006-12-15 03:01:28 +03:00
* Moreover there ' s a no - open - specs variant of RNDIS called " ActiveSync " .
*
2006-04-02 22:19:08 +04:00
* For these reasons and others , * * USE OF RNDIS IS STRONGLY DISCOURAGED * * in
* favor of such non - proprietary alternatives as CDC Ethernet or the newer ( and
* currently rare ) " Ethernet Emulation Model " ( EEM ) .
2005-08-31 20:54:36 +04:00
*/
/*
* RNDIS notifications from device : command completion ; " reverse "
* keepalives ; etc
*/
2008-01-26 01:51:17 +03:00
void rndis_status ( struct usbnet * dev , struct urb * urb )
2005-08-31 20:54:36 +04:00
{
devdbg ( dev , " rndis status urb, len %d stat %d " ,
urb - > actual_length , urb - > status ) ;
// FIXME for keepalives, respond immediately (asynchronously)
// if not an RNDIS status, do like cdc_status(dev,urb) does
}
2008-01-26 01:51:17 +03:00
EXPORT_SYMBOL_GPL ( rndis_status ) ;
2005-08-31 20:54:36 +04:00
2009-07-30 20:41:52 +04:00
/*
* RNDIS indicate messages .
*/
static void rndis_msg_indicate ( struct usbnet * dev , struct rndis_indicate * msg ,
int buflen )
{
struct cdc_state * info = ( void * ) & dev - > data ;
struct device * udev = & info - > control - > dev ;
if ( dev - > driver_info - > indication ) {
dev - > driver_info - > indication ( dev , msg , buflen ) ;
} else {
switch ( msg - > status ) {
case RNDIS_STATUS_MEDIA_CONNECT :
dev_info ( udev , " rndis media connect \n " ) ;
break ;
case RNDIS_STATUS_MEDIA_DISCONNECT :
dev_info ( udev , " rndis media disconnect \n " ) ;
break ;
default :
dev_info ( udev , " rndis indication: 0x%08x \n " ,
le32_to_cpu ( msg - > status ) ) ;
}
}
}
2005-08-31 20:54:36 +04:00
/*
* RPC done RNDIS - style . Caller guarantees :
* - message is properly byteswapped
* - there ' s no other request pending
* - buf can hold up to 1 KB response ( required by RNDIS spec )
* On return , the first few entries are already byteswapped .
*
* Call context is likely probe ( ) , before interface name is known ,
* which is why we won ' t try to use it in the diagnostics .
*/
2008-06-18 16:40:12 +04:00
int rndis_command ( struct usbnet * dev , struct rndis_msg_hdr * buf , int buflen )
2005-08-31 20:54:36 +04:00
{
struct cdc_state * info = ( void * ) & dev - > data ;
2006-12-15 03:01:28 +03:00
int master_ifnum ;
2005-08-31 20:54:36 +04:00
int retval ;
unsigned count ;
__le32 rsp ;
u32 xid = 0 , msg_len , request_id ;
/* REVISIT when this gets called from contexts other than probe() or
* disconnect ( ) : either serialize , or dispatch responses on xid
*/
2006-12-15 03:01:28 +03:00
/* Issue the request; xid is unique, don't bother byteswapping it */
2005-08-31 20:54:36 +04:00
if ( likely ( buf - > msg_type ! = RNDIS_MSG_HALT
& & buf - > msg_type ! = RNDIS_MSG_RESET ) ) {
xid = dev - > xid + + ;
if ( ! xid )
xid = dev - > xid + + ;
buf - > request_id = ( __force __le32 ) xid ;
}
2006-12-15 03:01:28 +03:00
master_ifnum = info - > control - > cur_altsetting - > desc . bInterfaceNumber ;
2005-08-31 20:54:36 +04:00
retval = usb_control_msg ( dev - > udev ,
usb_sndctrlpipe ( dev - > udev , 0 ) ,
USB_CDC_SEND_ENCAPSULATED_COMMAND ,
USB_TYPE_CLASS | USB_RECIP_INTERFACE ,
2006-12-15 03:01:28 +03:00
0 , master_ifnum ,
2005-08-31 20:54:36 +04:00
buf , le32_to_cpu ( buf - > msg_len ) ,
RNDIS_CONTROL_TIMEOUT_MS ) ;
if ( unlikely ( retval < 0 | | xid = = 0 ) )
return retval ;
// FIXME Seems like some devices discard responses when
// we time out and cancel our "get response" requests...
// so, this is fragile. Probably need to poll for status.
/* ignore status endpoint, just poll the control channel;
* the request probably completed immediately
*/
rsp = buf - > msg_type | RNDIS_MSG_COMPLETION ;
for ( count = 0 ; count < 10 ; count + + ) {
2006-12-15 03:01:28 +03:00
memset ( buf , 0 , CONTROL_BUFFER_SIZE ) ;
2005-08-31 20:54:36 +04:00
retval = usb_control_msg ( dev - > udev ,
usb_rcvctrlpipe ( dev - > udev , 0 ) ,
USB_CDC_GET_ENCAPSULATED_RESPONSE ,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE ,
2006-12-15 03:01:28 +03:00
0 , master_ifnum ,
2008-06-18 16:40:12 +04:00
buf , buflen ,
2005-08-31 20:54:36 +04:00
RNDIS_CONTROL_TIMEOUT_MS ) ;
if ( likely ( retval > = 8 ) ) {
msg_len = le32_to_cpu ( buf - > msg_len ) ;
request_id = ( __force u32 ) buf - > request_id ;
if ( likely ( buf - > msg_type = = rsp ) ) {
if ( likely ( request_id = = xid ) ) {
if ( unlikely ( rsp = = RNDIS_MSG_RESET_C ) )
return 0 ;
if ( likely ( RNDIS_STATUS_SUCCESS
= = buf - > status ) )
return 0 ;
dev_dbg ( & info - > control - > dev ,
" rndis reply status %08x \n " ,
le32_to_cpu ( buf - > status ) ) ;
return - EL3RST ;
}
dev_dbg ( & info - > control - > dev ,
" rndis reply id %d expected %d \n " ,
request_id , xid ) ;
/* then likely retry */
} else switch ( buf - > msg_type ) {
2009-07-30 20:41:52 +04:00
case RNDIS_MSG_INDICATE : /* fault/event */
rndis_msg_indicate ( dev , ( void * ) buf , buflen ) ;
2008-01-26 01:51:34 +03:00
2005-08-31 20:54:36 +04:00
break ;
case RNDIS_MSG_KEEPALIVE : { /* ping */
struct rndis_keepalive_c * msg = ( void * ) buf ;
msg - > msg_type = RNDIS_MSG_KEEPALIVE_C ;
2009-02-15 09:56:56 +03:00
msg - > msg_len = cpu_to_le32 ( sizeof * msg ) ;
2005-08-31 20:54:36 +04:00
msg - > status = RNDIS_STATUS_SUCCESS ;
retval = usb_control_msg ( dev - > udev ,
usb_sndctrlpipe ( dev - > udev , 0 ) ,
USB_CDC_SEND_ENCAPSULATED_COMMAND ,
USB_TYPE_CLASS | USB_RECIP_INTERFACE ,
2006-12-15 03:01:28 +03:00
0 , master_ifnum ,
2005-08-31 20:54:36 +04:00
msg , sizeof * msg ,
RNDIS_CONTROL_TIMEOUT_MS ) ;
if ( unlikely ( retval < 0 ) )
dev_dbg ( & info - > control - > dev ,
" rndis keepalive err %d \n " ,
retval ) ;
}
break ;
default :
dev_dbg ( & info - > control - > dev ,
" unexpected rndis msg %08x len %d \n " ,
le32_to_cpu ( buf - > msg_type ) , msg_len ) ;
}
} else {
/* device probably issued a protocol stall; ignore */
dev_dbg ( & info - > control - > dev ,
" rndis response error, code %d \n " , retval ) ;
}
2008-05-15 03:20:16 +04:00
msleep ( 20 ) ;
2005-08-31 20:54:36 +04:00
}
dev_dbg ( & info - > control - > dev , " rndis response timeout \n " ) ;
return - ETIMEDOUT ;
}
2008-01-26 01:51:17 +03:00
EXPORT_SYMBOL_GPL ( rndis_command ) ;
2005-08-31 20:54:36 +04:00
2007-04-18 04:53:20 +04:00
/*
* rndis_query :
*
* Performs a query for @ oid along with 0 or more bytes of payload as
* specified by @ in_len . If @ reply_len is not set to - 1 then the reply
* length is checked against this value , resulting in an error if it
* doesn ' t match .
*
* NOTE : Adding a payload exactly or greater than the size of the expected
* response payload is an evident requirement MSFT added for ActiveSync .
*
* The only exception is for OIDs that return a variably sized response ,
* in which case no payload should be added . This undocumented ( and
* nonsensical ! ) issue was found by sniffing protocol requests from the
* ActiveSync 4.1 Windows driver .
*/
static int rndis_query ( struct usbnet * dev , struct usb_interface * intf ,
2008-03-17 01:21:54 +03:00
void * buf , __le32 oid , u32 in_len ,
2007-04-18 04:53:20 +04:00
void * * reply , int * reply_len )
{
int retval ;
union {
void * buf ;
struct rndis_msg_hdr * header ;
struct rndis_query * get ;
struct rndis_query_c * get_c ;
} u ;
u32 off , len ;
u . buf = buf ;
memset ( u . get , 0 , sizeof * u . get + in_len ) ;
u . get - > msg_type = RNDIS_MSG_QUERY ;
u . get - > msg_len = cpu_to_le32 ( sizeof * u . get + in_len ) ;
u . get - > oid = oid ;
u . get - > len = cpu_to_le32 ( in_len ) ;
2009-02-15 09:56:56 +03:00
u . get - > offset = cpu_to_le32 ( 20 ) ;
2007-04-18 04:53:20 +04:00
2008-06-18 16:40:12 +04:00
retval = rndis_command ( dev , u . header , CONTROL_BUFFER_SIZE ) ;
2007-04-18 04:53:20 +04:00
if ( unlikely ( retval < 0 ) ) {
dev_err ( & intf - > dev , " RNDIS_MSG_QUERY(0x%08x) failed, %d \n " ,
oid , retval ) ;
return retval ;
}
off = le32_to_cpu ( u . get_c - > offset ) ;
len = le32_to_cpu ( u . get_c - > len ) ;
if ( unlikely ( ( 8 + off + len ) > CONTROL_BUFFER_SIZE ) )
goto response_error ;
if ( * reply_len ! = - 1 & & len ! = * reply_len )
goto response_error ;
* reply = ( unsigned char * ) & u . get_c - > request_id + off ;
* reply_len = len ;
return retval ;
response_error :
dev_err ( & intf - > dev , " RNDIS_MSG_QUERY(0x%08x) "
" invalid response - off %d len %d \n " ,
oid , off , len ) ;
return - EDOM ;
}
2009-03-20 22:36:00 +03:00
/* same as usbnet_netdev_ops but MTU change not allowed */
static const struct net_device_ops rndis_netdev_ops = {
. ndo_open = usbnet_open ,
. ndo_stop = usbnet_stop ,
. ndo_start_xmit = usbnet_start_xmit ,
. ndo_tx_timeout = usbnet_tx_timeout ,
. ndo_set_mac_address = eth_mac_addr ,
. ndo_validate_addr = eth_validate_addr ,
} ;
2008-01-28 00:34:33 +03:00
int
generic_rndis_bind ( struct usbnet * dev , struct usb_interface * intf , int flags )
2005-08-31 20:54:36 +04:00
{
int retval ;
struct net_device * net = dev - > net ;
2007-01-16 13:03:01 +03:00
struct cdc_state * info = ( void * ) & dev - > data ;
2005-08-31 20:54:36 +04:00
union {
void * buf ;
struct rndis_msg_hdr * header ;
struct rndis_init * init ;
struct rndis_init_c * init_c ;
struct rndis_query * get ;
struct rndis_query_c * get_c ;
struct rndis_set * set ;
struct rndis_set_c * set_c ;
2008-01-26 01:50:55 +03:00
struct rndis_halt * halt ;
2005-08-31 20:54:36 +04:00
} u ;
2008-05-21 04:34:30 +04:00
u32 tmp ;
__le32 phym_unspec , * phym ;
2007-04-18 04:53:20 +04:00
int reply_len ;
unsigned char * bp ;
2005-08-31 20:54:36 +04:00
/* we can't rely on i/o from stack working, or stack allocation */
2006-12-15 03:01:28 +03:00
u . buf = kmalloc ( CONTROL_BUFFER_SIZE , GFP_KERNEL ) ;
2005-08-31 20:54:36 +04:00
if ( ! u . buf )
return - ENOMEM ;
retval = usbnet_generic_cdc_bind ( dev , intf ) ;
if ( retval < 0 )
2007-01-16 13:03:01 +03:00
goto fail ;
2005-08-31 20:54:36 +04:00
u . init - > msg_type = RNDIS_MSG_INIT ;
2009-02-15 09:56:56 +03:00
u . init - > msg_len = cpu_to_le32 ( sizeof * u . init ) ;
u . init - > major_version = cpu_to_le32 ( 1 ) ;
u . init - > minor_version = cpu_to_le32 ( 0 ) ;
2005-08-31 20:54:36 +04:00
2006-12-15 03:01:28 +03:00
/* max transfer (in spec) is 0x4000 at full speed, but for
* TX we ' ll stick to one Ethernet packet plus RNDIS framing .
* For RX we handle drivers that zero - pad to end - of - packet .
* Don ' t let userspace change these settings .
2007-04-18 04:53:20 +04:00
*
* NOTE : there still seems to be wierdness here , as if we need
* to do some more things to make sure WinCE targets accept this .
* They default to jumbograms of 8 KB or 16 KB , which is absurd
* for such low data rates and which is also more than Linux
* can usually expect to allocate for SKB data . . .
2006-12-15 03:01:28 +03:00
*/
net - > hard_header_len + = sizeof ( struct rndis_data_hdr ) ;
dev - > hard_mtu = net - > mtu + net - > hard_header_len ;
2008-03-14 01:56:36 +03:00
dev - > maxpacket = usb_maxpacket ( dev - > udev , dev - > out , 1 ) ;
if ( dev - > maxpacket = = 0 ) {
if ( netif_msg_probe ( dev ) )
dev_dbg ( & intf - > dev , " dev->maxpacket can't be 0 \n " ) ;
retval = - EINVAL ;
goto fail_and_release ;
}
2006-12-15 03:01:28 +03:00
dev - > rx_urb_size = dev - > hard_mtu + ( dev - > maxpacket + 1 ) ;
dev - > rx_urb_size & = ~ ( dev - > maxpacket - 1 ) ;
u . init - > max_transfer_size = cpu_to_le32 ( dev - > rx_urb_size ) ;
2009-03-20 22:36:00 +03:00
net - > netdev_ops = & rndis_netdev_ops ;
2008-06-18 16:40:12 +04:00
retval = rndis_command ( dev , u . header , CONTROL_BUFFER_SIZE ) ;
2005-08-31 20:54:36 +04:00
if ( unlikely ( retval < 0 ) ) {
/* it might not even be an RNDIS device!! */
dev_err ( & intf - > dev , " RNDIS init failed, %d \n " , retval ) ;
2007-04-18 04:53:20 +04:00
goto fail_and_release ;
2006-12-15 03:01:28 +03:00
}
tmp = le32_to_cpu ( u . init_c - > max_transfer_size ) ;
if ( tmp < dev - > hard_mtu ) {
2007-10-24 02:47:19 +04:00
if ( tmp < = net - > hard_header_len ) {
dev_err ( & intf - > dev ,
" dev can't take %u byte packets (max %u) \n " ,
dev - > hard_mtu , tmp ) ;
retval = - EINVAL ;
2008-01-26 01:50:55 +03:00
goto halt_fail_and_release ;
2007-10-24 02:47:19 +04:00
}
dev - > hard_mtu = tmp ;
net - > mtu = dev - > hard_mtu - net - > hard_header_len ;
dev_warn ( & intf - > dev ,
" dev can't take %u byte packets (max %u), "
" adjusting MTU to %u \n " ,
dev - > hard_mtu , tmp , net - > mtu ) ;
2005-08-31 20:54:36 +04:00
}
2006-12-15 03:01:28 +03:00
2005-08-31 20:54:36 +04:00
/* REVISIT: peripheral "alignment" request is ignored ... */
2006-12-15 03:01:28 +03:00
dev_dbg ( & intf - > dev ,
" hard mtu %u (%u from dev), rx buflen %Zu, align %d \n " ,
dev - > hard_mtu , tmp , dev - > rx_urb_size ,
2005-08-31 20:54:36 +04:00
1 < < le32_to_cpu ( u . init_c - > packet_alignment ) ) ;
2008-01-26 01:51:28 +03:00
/* module has some device initialization code needs to be done right
* after RNDIS_INIT */
if ( dev - > driver_info - > early_init & &
dev - > driver_info - > early_init ( dev ) ! = 0 )
goto halt_fail_and_release ;
2008-01-28 00:34:33 +03:00
/* Check physical medium */
2008-03-23 13:45:44 +03:00
phym = NULL ;
2008-01-28 00:34:33 +03:00
reply_len = sizeof * phym ;
retval = rndis_query ( dev , intf , u . buf , OID_GEN_PHYSICAL_MEDIUM ,
0 , ( void * * ) & phym , & reply_len ) ;
2008-03-23 13:45:44 +03:00
if ( retval ! = 0 | | ! phym ) {
2008-01-28 00:34:33 +03:00
/* OID is optional so don't fail here. */
2008-03-23 13:45:44 +03:00
phym_unspec = RNDIS_PHYSICAL_MEDIUM_UNSPECIFIED ;
phym = & phym_unspec ;
}
2008-01-28 00:34:33 +03:00
if ( ( flags & FLAG_RNDIS_PHYM_WIRELESS ) & &
* phym ! = RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN ) {
if ( netif_msg_probe ( dev ) )
dev_dbg ( & intf - > dev , " driver requires wireless "
" physical medium, but device is not. \n " ) ;
retval = - ENODEV ;
goto halt_fail_and_release ;
}
if ( ( flags & FLAG_RNDIS_PHYM_NOT_WIRELESS ) & &
* phym = = RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN ) {
if ( netif_msg_probe ( dev ) )
dev_dbg ( & intf - > dev , " driver requires non-wireless "
" physical medium, but device is wireless. \n " ) ;
retval = - ENODEV ;
goto halt_fail_and_release ;
}
2007-04-18 04:53:20 +04:00
/* Get designated host ethernet address */
reply_len = ETH_ALEN ;
retval = rndis_query ( dev , intf , u . buf , OID_802_3_PERMANENT_ADDRESS ,
48 , ( void * * ) & bp , & reply_len ) ;
if ( unlikely ( retval < 0 ) ) {
2005-08-31 20:54:36 +04:00
dev_err ( & intf - > dev , " rndis get ethaddr, %d \n " , retval ) ;
2008-01-26 01:50:55 +03:00
goto halt_fail_and_release ;
2005-08-31 20:54:36 +04:00
}
2007-04-18 04:53:20 +04:00
memcpy ( net - > dev_addr , bp , ETH_ALEN ) ;
2005-08-31 20:54:36 +04:00
/* set a nonzero filter to enable data transfers */
memset ( u . set , 0 , sizeof * u . set ) ;
u . set - > msg_type = RNDIS_MSG_SET ;
2009-02-15 09:56:56 +03:00
u . set - > msg_len = cpu_to_le32 ( 4 + sizeof * u . set ) ;
2005-08-31 20:54:36 +04:00
u . set - > oid = OID_GEN_CURRENT_PACKET_FILTER ;
2009-02-15 09:56:56 +03:00
u . set - > len = cpu_to_le32 ( 4 ) ;
u . set - > offset = cpu_to_le32 ( ( sizeof * u . set ) - 8 ) ;
2008-01-26 01:51:01 +03:00
* ( __le32 * ) ( u . buf + sizeof * u . set ) = RNDIS_DEFAULT_FILTER ;
2005-08-31 20:54:36 +04:00
2008-06-18 16:40:12 +04:00
retval = rndis_command ( dev , u . header , CONTROL_BUFFER_SIZE ) ;
2005-08-31 20:54:36 +04:00
if ( unlikely ( retval < 0 ) ) {
dev_err ( & intf - > dev , " rndis set packet filter, %d \n " , retval ) ;
2008-01-26 01:50:55 +03:00
goto halt_fail_and_release ;
2005-08-31 20:54:36 +04:00
}
retval = 0 ;
2007-01-16 13:03:01 +03:00
kfree ( u . buf ) ;
return retval ;
2008-01-26 01:50:55 +03:00
halt_fail_and_release :
memset ( u . halt , 0 , sizeof * u . halt ) ;
u . halt - > msg_type = RNDIS_MSG_HALT ;
2009-02-15 09:56:56 +03:00
u . halt - > msg_len = cpu_to_le32 ( sizeof * u . halt ) ;
2008-06-18 16:40:12 +04:00
( void ) rndis_command ( dev , ( void * ) u . halt , CONTROL_BUFFER_SIZE ) ;
2007-01-16 13:03:01 +03:00
fail_and_release :
usb_set_intfdata ( info - > data , NULL ) ;
usb_driver_release_interface ( driver_of ( intf ) , info - > data ) ;
2007-04-18 04:53:20 +04:00
info - > data = NULL ;
2007-01-16 13:03:01 +03:00
fail :
2005-08-31 20:54:36 +04:00
kfree ( u . buf ) ;
return retval ;
}
2008-01-26 01:51:17 +03:00
EXPORT_SYMBOL_GPL ( generic_rndis_bind ) ;
2005-08-31 20:54:36 +04:00
2008-01-28 00:34:33 +03:00
static int rndis_bind ( struct usbnet * dev , struct usb_interface * intf )
{
return generic_rndis_bind ( dev , intf , FLAG_RNDIS_PHYM_NOT_WIRELESS ) ;
}
2008-01-26 01:51:17 +03:00
void rndis_unbind ( struct usbnet * dev , struct usb_interface * intf )
2005-08-31 20:54:36 +04:00
{
struct rndis_halt * halt ;
/* try to clear any rndis state/activity (no i/o from stack!) */
2008-01-26 01:50:49 +03:00
halt = kzalloc ( CONTROL_BUFFER_SIZE , GFP_KERNEL ) ;
2005-08-31 20:54:36 +04:00
if ( halt ) {
halt - > msg_type = RNDIS_MSG_HALT ;
2009-02-15 09:56:56 +03:00
halt - > msg_len = cpu_to_le32 ( sizeof * halt ) ;
2008-06-18 16:40:12 +04:00
( void ) rndis_command ( dev , ( void * ) halt , CONTROL_BUFFER_SIZE ) ;
2005-08-31 20:54:36 +04:00
kfree ( halt ) ;
}
2008-01-26 01:50:38 +03:00
usbnet_cdc_unbind ( dev , intf ) ;
2005-08-31 20:54:36 +04:00
}
2008-01-26 01:51:17 +03:00
EXPORT_SYMBOL_GPL ( rndis_unbind ) ;
2005-08-31 20:54:36 +04:00
/*
* DATA - - host must not write zlps
*/
2008-01-26 01:51:17 +03:00
int rndis_rx_fixup ( struct usbnet * dev , struct sk_buff * skb )
2005-08-31 20:54:36 +04:00
{
/* peripheral may have batched packets to us... */
while ( likely ( skb - > len ) ) {
struct rndis_data_hdr * hdr = ( void * ) skb - > data ;
struct sk_buff * skb2 ;
u32 msg_len , data_offset , data_len ;
msg_len = le32_to_cpu ( hdr - > msg_len ) ;
data_offset = le32_to_cpu ( hdr - > data_offset ) ;
data_len = le32_to_cpu ( hdr - > data_len ) ;
/* don't choke if we see oob, per-packet data, etc */
if ( unlikely ( hdr - > msg_type ! = RNDIS_MSG_PACKET
| | skb - > len < msg_len
| | ( data_offset + data_len + 8 ) > msg_len ) ) {
2009-06-29 20:52:26 +04:00
dev - > net - > stats . rx_frame_errors + + ;
2005-08-31 20:54:36 +04:00
devdbg ( dev , " bad rndis message %d/%d/%d/%d, len %d " ,
le32_to_cpu ( hdr - > msg_type ) ,
msg_len , data_offset , data_len , skb - > len ) ;
return 0 ;
}
skb_pull ( skb , 8 + data_offset ) ;
/* at most one packet left? */
if ( likely ( ( data_len - skb - > len ) < = sizeof * hdr ) ) {
skb_trim ( skb , data_len ) ;
break ;
}
/* try to return all the packets in the batch */
skb2 = skb_clone ( skb , GFP_ATOMIC ) ;
if ( unlikely ( ! skb2 ) )
break ;
skb_pull ( skb , msg_len - sizeof * hdr ) ;
skb_trim ( skb2 , data_len ) ;
usbnet_skb_return ( dev , skb2 ) ;
}
/* caller will usbnet_skb_return the remaining packet */
return 1 ;
}
2008-01-26 01:51:17 +03:00
EXPORT_SYMBOL_GPL ( rndis_rx_fixup ) ;
2005-08-31 20:54:36 +04:00
2008-01-26 01:51:17 +03:00
struct sk_buff *
2005-10-21 11:21:58 +04:00
rndis_tx_fixup ( struct usbnet * dev , struct sk_buff * skb , gfp_t flags )
2005-08-31 20:54:36 +04:00
{
struct rndis_data_hdr * hdr ;
struct sk_buff * skb2 ;
unsigned len = skb - > len ;
if ( likely ( ! skb_cloned ( skb ) ) ) {
int room = skb_headroom ( skb ) ;
/* enough head room as-is? */
if ( unlikely ( ( sizeof * hdr ) < = room ) )
goto fill ;
/* enough room, but needs to be readjusted? */
room + = skb_tailroom ( skb ) ;
if ( likely ( ( sizeof * hdr ) < = room ) ) {
skb - > data = memmove ( skb - > head + sizeof * hdr ,
skb - > data , len ) ;
2007-04-20 07:29:13 +04:00
skb_set_tail_pointer ( skb , len ) ;
2005-08-31 20:54:36 +04:00
goto fill ;
}
}
/* create a new skb, with the correct size (and tailpad) */
skb2 = skb_copy_expand ( skb , sizeof * hdr , 1 , flags ) ;
dev_kfree_skb_any ( skb ) ;
if ( unlikely ( ! skb2 ) )
return skb2 ;
skb = skb2 ;
/* fill out the RNDIS header. we won't bother trying to batch
* packets ; Linux minimizes wasted bandwidth through tx queues .
*/
fill :
hdr = ( void * ) __skb_push ( skb , sizeof * hdr ) ;
memset ( hdr , 0 , sizeof * hdr ) ;
hdr - > msg_type = RNDIS_MSG_PACKET ;
hdr - > msg_len = cpu_to_le32 ( skb - > len ) ;
2009-02-15 09:56:56 +03:00
hdr - > data_offset = cpu_to_le32 ( sizeof ( * hdr ) - 8 ) ;
2005-08-31 20:54:36 +04:00
hdr - > data_len = cpu_to_le32 ( len ) ;
/* FIXME make the last packet always be short ... */
return skb ;
}
2008-01-26 01:51:17 +03:00
EXPORT_SYMBOL_GPL ( rndis_tx_fixup ) ;
2005-08-31 20:54:36 +04:00
static const struct driver_info rndis_info = {
. description = " RNDIS device " ,
2007-04-18 04:53:20 +04:00
. flags = FLAG_ETHER | FLAG_FRAMING_RN | FLAG_NO_SETINT ,
2008-01-28 00:34:33 +03:00
. bind = rndis_bind ,
2005-08-31 20:54:36 +04:00
. unbind = rndis_unbind ,
. status = rndis_status ,
. rx_fixup = rndis_rx_fixup ,
. tx_fixup = rndis_tx_fixup ,
} ;
/*-------------------------------------------------------------------------*/
static const struct usb_device_id products [ ] = {
{
/* RNDIS is MSFT's un-official variant of CDC ACM */
USB_INTERFACE_INFO ( USB_CLASS_COMM , 2 /* ACM */ , 0x0ff ) ,
. driver_info = ( unsigned long ) & rndis_info ,
2006-12-15 03:01:28 +03:00
} , {
/* "ActiveSync" is an undocumented variant of RNDIS, used in WM5 */
USB_INTERFACE_INFO ( USB_CLASS_MISC , 1 , 1 ) ,
. driver_info = ( unsigned long ) & rndis_info ,
2008-07-23 00:55:58 +04:00
} , {
/* RNDIS for tethering */
USB_INTERFACE_INFO ( USB_CLASS_WIRELESS_CONTROLLER , 1 , 3 ) ,
. driver_info = ( unsigned long ) & rndis_info ,
2005-08-31 20:54:36 +04:00
} ,
{ } , // END
} ;
MODULE_DEVICE_TABLE ( usb , products ) ;
static struct usb_driver rndis_driver = {
. name = " rndis_host " ,
. id_table = products ,
. probe = usbnet_probe ,
. disconnect = usbnet_disconnect ,
. suspend = usbnet_suspend ,
. resume = usbnet_resume ,
} ;
static int __init rndis_init ( void )
{
2006-04-02 22:19:08 +04:00
return usb_register ( & rndis_driver ) ;
2005-08-31 20:54:36 +04:00
}
module_init ( rndis_init ) ;
static void __exit rndis_exit ( void )
{
2006-04-02 22:19:08 +04:00
usb_deregister ( & rndis_driver ) ;
2005-08-31 20:54:36 +04:00
}
module_exit ( rndis_exit ) ;
MODULE_AUTHOR ( " David Brownell " ) ;
MODULE_DESCRIPTION ( " USB Host side RNDIS driver " ) ;
MODULE_LICENSE ( " GPL " ) ;