2005-04-16 15:20:36 -07:00
/*********************************************************************
2007-02-09 23:24:53 +09:00
*
2005-04-16 15:20:36 -07:00
* Filename : irlan_eth . c
2007-02-09 23:24:53 +09:00
* Version :
* Description :
2005-04-16 15:20:36 -07:00
* Status : Experimental .
* Author : Dag Brattli < dagb @ cs . uit . no >
* Created at : Thu Oct 15 08 : 37 : 58 1998
* Modified at : Tue Mar 21 09 : 06 : 41 2000
* Modified by : Dag Brattli < dagb @ cs . uit . no >
* Sources : skeleton . c by Donald Becker < becker @ CESDIS . gsfc . nasa . gov >
* slip . c by Laurence Culhane , < loz @ holmes . demon . co . uk >
* Fred N . van Kempen , < waltje @ uwalt . nl . mugnet . org >
2007-02-09 23:24:53 +09:00
*
2005-04-16 15:20:36 -07:00
* Copyright ( c ) 1998 - 2000 Dag Brattli , All Rights Reserved .
2007-02-09 23:24:53 +09:00
*
* 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
2005-04-16 15:20:36 -07:00
* the License , or ( at your option ) any later version .
2007-02-09 23:24:53 +09:00
*
2007-10-19 23:21:04 +02:00
* Neither Dag Brattli nor University of Tromsø admit liability nor
2007-02-09 23:24:53 +09:00
* provide warranty for any of this software . This material is
2005-04-16 15:20:36 -07:00
* provided " AS-IS " and at no charge .
2007-02-09 23:24:53 +09:00
*
2005-04-16 15:20:36 -07:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/inetdevice.h>
# include <linux/if_arp.h>
# include <linux/module.h>
# include <net/arp.h>
# include <net/irda/irda.h>
# include <net/irda/irmod.h>
# include <net/irda/irlan_common.h>
# include <net/irda/irlan_client.h>
# include <net/irda/irlan_event.h>
# include <net/irda/irlan_eth.h>
static int irlan_eth_open ( struct net_device * dev ) ;
static int irlan_eth_close ( struct net_device * dev ) ;
static int irlan_eth_xmit ( struct sk_buff * skb , struct net_device * dev ) ;
static void irlan_eth_set_multicast_list ( struct net_device * dev ) ;
static struct net_device_stats * irlan_eth_get_stats ( struct net_device * dev ) ;
2009-03-20 19:35:34 +00:00
static const struct net_device_ops irlan_eth_netdev_ops = {
. ndo_open = irlan_eth_open ,
. ndo_stop = irlan_eth_close ,
. ndo_start_xmit = irlan_eth_xmit ,
. ndo_get_stats = irlan_eth_get_stats ,
. ndo_set_multicast_list = irlan_eth_set_multicast_list ,
. ndo_change_mtu = eth_change_mtu ,
. ndo_validate_addr = eth_validate_addr ,
} ;
2005-04-16 15:20:36 -07:00
/*
* Function irlan_eth_setup ( dev )
*
* The network device initialization function .
*
*/
static void irlan_eth_setup ( struct net_device * dev )
{
2009-03-20 19:35:34 +00:00
ether_setup ( dev ) ;
dev - > netdev_ops = & irlan_eth_netdev_ops ;
2005-04-16 15:20:36 -07:00
dev - > destructor = free_netdev ;
2007-02-09 23:24:53 +09:00
/*
2005-04-16 15:20:36 -07:00
* Lets do all queueing in IrTTP instead of this device driver .
* Queueing here as well can introduce some strange latency
* problems , which we will avoid by setting the queue size to 0.
*/
/*
* The bugs in IrTTP and IrLAN that created this latency issue
* have now been fixed , and we can propagate flow control properly
* to the network layer . However , this requires a minimal queue of
* packets for the device .
* Without flow control , the Tx Queue is 14 ( ttp ) + 0 ( dev ) = 14
* With flow control , the Tx Queue is 7 ( ttp ) + 4 ( dev ) = 11
* See irlan_eth_flow_indication ( ) . . .
* Note : this number was randomly selected and would need to
* be adjusted .
* Jean II */
dev - > tx_queue_len = 4 ;
}
/*
* Function alloc_irlandev
*
* Allocate network device and control block
*
*/
struct net_device * alloc_irlandev ( const char * name )
{
return alloc_netdev ( sizeof ( struct irlan_cb ) , name ,
irlan_eth_setup ) ;
}
/*
* Function irlan_eth_open ( dev )
*
* Network device has been opened by user
*
*/
static int irlan_eth_open ( struct net_device * dev )
{
struct irlan_cb * self = netdev_priv ( dev ) ;
2007-02-09 23:24:53 +09:00
2008-03-05 20:47:47 -08:00
IRDA_DEBUG ( 2 , " %s() \n " , __func__ ) ;
2005-04-16 15:20:36 -07:00
/* Ready to play! */
2007-02-09 23:24:53 +09:00
netif_stop_queue ( dev ) ; /* Wait until data link is ready */
2005-04-16 15:20:36 -07:00
/* We are now open, so time to do some work */
self - > disconnect_reason = 0 ;
irlan_client_wakeup ( self , self - > saddr , self - > daddr ) ;
2007-02-09 23:24:53 +09:00
/* Make sure we have a hardware address before we return,
2005-04-16 15:20:36 -07:00
so DHCP clients gets happy */
return wait_event_interruptible ( self - > open_wait ,
! self - > tsap_data - > connected ) ;
}
/*
* Function irlan_eth_close ( dev )
*
* Stop the ether network device , his function will usually be called by
2007-02-09 23:24:53 +09:00
* ifconfig down . We should now disconnect the link , We start the
2005-04-16 15:20:36 -07:00
* close timer , so that the instance will be removed if we are unable
* to discover the remote device after the disconnect .
*/
static int irlan_eth_close ( struct net_device * dev )
{
struct irlan_cb * self = netdev_priv ( dev ) ;
2007-02-09 23:24:53 +09:00
2008-03-05 20:47:47 -08:00
IRDA_DEBUG ( 2 , " %s() \n " , __func__ ) ;
2007-02-09 23:24:53 +09:00
2005-04-16 15:20:36 -07:00
/* Stop device */
netif_stop_queue ( dev ) ;
2007-02-09 23:24:53 +09:00
2005-04-16 15:20:36 -07:00
irlan_close_data_channel ( self ) ;
irlan_close_tsaps ( self ) ;
irlan_do_client_event ( self , IRLAN_LMP_DISCONNECT , NULL ) ;
2007-02-09 23:24:53 +09:00
irlan_do_provider_event ( self , IRLAN_LMP_DISCONNECT , NULL ) ;
2005-04-16 15:20:36 -07:00
/* Remove frames queued on the control channel */
skb_queue_purge ( & self - > client . txq ) ;
self - > client . tx_busy = 0 ;
2007-02-09 23:24:53 +09:00
2005-04-16 15:20:36 -07:00
return 0 ;
}
/*
* Function irlan_eth_tx ( skb )
*
* Transmits ethernet frames over IrDA link .
*
*/
static int irlan_eth_xmit ( struct sk_buff * skb , struct net_device * dev )
{
struct irlan_cb * self = netdev_priv ( dev ) ;
int ret ;
/* skb headroom large enough to contain all IrDA-headers? */
if ( ( skb_headroom ( skb ) < self - > max_header_size ) | | ( skb_shared ( skb ) ) ) {
2007-02-09 23:24:53 +09:00
struct sk_buff * new_skb =
2005-04-16 15:20:36 -07:00
skb_realloc_headroom ( skb , self - > max_header_size ) ;
/* We have to free the original skb anyway */
dev_kfree_skb ( skb ) ;
/* Did the realloc succeed? */
if ( new_skb = = NULL )
return 0 ;
/* Use the new skb instead */
skb = new_skb ;
2007-02-09 23:24:53 +09:00
}
2005-04-16 15:20:36 -07:00
dev - > trans_start = jiffies ;
/* Now queue the packet in the transport layer */
if ( self - > use_udata )
ret = irttp_udata_request ( self - > tsap_data , skb ) ;
else
ret = irttp_data_request ( self - > tsap_data , skb ) ;
if ( ret < 0 ) {
2007-02-09 23:24:53 +09:00
/*
2005-04-16 15:20:36 -07:00
* IrTTPs tx queue is full , so we just have to
* drop the frame ! You might think that we should
* just return - 1 and don ' t deallocate the frame ,
* but that is dangerous since it ' s possible that
* we have replaced the original skb with a new
* one with larger headroom , and that would really
* confuse do_dev_queue_xmit ( ) in dev . c ! I have
2007-02-09 23:24:53 +09:00
* tried : - ) DB
2005-04-16 15:20:36 -07:00
*/
/* irttp_data_request already free the packet */
self - > stats . tx_dropped + + ;
} else {
self - > stats . tx_packets + + ;
2007-02-09 23:24:53 +09:00
self - > stats . tx_bytes + = skb - > len ;
2005-04-16 15:20:36 -07:00
}
2007-02-09 23:24:53 +09:00
2005-04-16 15:20:36 -07:00
return 0 ;
}
/*
* Function irlan_eth_receive ( handle , skb )
*
* This function gets the data that is received on the data channel
*
*/
int irlan_eth_receive ( void * instance , void * sap , struct sk_buff * skb )
{
struct irlan_cb * self = instance ;
if ( skb = = NULL ) {
2007-02-09 23:24:53 +09:00
+ + self - > stats . rx_dropped ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
if ( skb - > len < ETH_HLEN ) {
IRDA_DEBUG ( 0 , " %s() : IrLAN frame too short (%d) \n " ,
2008-03-05 20:47:47 -08:00
__func__ , skb - > len ) ;
2007-02-09 23:24:53 +09:00
+ + self - > stats . rx_dropped ;
2005-04-16 15:20:36 -07:00
dev_kfree_skb ( skb ) ;
return 0 ;
}
2007-02-09 23:24:53 +09:00
/*
* Adopt this frame ! Important to set all these fields since they
2005-04-16 15:20:36 -07:00
* might have been previously set by the low level IrDA network
2007-02-09 23:24:53 +09:00
* device driver
2005-04-16 15:20:36 -07:00
*/
2007-04-25 17:40:23 -07:00
skb - > protocol = eth_type_trans ( skb , self - > dev ) ; /* Remove eth header */
2007-02-09 23:24:53 +09:00
2005-04-16 15:20:36 -07:00
self - > stats . rx_packets + + ;
2007-02-09 23:24:53 +09:00
self - > stats . rx_bytes + = skb - > len ;
2005-04-16 15:20:36 -07:00
netif_rx ( skb ) ; /* Eat it! */
2007-02-09 23:24:53 +09:00
2005-04-16 15:20:36 -07:00
return 0 ;
}
/*
* Function irlan_eth_flow ( status )
*
2007-02-09 23:24:53 +09:00
* Do flow control between IP / Ethernet and IrLAN / IrTTP . This is done by
2005-04-16 15:20:36 -07:00
* controlling the queue stop / start .
*
* The IrDA link layer has the advantage to have flow control , and
* IrTTP now properly handles that . Flow controlling the higher layers
* prevent us to drop Tx packets in here ( up to 15 % for a TCP socket ,
* more for UDP socket ) .
* Also , this allow us to reduce the overall transmit queue , which means
* less latency in case of mixed traffic .
* Jean II
*/
void irlan_eth_flow_indication ( void * instance , void * sap , LOCAL_FLOW flow )
{
struct irlan_cb * self ;
struct net_device * dev ;
self = ( struct irlan_cb * ) instance ;
IRDA_ASSERT ( self ! = NULL , return ; ) ;
IRDA_ASSERT ( self - > magic = = IRLAN_MAGIC , return ; ) ;
2007-02-09 23:24:53 +09:00
2005-04-16 15:20:36 -07:00
dev = self - > dev ;
IRDA_ASSERT ( dev ! = NULL , return ; ) ;
2007-02-09 23:24:53 +09:00
2008-03-05 20:47:47 -08:00
IRDA_DEBUG ( 0 , " %s() : flow %s ; running %d \n " , __func__ ,
2005-04-16 15:20:36 -07:00
flow = = FLOW_STOP ? " FLOW_STOP " : " FLOW_START " ,
netif_running ( dev ) ) ;
switch ( flow ) {
case FLOW_STOP :
/* IrTTP is full, stop higher layers */
netif_stop_queue ( dev ) ;
break ;
case FLOW_START :
default :
/* Tell upper layers that its time to transmit frames again */
/* Schedule network layer */
netif_wake_queue ( dev ) ;
break ;
}
}
/*
* Function set_multicast_list ( dev )
*
* Configure the filtering of the device
*
*/
# define HW_MAX_ADDRS 4 /* Must query to get it! */
2007-02-09 23:24:53 +09:00
static void irlan_eth_set_multicast_list ( struct net_device * dev )
2005-04-16 15:20:36 -07:00
{
2007-02-09 23:24:53 +09:00
struct irlan_cb * self = netdev_priv ( dev ) ;
2005-04-16 15:20:36 -07:00
2008-03-05 20:47:47 -08:00
IRDA_DEBUG ( 2 , " %s() \n " , __func__ ) ;
2005-04-16 15:20:36 -07:00
/* Check if data channel has been connected yet */
if ( self - > client . state ! = IRLAN_DATA ) {
2008-03-05 20:47:47 -08:00
IRDA_DEBUG ( 1 , " %s(), delaying! \n " , __func__ ) ;
2005-04-16 15:20:36 -07:00
return ;
}
if ( dev - > flags & IFF_PROMISC ) {
/* Enable promiscuous mode */
2007-12-20 14:00:51 -08:00
IRDA_WARNING ( " Promiscuous mode not implemented by IrLAN! \n " ) ;
2007-02-09 23:24:53 +09:00
}
2005-04-16 15:20:36 -07:00
else if ( ( dev - > flags & IFF_ALLMULTI ) | | dev - > mc_count > HW_MAX_ADDRS ) {
/* Disable promiscuous mode, use normal mode. */
2008-03-05 20:47:47 -08:00
IRDA_DEBUG ( 4 , " %s(), Setting multicast filter \n " , __func__ ) ;
2005-04-16 15:20:36 -07:00
/* hardware_set_filter(NULL); */
irlan_set_multicast_filter ( self , TRUE ) ;
}
else if ( dev - > mc_count ) {
2008-03-05 20:47:47 -08:00
IRDA_DEBUG ( 4 , " %s(), Setting multicast filter \n " , __func__ ) ;
2005-04-16 15:20:36 -07:00
/* Walk the address list, and load the filter */
/* hardware_set_filter(dev->mc_list); */
irlan_set_multicast_filter ( self , TRUE ) ;
}
else {
2008-03-05 20:47:47 -08:00
IRDA_DEBUG ( 4 , " %s(), Clearing multicast filter \n " , __func__ ) ;
2005-04-16 15:20:36 -07:00
irlan_set_multicast_filter ( self , FALSE ) ;
}
if ( dev - > flags & IFF_BROADCAST )
irlan_set_broadcast_filter ( self , TRUE ) ;
else
irlan_set_broadcast_filter ( self , FALSE ) ;
}
/*
* Function irlan_get_stats ( dev )
*
* Get the current statistics for this device
*
*/
2007-02-09 23:24:53 +09:00
static struct net_device_stats * irlan_eth_get_stats ( struct net_device * dev )
2005-04-16 15:20:36 -07:00
{
struct irlan_cb * self = netdev_priv ( dev ) ;
return & self - > stats ;
}