2005-04-17 02:20:36 +04:00
/* sbni.c: Granch SBNI12 leased line adapters driver for linux
*
* Written 2001 by Denis I . Timofeev ( timofeev @ granch . ru )
*
* Previous versions were written by Yaroslav Polyakov ,
* Alexey Zverev and Max Khon .
*
* Driver supports SBNI12 - 02 , - 04 , - 05 , - 10 , - 11 cards , single and
* double - channel , PCI and ISA modifications .
* More info and useful utilities to work with SBNI12 cards you can find
* at http : //www.granch.com (English) or http://www.granch.ru (Russian)
*
* This software may be used and distributed according to the terms
* of the GNU General Public License .
*
*
* 5.0 .1 Jun 22 2001
* - Fixed bug in probe
* 5.0 .0 Jun 06 2001
* - Driver was completely redesigned by Denis I . Timofeev ,
* - now PCI / Dual , ISA / Dual ( with single interrupt line ) models are
* - supported
* 3.3 .0 Thu Feb 24 21 : 30 : 28 NOVT 2000
* - PCI cards support
* 3.2 .0 Mon Dec 13 22 : 26 : 53 NOVT 1999
* - Completely rebuilt all the packet storage system
* - to work in Ethernet - like style .
* 3.1 .1 just fixed some bugs ( 5 aug 1999 )
* 3.1 .0 added balancing feature ( 26 apr 1999 )
* 3.0 .1 just fixed some bugs ( 14 apr 1999 ) .
* 3.0 .0 Initial Revision , Yaroslav Polyakov ( 24 Feb 1999 )
* - added pre - calculation for CRC , fixed bug with " len-2 " frames ,
* - removed outbound fragmentation ( MTU = 1000 ) , written CRC - calculation
* - on asm , added work with hard_headers and now we have our own cache
* - for them , optionally supported word - interchange on some chipsets ,
*
* Known problem : this driver wasn ' t tested on multiprocessor machine .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/ptrace.h>
# include <linux/fcntl.h>
# include <linux/ioport.h>
# include <linux/interrupt.h>
# include <linux/slab.h>
# include <linux/string.h>
# include <linux/errno.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/pci.h>
# include <linux/skbuff.h>
# include <linux/timer.h>
# include <linux/init.h>
# include <linux/delay.h>
# include <net/arp.h>
# include <asm/io.h>
# include <asm/types.h>
# include <asm/byteorder.h>
# include <asm/irq.h>
# include <asm/uaccess.h>
# include "sbni.h"
/* device private data */
struct net_local {
struct net_device_stats stats ;
struct timer_list watchdog ;
spinlock_t lock ;
struct sk_buff * rx_buf_p ; /* receive buffer ptr */
struct sk_buff * tx_buf_p ; /* transmit buffer ptr */
unsigned int framelen ; /* current frame length */
unsigned int maxframe ; /* maximum valid frame length */
unsigned int state ;
unsigned int inppos , outpos ; /* positions in rx/tx buffers */
/* transmitting frame number - from frames qty to 1 */
unsigned int tx_frameno ;
/* expected number of next receiving frame */
unsigned int wait_frameno ;
/* count of failed attempts to frame send - 32 attempts do before
error - while receiver tunes on opposite side of wire */
unsigned int trans_errors ;
/* idle time; send pong when limit exceeded */
unsigned int timer_ticks ;
/* fields used for receive level autoselection */
int delta_rxl ;
unsigned int cur_rxl_index , timeout_rxl ;
unsigned long cur_rxl_rcvd , prev_rxl_rcvd ;
struct sbni_csr1 csr1 ; /* current value of CSR1 */
struct sbni_in_stats in_stats ; /* internal statistics */
struct net_device * second ; /* for ISA/dual cards */
# ifdef CONFIG_SBNI_MULTILINE
struct net_device * master ;
struct net_device * link ;
# endif
} ;
static int sbni_card_probe ( unsigned long ) ;
static int sbni_pci_probe ( struct net_device * ) ;
static struct net_device * sbni_probe1 ( struct net_device * , unsigned long , int ) ;
static int sbni_open ( struct net_device * ) ;
static int sbni_close ( struct net_device * ) ;
static int sbni_start_xmit ( struct sk_buff * , struct net_device * ) ;
static int sbni_ioctl ( struct net_device * , struct ifreq * , int ) ;
static struct net_device_stats * sbni_get_stats ( struct net_device * ) ;
static void set_multicast_list ( struct net_device * ) ;
static irqreturn_t sbni_interrupt ( int , void * , struct pt_regs * ) ;
static void handle_channel ( struct net_device * ) ;
static int recv_frame ( struct net_device * ) ;
static void send_frame ( struct net_device * ) ;
static int upload_data ( struct net_device * ,
unsigned , unsigned , unsigned , u32 ) ;
static void download_data ( struct net_device * , u32 * ) ;
static void sbni_watchdog ( unsigned long ) ;
static void interpret_ack ( struct net_device * , unsigned ) ;
static int append_frame_to_pkt ( struct net_device * , unsigned , u32 ) ;
static void indicate_pkt ( struct net_device * ) ;
static void card_start ( struct net_device * ) ;
static void prepare_to_send ( struct sk_buff * , struct net_device * ) ;
static void drop_xmit_queue ( struct net_device * ) ;
static void send_frame_header ( struct net_device * , u32 * ) ;
static int skip_tail ( unsigned int , unsigned int , u32 ) ;
static int check_fhdr ( u32 , u32 * , u32 * , u32 * , u32 * , u32 * ) ;
static void change_level ( struct net_device * ) ;
static void timeout_change_level ( struct net_device * ) ;
static u32 calc_crc32 ( u32 , u8 * , u32 ) ;
static struct sk_buff * get_rx_buf ( struct net_device * ) ;
static int sbni_init ( struct net_device * ) ;
# ifdef CONFIG_SBNI_MULTILINE
static int enslave ( struct net_device * , struct net_device * ) ;
static int emancipate ( struct net_device * ) ;
# endif
# ifdef __i386__
# define ASM_CRC 1
# endif
static const char version [ ] =
" Granch SBNI12 driver ver 5.0.1 Jun 22 2001 Denis I.Timofeev. \n " ;
static int skip_pci_probe __initdata = 0 ;
static int scandone __initdata = 0 ;
static int num __initdata = 0 ;
static unsigned char rxl_tab [ ] ;
static u32 crc32tab [ ] ;
/* A list of all installed devices, for removing the driver module. */
static struct net_device * sbni_cards [ SBNI_MAX_NUM_CARDS ] ;
/* Lists of device's parameters */
static u32 io [ SBNI_MAX_NUM_CARDS ] __initdata =
{ [ 0 . . . SBNI_MAX_NUM_CARDS - 1 ] = - 1 } ;
static u32 irq [ SBNI_MAX_NUM_CARDS ] __initdata ;
static u32 baud [ SBNI_MAX_NUM_CARDS ] __initdata ;
static u32 rxl [ SBNI_MAX_NUM_CARDS ] __initdata =
{ [ 0 . . . SBNI_MAX_NUM_CARDS - 1 ] = - 1 } ;
static u32 mac [ SBNI_MAX_NUM_CARDS ] __initdata ;
# ifndef MODULE
typedef u32 iarr [ ] ;
static iarr __initdata * dest [ 5 ] = { & io , & irq , & baud , & rxl , & mac } ;
# endif
/* A zero-terminated list of I/O addresses to be probed on ISA bus */
static unsigned int netcard_portlist [ ] __initdata = {
0x210 , 0x214 , 0x220 , 0x224 , 0x230 , 0x234 , 0x240 , 0x244 , 0x250 , 0x254 ,
0x260 , 0x264 , 0x270 , 0x274 , 0x280 , 0x284 , 0x290 , 0x294 , 0x2a0 , 0x2a4 ,
0x2b0 , 0x2b4 , 0x2c0 , 0x2c4 , 0x2d0 , 0x2d4 , 0x2e0 , 0x2e4 , 0x2f0 , 0x2f4 ,
0 } ;
/*
* Look for SBNI card which addr stored in dev - > base_addr , if nonzero .
* Otherwise , look through PCI bus . If none PCI - card was found , scan ISA .
*/
static inline int __init
sbni_isa_probe ( struct net_device * dev )
{
if ( dev - > base_addr > 0x1ff
& & request_region ( dev - > base_addr , SBNI_IO_EXTENT , dev - > name )
& & sbni_probe1 ( dev , dev - > base_addr , dev - > irq ) )
return 0 ;
else {
printk ( KERN_ERR " sbni: base address 0x%lx is busy, or adapter "
" is malfunctional! \n " , dev - > base_addr ) ;
return - ENODEV ;
}
}
static void __init sbni_devsetup ( struct net_device * dev )
{
ether_setup ( dev ) ;
dev - > open = & sbni_open ;
dev - > stop = & sbni_close ;
dev - > hard_start_xmit = & sbni_start_xmit ;
dev - > get_stats = & sbni_get_stats ;
dev - > set_multicast_list = & set_multicast_list ;
dev - > do_ioctl = & sbni_ioctl ;
SET_MODULE_OWNER ( dev ) ;
}
int __init sbni_probe ( int unit )
{
struct net_device * dev ;
static unsigned version_printed __initdata = 0 ;
int err ;
dev = alloc_netdev ( sizeof ( struct net_local ) , " sbni " , sbni_devsetup ) ;
if ( ! dev )
return - ENOMEM ;
sprintf ( dev - > name , " sbni%d " , unit ) ;
netdev_boot_setup_check ( dev ) ;
err = sbni_init ( dev ) ;
if ( err ) {
free_netdev ( dev ) ;
return err ;
}
err = register_netdev ( dev ) ;
if ( err ) {
release_region ( dev - > base_addr , SBNI_IO_EXTENT ) ;
free_netdev ( dev ) ;
return err ;
}
if ( version_printed + + = = 0 )
printk ( KERN_INFO " %s " , version ) ;
return 0 ;
}
static int __init sbni_init ( struct net_device * dev )
{
int i ;
if ( dev - > base_addr )
return sbni_isa_probe ( dev ) ;
/* otherwise we have to perform search our adapter */
if ( io [ num ] ! = - 1 )
dev - > base_addr = io [ num ] ,
dev - > irq = irq [ num ] ;
else if ( scandone | | io [ 0 ] ! = - 1 )
return - ENODEV ;
/* if io[ num ] contains non-zero address, then that is on ISA bus */
if ( dev - > base_addr )
return sbni_isa_probe ( dev ) ;
/* ...otherwise - scan PCI first */
if ( ! skip_pci_probe & & ! sbni_pci_probe ( dev ) )
return 0 ;
if ( io [ num ] = = - 1 ) {
/* Auto-scan will be stopped when first ISA card were found */
scandone = 1 ;
if ( num > 0 )
return - ENODEV ;
}
for ( i = 0 ; netcard_portlist [ i ] ; + + i ) {
int ioaddr = netcard_portlist [ i ] ;
if ( request_region ( ioaddr , SBNI_IO_EXTENT , dev - > name )
& & sbni_probe1 ( dev , ioaddr , 0 ) )
return 0 ;
}
return - ENODEV ;
}
int __init
sbni_pci_probe ( struct net_device * dev )
{
struct pci_dev * pdev = NULL ;
while ( ( pdev = pci_get_class ( PCI_CLASS_NETWORK_OTHER < < 8 , pdev ) )
! = NULL ) {
int pci_irq_line ;
unsigned long pci_ioaddr ;
u16 subsys ;
if ( pdev - > vendor ! = SBNI_PCI_VENDOR
& & pdev - > device ! = SBNI_PCI_DEVICE )
continue ;
pci_ioaddr = pci_resource_start ( pdev , 0 ) ;
pci_irq_line = pdev - > irq ;
/* Avoid already found cards from previous calls */
if ( ! request_region ( pci_ioaddr , SBNI_IO_EXTENT , dev - > name ) ) {
pci_read_config_word ( pdev , PCI_SUBSYSTEM_ID , & subsys ) ;
if ( subsys ! = 2 )
continue ;
/* Dual adapter is present */
if ( ! request_region ( pci_ioaddr + = 4 , SBNI_IO_EXTENT ,
dev - > name ) )
continue ;
}
if ( pci_irq_line < = 0 | | pci_irq_line > = NR_IRQS )
printk ( KERN_WARNING " WARNING: The PCI BIOS assigned "
" this PCI card to IRQ %d, which is unlikely "
" to work!. \n "
KERN_WARNING " You should use the PCI BIOS "
" setup to assign a valid IRQ line. \n " ,
pci_irq_line ) ;
/* avoiding re-enable dual adapters */
if ( ( pci_ioaddr & 7 ) = = 0 & & pci_enable_device ( pdev ) ) {
release_region ( pci_ioaddr , SBNI_IO_EXTENT ) ;
pci_dev_put ( pdev ) ;
return - EIO ;
}
if ( sbni_probe1 ( dev , pci_ioaddr , pci_irq_line ) ) {
SET_NETDEV_DEV ( dev , & pdev - > dev ) ;
/* not the best thing to do, but this is all messed up
for hotplug systems anyway . . . */
pci_dev_put ( pdev ) ;
return 0 ;
}
}
return - ENODEV ;
}
static struct net_device * __init
sbni_probe1 ( struct net_device * dev , unsigned long ioaddr , int irq )
{
struct net_local * nl ;
if ( sbni_card_probe ( ioaddr ) ) {
release_region ( ioaddr , SBNI_IO_EXTENT ) ;
return NULL ;
}
outb ( 0 , ioaddr + CSR0 ) ;
if ( irq < 2 ) {
unsigned long irq_mask ;
irq_mask = probe_irq_on ( ) ;
outb ( EN_INT | TR_REQ , ioaddr + CSR0 ) ;
outb ( PR_RES , ioaddr + CSR1 ) ;
mdelay ( 50 ) ;
irq = probe_irq_off ( irq_mask ) ;
outb ( 0 , ioaddr + CSR0 ) ;
if ( ! irq ) {
printk ( KERN_ERR " %s: can't detect device irq! \n " ,
dev - > name ) ;
release_region ( ioaddr , SBNI_IO_EXTENT ) ;
return NULL ;
}
} else if ( irq = = 2 )
irq = 9 ;
dev - > irq = irq ;
dev - > base_addr = ioaddr ;
/* Allocate dev->priv and fill in sbni-specific dev fields. */
nl = dev - > priv ;
if ( ! nl ) {
printk ( KERN_ERR " %s: unable to get memory! \n " , dev - > name ) ;
release_region ( ioaddr , SBNI_IO_EXTENT ) ;
return NULL ;
}
dev - > priv = nl ;
memset ( nl , 0 , sizeof ( struct net_local ) ) ;
spin_lock_init ( & nl - > lock ) ;
/* store MAC address (generate if that isn't known) */
* ( u16 * ) dev - > dev_addr = htons ( 0x00ff ) ;
* ( u32 * ) ( dev - > dev_addr + 2 ) = htonl ( 0x01000000 |
( ( mac [ num ] ? mac [ num ] : ( u32 ) ( ( long ) dev - > priv ) ) & 0x00ffffff ) ) ;
/* store link settings (speed, receive level ) */
nl - > maxframe = DEFAULT_FRAME_LEN ;
nl - > csr1 . rate = baud [ num ] ;
if ( ( nl - > cur_rxl_index = rxl [ num ] ) = = - 1 )
/* autotune rxl */
nl - > cur_rxl_index = DEF_RXL ,
nl - > delta_rxl = DEF_RXL_DELTA ;
else
nl - > delta_rxl = 0 ;
nl - > csr1 . rxl = rxl_tab [ nl - > cur_rxl_index ] ;
if ( inb ( ioaddr + CSR0 ) & 0x01 )
nl - > state | = FL_SLOW_MODE ;
printk ( KERN_NOTICE " %s: ioaddr %#lx, irq %d, "
" MAC: 00:ff:01:%02x:%02x:%02x \n " ,
dev - > name , dev - > base_addr , dev - > irq ,
( ( u8 * ) dev - > dev_addr ) [ 3 ] ,
( ( u8 * ) dev - > dev_addr ) [ 4 ] ,
( ( u8 * ) dev - > dev_addr ) [ 5 ] ) ;
printk ( KERN_NOTICE " %s: speed %d, receive level " , dev - > name ,
( ( nl - > state & FL_SLOW_MODE ) ? 500000 : 2000000 )
/ ( 1 < < nl - > csr1 . rate ) ) ;
if ( nl - > delta_rxl = = 0 )
printk ( " 0x%x (fixed) \n " , nl - > cur_rxl_index ) ;
else
printk ( " (auto) \n " ) ;
# ifdef CONFIG_SBNI_MULTILINE
nl - > master = dev ;
nl - > link = NULL ;
# endif
sbni_cards [ num + + ] = dev ;
return dev ;
}
/* -------------------------------------------------------------------------- */
# ifdef CONFIG_SBNI_MULTILINE
static int
sbni_start_xmit ( struct sk_buff * skb , struct net_device * dev )
{
struct net_device * p ;
netif_stop_queue ( dev ) ;
/* Looking for idle device in the list */
for ( p = dev ; p ; ) {
struct net_local * nl = ( struct net_local * ) p - > priv ;
spin_lock ( & nl - > lock ) ;
if ( nl - > tx_buf_p | | ( nl - > state & FL_LINE_DOWN ) ) {
p = nl - > link ;
spin_unlock ( & nl - > lock ) ;
} else {
/* Idle dev is found */
prepare_to_send ( skb , p ) ;
spin_unlock ( & nl - > lock ) ;
netif_start_queue ( dev ) ;
return 0 ;
}
}
return 1 ;
}
# else /* CONFIG_SBNI_MULTILINE */
static int
sbni_start_xmit ( struct sk_buff * skb , struct net_device * dev )
{
struct net_local * nl = ( struct net_local * ) dev - > priv ;
netif_stop_queue ( dev ) ;
spin_lock ( & nl - > lock ) ;
prepare_to_send ( skb , dev ) ;
spin_unlock ( & nl - > lock ) ;
return 0 ;
}
# endif /* CONFIG_SBNI_MULTILINE */
/* -------------------------------------------------------------------------- */
/* interrupt handler */
/*
* SBNI12D - 10 , - 11 / ISA boards within " common interrupt " mode could not
* be looked as two independent single - channel devices . Every channel seems
* as Ethernet interface but interrupt handler must be common . Really , first
* channel ( " master " ) driver only registers the handler . In its struct net_local
* it has got pointer to " slave " channel ' s struct net_local and handles that ' s
* interrupts too .
* dev of successfully attached ISA SBNI boards is linked to list .
* While next board driver is initialized , it scans this list . If one
* has found dev with same irq and ioaddr different by 4 then it assumes
* this board to be " master " .
*/
static irqreturn_t
sbni_interrupt ( int irq , void * dev_id , struct pt_regs * regs )
{
struct net_device * dev = ( struct net_device * ) dev_id ;
struct net_local * nl = ( struct net_local * ) dev - > priv ;
int repeat ;
spin_lock ( & nl - > lock ) ;
if ( nl - > second )
spin_lock ( & ( ( struct net_local * ) nl - > second - > priv ) - > lock ) ;
do {
repeat = 0 ;
if ( inb ( dev - > base_addr + CSR0 ) & ( RC_RDY | TR_RDY ) )
handle_channel ( dev ) ,
repeat = 1 ;
if ( nl - > second & & /* second channel present */
( inb ( nl - > second - > base_addr + CSR0 ) & ( RC_RDY | TR_RDY ) ) )
handle_channel ( nl - > second ) ,
repeat = 1 ;
} while ( repeat ) ;
if ( nl - > second )
spin_unlock ( & ( ( struct net_local * ) nl - > second - > priv ) - > lock ) ;
spin_unlock ( & nl - > lock ) ;
return IRQ_HANDLED ;
}
static void
handle_channel ( struct net_device * dev )
{
struct net_local * nl = ( struct net_local * ) dev - > priv ;
unsigned long ioaddr = dev - > base_addr ;
int req_ans ;
unsigned char csr0 ;
# ifdef CONFIG_SBNI_MULTILINE
/* Lock the master device because we going to change its local data */
if ( nl - > state & FL_SLAVE )
spin_lock ( & ( ( struct net_local * ) nl - > master - > priv ) - > lock ) ;
# endif
outb ( ( inb ( ioaddr + CSR0 ) & ~ EN_INT ) | TR_REQ , ioaddr + CSR0 ) ;
nl - > timer_ticks = CHANGE_LEVEL_START_TICKS ;
for ( ; ; ) {
csr0 = inb ( ioaddr + CSR0 ) ;
if ( ( csr0 & ( RC_RDY | TR_RDY ) ) = = 0 )
break ;
req_ans = ! ( nl - > state & FL_PREV_OK ) ;
if ( csr0 & RC_RDY )
req_ans = recv_frame ( dev ) ;
/*
* TR_RDY always equals 1 here because we have owned the marker ,
* and we set TR_REQ when disabled interrupts
*/
csr0 = inb ( ioaddr + CSR0 ) ;
if ( ! ( csr0 & TR_RDY ) | | ( csr0 & RC_RDY ) )
printk ( KERN_ERR " %s: internal error! \n " , dev - > name ) ;
/* if state & FL_NEED_RESEND != 0 then tx_frameno != 0 */
if ( req_ans | | nl - > tx_frameno ! = 0 )
send_frame ( dev ) ;
else
/* send marker without any data */
outb ( inb ( ioaddr + CSR0 ) & ~ TR_REQ , ioaddr + CSR0 ) ;
}
outb ( inb ( ioaddr + CSR0 ) | EN_INT , ioaddr + CSR0 ) ;
# ifdef CONFIG_SBNI_MULTILINE
if ( nl - > state & FL_SLAVE )
spin_unlock ( & ( ( struct net_local * ) nl - > master - > priv ) - > lock ) ;
# endif
}
/*
* Routine returns 1 if it need to acknoweledge received frame .
* Empty frame received without errors won ' t be acknoweledged .
*/
static int
recv_frame ( struct net_device * dev )
{
struct net_local * nl = ( struct net_local * ) dev - > priv ;
unsigned long ioaddr = dev - > base_addr ;
u32 crc = CRC32_INITIAL ;
unsigned framelen , frameno , ack ;
unsigned is_first , frame_ok ;
if ( check_fhdr ( ioaddr , & framelen , & frameno , & ack , & is_first , & crc ) ) {
frame_ok = framelen > 4
? upload_data ( dev , framelen , frameno , is_first , crc )
: skip_tail ( ioaddr , framelen , crc ) ;
if ( frame_ok )
interpret_ack ( dev , ack ) ;
} else
frame_ok = 0 ;
outb ( inb ( ioaddr + CSR0 ) ^ CT_ZER , ioaddr + CSR0 ) ;
if ( frame_ok ) {
nl - > state | = FL_PREV_OK ;
if ( framelen > 4 )
nl - > in_stats . all_rx_number + + ;
} else
nl - > state & = ~ FL_PREV_OK ,
change_level ( dev ) ,
nl - > in_stats . all_rx_number + + ,
nl - > in_stats . bad_rx_number + + ;
return ! frame_ok | | framelen > 4 ;
}
static void
send_frame ( struct net_device * dev )
{
struct net_local * nl = ( struct net_local * ) dev - > priv ;
u32 crc = CRC32_INITIAL ;
if ( nl - > state & FL_NEED_RESEND ) {
/* if frame was sended but not ACK'ed - resend it */
if ( nl - > trans_errors ) {
- - nl - > trans_errors ;
if ( nl - > framelen ! = 0 )
nl - > in_stats . resend_tx_number + + ;
} else {
/* cannot xmit with many attempts */
# ifdef CONFIG_SBNI_MULTILINE
if ( ( nl - > state & FL_SLAVE ) | | nl - > link )
# endif
nl - > state | = FL_LINE_DOWN ;
drop_xmit_queue ( dev ) ;
goto do_send ;
}
} else
nl - > trans_errors = TR_ERROR_COUNT ;
send_frame_header ( dev , & crc ) ;
nl - > state | = FL_NEED_RESEND ;
/*
* FL_NEED_RESEND will be cleared after ACK , but if empty
* frame sended then in prepare_to_send next frame
*/
if ( nl - > framelen ) {
download_data ( dev , & crc ) ;
nl - > in_stats . all_tx_number + + ;
nl - > state | = FL_WAIT_ACK ;
}
outsb ( dev - > base_addr + DAT , ( u8 * ) & crc , sizeof crc ) ;
do_send :
outb ( inb ( dev - > base_addr + CSR0 ) & ~ TR_REQ , dev - > base_addr + CSR0 ) ;
if ( nl - > tx_frameno )
/* next frame exists - we request card to send it */
outb ( inb ( dev - > base_addr + CSR0 ) | TR_REQ ,
dev - > base_addr + CSR0 ) ;
}
/*
* Write the frame data into adapter ' s buffer memory , and calculate CRC .
* Do padding if necessary .
*/
static void
download_data ( struct net_device * dev , u32 * crc_p )
{
struct net_local * nl = ( struct net_local * ) dev - > priv ;
struct sk_buff * skb = nl - > tx_buf_p ;
unsigned len = min_t ( unsigned int , skb - > len - nl - > outpos , nl - > framelen ) ;
outsb ( dev - > base_addr + DAT , skb - > data + nl - > outpos , len ) ;
* crc_p = calc_crc32 ( * crc_p , skb - > data + nl - > outpos , len ) ;
/* if packet too short we should write some more bytes to pad */
for ( len = nl - > framelen - len ; len - - ; )
outb ( 0 , dev - > base_addr + DAT ) ,
* crc_p = CRC32 ( 0 , * crc_p ) ;
}
static int
upload_data ( struct net_device * dev , unsigned framelen , unsigned frameno ,
unsigned is_first , u32 crc )
{
struct net_local * nl = ( struct net_local * ) dev - > priv ;
int frame_ok ;
if ( is_first )
nl - > wait_frameno = frameno ,
nl - > inppos = 0 ;
if ( nl - > wait_frameno = = frameno ) {
if ( nl - > inppos + framelen < = ETHER_MAX_LEN )
frame_ok = append_frame_to_pkt ( dev , framelen , crc ) ;
/*
* if CRC is right but framelen incorrect then transmitter
* error was occurred . . . drop entire packet
*/
else if ( ( frame_ok = skip_tail ( dev - > base_addr , framelen , crc ) )
! = 0 )
nl - > wait_frameno = 0 ,
nl - > inppos = 0 ,
# ifdef CONFIG_SBNI_MULTILINE
( ( struct net_local * ) nl - > master - > priv )
- > stats . rx_errors + + ,
( ( struct net_local * ) nl - > master - > priv )
- > stats . rx_missed_errors + + ;
# else
nl - > stats . rx_errors + + ,
nl - > stats . rx_missed_errors + + ;
# endif
/* now skip all frames until is_first != 0 */
} else
frame_ok = skip_tail ( dev - > base_addr , framelen , crc ) ;
if ( is_first & & ! frame_ok )
/*
* Frame has been broken , but we had already stored
* is_first . . . Drop entire packet .
*/
nl - > wait_frameno = 0 ,
# ifdef CONFIG_SBNI_MULTILINE
( ( struct net_local * ) nl - > master - > priv ) - > stats . rx_errors + + ,
( ( struct net_local * ) nl - > master - > priv ) - > stats . rx_crc_errors + + ;
# else
nl - > stats . rx_errors + + ,
nl - > stats . rx_crc_errors + + ;
# endif
return frame_ok ;
}
static __inline void
send_complete ( struct net_local * nl )
{
# ifdef CONFIG_SBNI_MULTILINE
( ( struct net_local * ) nl - > master - > priv ) - > stats . tx_packets + + ;
( ( struct net_local * ) nl - > master - > priv ) - > stats . tx_bytes
+ = nl - > tx_buf_p - > len ;
# else
nl - > stats . tx_packets + + ;
nl - > stats . tx_bytes + = nl - > tx_buf_p - > len ;
# endif
dev_kfree_skb_irq ( nl - > tx_buf_p ) ;
nl - > tx_buf_p = NULL ;
nl - > outpos = 0 ;
nl - > state & = ~ ( FL_WAIT_ACK | FL_NEED_RESEND ) ;
nl - > framelen = 0 ;
}
static void
interpret_ack ( struct net_device * dev , unsigned ack )
{
struct net_local * nl = ( struct net_local * ) dev - > priv ;
if ( ack = = FRAME_SENT_OK ) {
nl - > state & = ~ FL_NEED_RESEND ;
if ( nl - > state & FL_WAIT_ACK ) {
nl - > outpos + = nl - > framelen ;
if ( - - nl - > tx_frameno )
nl - > framelen = min_t ( unsigned int ,
nl - > maxframe ,
nl - > tx_buf_p - > len - nl - > outpos ) ;
else
send_complete ( nl ) ,
# ifdef CONFIG_SBNI_MULTILINE
netif_wake_queue ( nl - > master ) ;
# else
netif_wake_queue ( dev ) ;
# endif
}
}
nl - > state & = ~ FL_WAIT_ACK ;
}
/*
* Glue received frame with previous fragments of packet .
* Indicate packet when last frame would be accepted .
*/
static int
append_frame_to_pkt ( struct net_device * dev , unsigned framelen , u32 crc )
{
struct net_local * nl = ( struct net_local * ) dev - > priv ;
u8 * p ;
if ( nl - > inppos + framelen > ETHER_MAX_LEN )
return 0 ;
if ( ! nl - > rx_buf_p & & ! ( nl - > rx_buf_p = get_rx_buf ( dev ) ) )
return 0 ;
p = nl - > rx_buf_p - > data + nl - > inppos ;
insb ( dev - > base_addr + DAT , p , framelen ) ;
if ( calc_crc32 ( crc , p , framelen ) ! = CRC32_REMAINDER )
return 0 ;
nl - > inppos + = framelen - 4 ;
if ( - - nl - > wait_frameno = = 0 ) /* last frame received */
indicate_pkt ( dev ) ;
return 1 ;
}
/*
* Prepare to start output on adapter .
* Transmitter will be actually activated when marker is accepted .
*/
static void
prepare_to_send ( struct sk_buff * skb , struct net_device * dev )
{
struct net_local * nl = ( struct net_local * ) dev - > priv ;
unsigned int len ;
/* nl->tx_buf_p == NULL here! */
if ( nl - > tx_buf_p )
printk ( KERN_ERR " %s: memory leak! \n " , dev - > name ) ;
nl - > outpos = 0 ;
nl - > state & = ~ ( FL_WAIT_ACK | FL_NEED_RESEND ) ;
len = skb - > len ;
if ( len < SBNI_MIN_LEN )
len = SBNI_MIN_LEN ;
nl - > tx_buf_p = skb ;
nl - > tx_frameno = ( len + nl - > maxframe - 1 ) / nl - > maxframe ;
nl - > framelen = len < nl - > maxframe ? len : nl - > maxframe ;
outb ( inb ( dev - > base_addr + CSR0 ) | TR_REQ , dev - > base_addr + CSR0 ) ;
# ifdef CONFIG_SBNI_MULTILINE
nl - > master - > trans_start = jiffies ;
# else
dev - > trans_start = jiffies ;
# endif
}
static void
drop_xmit_queue ( struct net_device * dev )
{
struct net_local * nl = ( struct net_local * ) dev - > priv ;
if ( nl - > tx_buf_p )
dev_kfree_skb_any ( nl - > tx_buf_p ) ,
nl - > tx_buf_p = NULL ,
# ifdef CONFIG_SBNI_MULTILINE
( ( struct net_local * ) nl - > master - > priv )
- > stats . tx_errors + + ,
( ( struct net_local * ) nl - > master - > priv )
- > stats . tx_carrier_errors + + ;
# else
nl - > stats . tx_errors + + ,
nl - > stats . tx_carrier_errors + + ;
# endif
nl - > tx_frameno = 0 ;
nl - > framelen = 0 ;
nl - > outpos = 0 ;
nl - > state & = ~ ( FL_WAIT_ACK | FL_NEED_RESEND ) ;
# ifdef CONFIG_SBNI_MULTILINE
netif_start_queue ( nl - > master ) ;
nl - > master - > trans_start = jiffies ;
# else
netif_start_queue ( dev ) ;
dev - > trans_start = jiffies ;
# endif
}
static void
send_frame_header ( struct net_device * dev , u32 * crc_p )
{
struct net_local * nl = ( struct net_local * ) dev - > priv ;
u32 crc = * crc_p ;
u32 len_field = nl - > framelen + 6 ; /* CRC + frameno + reserved */
u8 value ;
if ( nl - > state & FL_NEED_RESEND )
len_field | = FRAME_RETRY ; /* non-first attempt... */
if ( nl - > outpos = = 0 )
len_field | = FRAME_FIRST ;
len_field | = ( nl - > state & FL_PREV_OK ) ? FRAME_SENT_OK : FRAME_SENT_BAD ;
outb ( SBNI_SIG , dev - > base_addr + DAT ) ;
value = ( u8 ) len_field ;
outb ( value , dev - > base_addr + DAT ) ;
crc = CRC32 ( value , crc ) ;
value = ( u8 ) ( len_field > > 8 ) ;
outb ( value , dev - > base_addr + DAT ) ;
crc = CRC32 ( value , crc ) ;
outb ( nl - > tx_frameno , dev - > base_addr + DAT ) ;
crc = CRC32 ( nl - > tx_frameno , crc ) ;
outb ( 0 , dev - > base_addr + DAT ) ;
crc = CRC32 ( 0 , crc ) ;
* crc_p = crc ;
}
/*
* if frame tail not needed ( incorrect number or received twice ) ,
* it won ' t store , but CRC will be calculated
*/
static int
skip_tail ( unsigned int ioaddr , unsigned int tail_len , u32 crc )
{
while ( tail_len - - )
crc = CRC32 ( inb ( ioaddr + DAT ) , crc ) ;
return crc = = CRC32_REMAINDER ;
}
/*
* Preliminary checks if frame header is correct , calculates its CRC
* and split it to simple fields
*/
static int
check_fhdr ( u32 ioaddr , u32 * framelen , u32 * frameno , u32 * ack ,
u32 * is_first , u32 * crc_p )
{
u32 crc = * crc_p ;
u8 value ;
if ( inb ( ioaddr + DAT ) ! = SBNI_SIG )
return 0 ;
value = inb ( ioaddr + DAT ) ;
* framelen = ( u32 ) value ;
crc = CRC32 ( value , crc ) ;
value = inb ( ioaddr + DAT ) ;
* framelen | = ( ( u32 ) value ) < < 8 ;
crc = CRC32 ( value , crc ) ;
* ack = * framelen & FRAME_ACK_MASK ;
* is_first = ( * framelen & FRAME_FIRST ) ! = 0 ;
if ( ( * framelen & = FRAME_LEN_MASK ) < 6
| | * framelen > SBNI_MAX_FRAME - 3 )
return 0 ;
value = inb ( ioaddr + DAT ) ;
* frameno = ( u32 ) value ;
crc = CRC32 ( value , crc ) ;
crc = CRC32 ( inb ( ioaddr + DAT ) , crc ) ; /* reserved byte */
* framelen - = 2 ;
* crc_p = crc ;
return 1 ;
}
static struct sk_buff *
get_rx_buf ( struct net_device * dev )
{
/* +2 is to compensate for the alignment fixup below */
struct sk_buff * skb = dev_alloc_skb ( ETHER_MAX_LEN + 2 ) ;
if ( ! skb )
return NULL ;
# ifdef CONFIG_SBNI_MULTILINE
skb - > dev = ( ( struct net_local * ) dev - > priv ) - > master ;
# else
skb - > dev = dev ;
# endif
skb_reserve ( skb , 2 ) ; /* Align IP on longword boundaries */
return skb ;
}
static void
indicate_pkt ( struct net_device * dev )
{
struct net_local * nl = ( struct net_local * ) dev - > priv ;
struct sk_buff * skb = nl - > rx_buf_p ;
skb_put ( skb , nl - > inppos ) ;
# ifdef CONFIG_SBNI_MULTILINE
skb - > protocol = eth_type_trans ( skb , nl - > master ) ;
netif_rx ( skb ) ;
dev - > last_rx = jiffies ;
+ + ( ( struct net_local * ) nl - > master - > priv ) - > stats . rx_packets ;
( ( struct net_local * ) nl - > master - > priv ) - > stats . rx_bytes + = nl - > inppos ;
# else
skb - > protocol = eth_type_trans ( skb , dev ) ;
netif_rx ( skb ) ;
dev - > last_rx = jiffies ;
+ + nl - > stats . rx_packets ;
nl - > stats . rx_bytes + = nl - > inppos ;
# endif
nl - > rx_buf_p = NULL ; /* protocol driver will clear this sk_buff */
}
/* -------------------------------------------------------------------------- */
/*
* Routine checks periodically wire activity and regenerates marker if
* connect was inactive for a long time .
*/
static void
sbni_watchdog ( unsigned long arg )
{
struct net_device * dev = ( struct net_device * ) arg ;
struct net_local * nl = ( struct net_local * ) dev - > priv ;
struct timer_list * w = & nl - > watchdog ;
unsigned long flags ;
unsigned char csr0 ;
spin_lock_irqsave ( & nl - > lock , flags ) ;
csr0 = inb ( dev - > base_addr + CSR0 ) ;
if ( csr0 & RC_CHK ) {
if ( nl - > timer_ticks ) {
if ( csr0 & ( RC_RDY | BU_EMP ) )
/* receiving not active */
nl - > timer_ticks - - ;
} else {
nl - > in_stats . timeout_number + + ;
if ( nl - > delta_rxl )
timeout_change_level ( dev ) ;
outb ( * ( u_char * ) & nl - > csr1 | PR_RES ,
dev - > base_addr + CSR1 ) ;
csr0 = inb ( dev - > base_addr + CSR0 ) ;
}
} else
nl - > state & = ~ FL_LINE_DOWN ;
outb ( csr0 | RC_CHK , dev - > base_addr + CSR0 ) ;
init_timer ( w ) ;
w - > expires = jiffies + SBNI_TIMEOUT ;
w - > data = arg ;
w - > function = sbni_watchdog ;
add_timer ( w ) ;
spin_unlock_irqrestore ( & nl - > lock , flags ) ;
}
static unsigned char rxl_tab [ ] = {
0x00 , 0x01 , 0x02 , 0x03 , 0x04 , 0x05 , 0x06 , 0x08 ,
0x0a , 0x0c , 0x0f , 0x16 , 0x18 , 0x1a , 0x1c , 0x1f
} ;
# define SIZE_OF_TIMEOUT_RXL_TAB 4
static unsigned char timeout_rxl_tab [ ] = {
0x03 , 0x05 , 0x08 , 0x0b
} ;
/* -------------------------------------------------------------------------- */
static void
card_start ( struct net_device * dev )
{
struct net_local * nl = ( struct net_local * ) dev - > priv ;
nl - > timer_ticks = CHANGE_LEVEL_START_TICKS ;
nl - > state & = ~ ( FL_WAIT_ACK | FL_NEED_RESEND ) ;
nl - > state | = FL_PREV_OK ;
nl - > inppos = nl - > outpos = 0 ;
nl - > wait_frameno = 0 ;
nl - > tx_frameno = 0 ;
nl - > framelen = 0 ;
outb ( * ( u_char * ) & nl - > csr1 | PR_RES , dev - > base_addr + CSR1 ) ;
outb ( EN_INT , dev - > base_addr + CSR0 ) ;
}
/* -------------------------------------------------------------------------- */
/* Receive level auto-selection */
static void
change_level ( struct net_device * dev )
{
struct net_local * nl = ( struct net_local * ) dev - > priv ;
if ( nl - > delta_rxl = = 0 ) /* do not auto-negotiate RxL */
return ;
if ( nl - > cur_rxl_index = = 0 )
nl - > delta_rxl = 1 ;
else if ( nl - > cur_rxl_index = = 15 )
nl - > delta_rxl = - 1 ;
else if ( nl - > cur_rxl_rcvd < nl - > prev_rxl_rcvd )
nl - > delta_rxl = - nl - > delta_rxl ;
nl - > csr1 . rxl = rxl_tab [ nl - > cur_rxl_index + = nl - > delta_rxl ] ;
inb ( dev - > base_addr + CSR0 ) ; /* needs for PCI cards */
outb ( * ( u8 * ) & nl - > csr1 , dev - > base_addr + CSR1 ) ;
nl - > prev_rxl_rcvd = nl - > cur_rxl_rcvd ;
nl - > cur_rxl_rcvd = 0 ;
}
static void
timeout_change_level ( struct net_device * dev )
{
struct net_local * nl = ( struct net_local * ) dev - > priv ;
nl - > cur_rxl_index = timeout_rxl_tab [ nl - > timeout_rxl ] ;
if ( + + nl - > timeout_rxl > = 4 )
nl - > timeout_rxl = 0 ;
nl - > csr1 . rxl = rxl_tab [ nl - > cur_rxl_index ] ;
inb ( dev - > base_addr + CSR0 ) ;
outb ( * ( unsigned char * ) & nl - > csr1 , dev - > base_addr + CSR1 ) ;
nl - > prev_rxl_rcvd = nl - > cur_rxl_rcvd ;
nl - > cur_rxl_rcvd = 0 ;
}
/* -------------------------------------------------------------------------- */
/*
* Open / initialize the board .
*/
static int
sbni_open ( struct net_device * dev )
{
struct net_local * nl = ( struct net_local * ) dev - > priv ;
struct timer_list * w = & nl - > watchdog ;
/*
* For double ISA adapters within " common irq " mode , we have to
* determine whether primary or secondary channel is initialized ,
* and set the irq handler only in first case .
*/
if ( dev - > base_addr < 0x400 ) { /* ISA only */
struct net_device * * p = sbni_cards ;
for ( ; * p & & p < sbni_cards + SBNI_MAX_NUM_CARDS ; + + p )
if ( ( * p ) - > irq = = dev - > irq
& & ( ( * p ) - > base_addr = = dev - > base_addr + 4
| | ( * p ) - > base_addr = = dev - > base_addr - 4 )
& & ( * p ) - > flags & IFF_UP ) {
( ( struct net_local * ) ( ( * p ) - > priv ) )
- > second = dev ;
printk ( KERN_NOTICE " %s: using shared irq "
" with %s \n " , dev - > name , ( * p ) - > name ) ;
nl - > state | = FL_SECONDARY ;
goto handler_attached ;
}
}
if ( request_irq ( dev - > irq , sbni_interrupt , SA_SHIRQ , dev - > name , dev ) ) {
printk ( KERN_ERR " %s: unable to get IRQ %d. \n " ,
dev - > name , dev - > irq ) ;
return - EAGAIN ;
}
handler_attached :
spin_lock ( & nl - > lock ) ;
memset ( & nl - > stats , 0 , sizeof ( struct net_device_stats ) ) ;
memset ( & nl - > in_stats , 0 , sizeof ( struct sbni_in_stats ) ) ;
card_start ( dev ) ;
netif_start_queue ( dev ) ;
/* set timer watchdog */
init_timer ( w ) ;
w - > expires = jiffies + SBNI_TIMEOUT ;
w - > data = ( unsigned long ) dev ;
w - > function = sbni_watchdog ;
add_timer ( w ) ;
spin_unlock ( & nl - > lock ) ;
return 0 ;
}
static int
sbni_close ( struct net_device * dev )
{
struct net_local * nl = ( struct net_local * ) dev - > priv ;
if ( nl - > second & & nl - > second - > flags & IFF_UP ) {
printk ( KERN_NOTICE " Secondary channel (%s) is active! \n " ,
nl - > second - > name ) ;
return - EBUSY ;
}
# ifdef CONFIG_SBNI_MULTILINE
if ( nl - > state & FL_SLAVE )
emancipate ( dev ) ;
else
while ( nl - > link ) /* it's master device! */
emancipate ( nl - > link ) ;
# endif
spin_lock ( & nl - > lock ) ;
nl - > second = NULL ;
drop_xmit_queue ( dev ) ;
netif_stop_queue ( dev ) ;
del_timer ( & nl - > watchdog ) ;
outb ( 0 , dev - > base_addr + CSR0 ) ;
if ( ! ( nl - > state & FL_SECONDARY ) )
free_irq ( dev - > irq , dev ) ;
nl - > state & = FL_SECONDARY ;
spin_unlock ( & nl - > lock ) ;
return 0 ;
}
/*
Valid combinations in CSR0 ( for probing ) :
VALID_DECODER 0000 , 0011 , 1011 , 1010
; 0 ; -
TR_REQ ; 1 ; +
TR_RDY ; 2 ; -
TR_RDY TR_REQ ; 3 ; +
BU_EMP ; 4 ; +
BU_EMP TR_REQ ; 5 ; +
BU_EMP TR_RDY ; 6 ; -
BU_EMP TR_RDY TR_REQ ; 7 ; +
RC_RDY ; 8 ; +
RC_RDY TR_REQ ; 9 ; +
RC_RDY TR_RDY ; 10 ; -
RC_RDY TR_RDY TR_REQ ; 11 ; -
RC_RDY BU_EMP ; 12 ; -
RC_RDY BU_EMP TR_REQ ; 13 ; -
RC_RDY BU_EMP TR_RDY ; 14 ; -
RC_RDY BU_EMP TR_RDY TR_REQ ; 15 ; -
*/
# define VALID_DECODER (2 + 8 + 0x10 + 0x20 + 0x80 + 0x100 + 0x200)
static int
sbni_card_probe ( unsigned long ioaddr )
{
unsigned char csr0 ;
csr0 = inb ( ioaddr + CSR0 ) ;
if ( csr0 ! = 0xff & & csr0 ! = 0x00 ) {
csr0 & = ~ EN_INT ;
if ( csr0 & BU_EMP )
csr0 | = EN_INT ;
if ( VALID_DECODER & ( 1 < < ( csr0 > > 4 ) ) )
return 0 ;
}
return - ENODEV ;
}
/* -------------------------------------------------------------------------- */
static int
sbni_ioctl ( struct net_device * dev , struct ifreq * ifr , int cmd )
{
struct net_local * nl = ( struct net_local * ) dev - > priv ;
struct sbni_flags flags ;
int error = 0 ;
# ifdef CONFIG_SBNI_MULTILINE
struct net_device * slave_dev ;
char slave_name [ 8 ] ;
# endif
switch ( cmd ) {
case SIOCDEVGETINSTATS :
if ( copy_to_user ( ifr - > ifr_data , & nl - > in_stats ,
sizeof ( struct sbni_in_stats ) ) )
error = - EFAULT ;
break ;
case SIOCDEVRESINSTATS :
if ( current - > euid ! = 0 ) /* root only */
return - EPERM ;
memset ( & nl - > in_stats , 0 , sizeof ( struct sbni_in_stats ) ) ;
break ;
case SIOCDEVGHWSTATE :
flags . mac_addr = * ( u32 * ) ( dev - > dev_addr + 3 ) ;
flags . rate = nl - > csr1 . rate ;
flags . slow_mode = ( nl - > state & FL_SLOW_MODE ) ! = 0 ;
flags . rxl = nl - > cur_rxl_index ;
flags . fixed_rxl = nl - > delta_rxl = = 0 ;
if ( copy_to_user ( ifr - > ifr_data , & flags , sizeof flags ) )
error = - EFAULT ;
break ;
case SIOCDEVSHWSTATE :
if ( current - > euid ! = 0 ) /* root only */
return - EPERM ;
spin_lock ( & nl - > lock ) ;
flags = * ( struct sbni_flags * ) & ifr - > ifr_ifru ;
if ( flags . fixed_rxl )
nl - > delta_rxl = 0 ,
nl - > cur_rxl_index = flags . rxl ;
else
nl - > delta_rxl = DEF_RXL_DELTA ,
nl - > cur_rxl_index = DEF_RXL ;
nl - > csr1 . rxl = rxl_tab [ nl - > cur_rxl_index ] ;
nl - > csr1 . rate = flags . rate ;
outb ( * ( u8 * ) & nl - > csr1 | PR_RES , dev - > base_addr + CSR1 ) ;
spin_unlock ( & nl - > lock ) ;
break ;
# ifdef CONFIG_SBNI_MULTILINE
case SIOCDEVENSLAVE :
if ( current - > euid ! = 0 ) /* root only */
return - EPERM ;
if ( copy_from_user ( slave_name , ifr - > ifr_data , sizeof slave_name ) )
return - EFAULT ;
slave_dev = dev_get_by_name ( slave_name ) ;
if ( ! slave_dev | | ! ( slave_dev - > flags & IFF_UP ) ) {
printk ( KERN_ERR " %s: trying to enslave non-active "
" device %s \n " , dev - > name , slave_name ) ;
return - EPERM ;
}
return enslave ( dev , slave_dev ) ;
case SIOCDEVEMANSIPATE :
if ( current - > euid ! = 0 ) /* root only */
return - EPERM ;
return emancipate ( dev ) ;
# endif /* CONFIG_SBNI_MULTILINE */
default :
return - EOPNOTSUPP ;
}
return error ;
}
# ifdef CONFIG_SBNI_MULTILINE
static int
enslave ( struct net_device * dev , struct net_device * slave_dev )
{
struct net_local * nl = ( struct net_local * ) dev - > priv ;
struct net_local * snl = ( struct net_local * ) slave_dev - > priv ;
if ( nl - > state & FL_SLAVE ) /* This isn't master or free device */
return - EBUSY ;
if ( snl - > state & FL_SLAVE ) /* That was already enslaved */
return - EBUSY ;
spin_lock ( & nl - > lock ) ;
spin_lock ( & snl - > lock ) ;
/* append to list */
snl - > link = nl - > link ;
nl - > link = slave_dev ;
snl - > master = dev ;
snl - > state | = FL_SLAVE ;
/* Summary statistics of MultiLine operation will be stored
in master ' s counters */
memset ( & snl - > stats , 0 , sizeof ( struct net_device_stats ) ) ;
netif_stop_queue ( slave_dev ) ;
netif_wake_queue ( dev ) ; /* Now we are able to transmit */
spin_unlock ( & snl - > lock ) ;
spin_unlock ( & nl - > lock ) ;
printk ( KERN_NOTICE " %s: slave device (%s) attached. \n " ,
dev - > name , slave_dev - > name ) ;
return 0 ;
}
static int
emancipate ( struct net_device * dev )
{
struct net_local * snl = ( struct net_local * ) dev - > priv ;
struct net_device * p = snl - > master ;
struct net_local * nl = ( struct net_local * ) p - > priv ;
if ( ! ( snl - > state & FL_SLAVE ) )
return - EINVAL ;
spin_lock ( & nl - > lock ) ;
spin_lock ( & snl - > lock ) ;
drop_xmit_queue ( dev ) ;
/* exclude from list */
for ( ; ; ) { /* must be in list */
struct net_local * t = ( struct net_local * ) p - > priv ;
if ( t - > link = = dev ) {
t - > link = snl - > link ;
break ;
}
p = t - > link ;
}
snl - > link = NULL ;
snl - > master = dev ;
snl - > state & = ~ FL_SLAVE ;
netif_start_queue ( dev ) ;
spin_unlock ( & snl - > lock ) ;
spin_unlock ( & nl - > lock ) ;
dev_put ( dev ) ;
return 0 ;
}
# endif
static struct net_device_stats *
sbni_get_stats ( struct net_device * dev )
{
return & ( ( struct net_local * ) dev - > priv ) - > stats ;
}
static void
set_multicast_list ( struct net_device * dev )
{
return ; /* sbni always operate in promiscuos mode */
}
# ifdef MODULE
module_param_array ( io , int , NULL , 0 ) ;
module_param_array ( irq , int , NULL , 0 ) ;
module_param_array ( baud , int , NULL , 0 ) ;
module_param_array ( rxl , int , NULL , 0 ) ;
module_param_array ( mac , int , NULL , 0 ) ;
module_param ( skip_pci_probe , bool , 0 ) ;
MODULE_LICENSE ( " GPL " ) ;
2006-03-21 09:34:52 +03:00
int __init init_module ( void )
2005-04-17 02:20:36 +04:00
{
struct net_device * dev ;
int err ;
while ( num < SBNI_MAX_NUM_CARDS ) {
dev = alloc_netdev ( sizeof ( struct net_local ) ,
" sbni%d " , sbni_devsetup ) ;
if ( ! dev )
break ;
sprintf ( dev - > name , " sbni%d " , num ) ;
err = sbni_init ( dev ) ;
if ( err ) {
free_netdev ( dev ) ;
break ;
}
if ( register_netdev ( dev ) ) {
release_region ( dev - > base_addr , SBNI_IO_EXTENT ) ;
free_netdev ( dev ) ;
break ;
}
}
return * sbni_cards ? 0 : - ENODEV ;
}
void
cleanup_module ( void )
{
struct net_device * dev ;
int num ;
for ( num = 0 ; num < SBNI_MAX_NUM_CARDS ; + + num )
if ( ( dev = sbni_cards [ num ] ) ! = NULL ) {
unregister_netdev ( dev ) ;
release_region ( dev - > base_addr , SBNI_IO_EXTENT ) ;
free_netdev ( dev ) ;
}
}
# else /* MODULE */
static int __init
sbni_setup ( char * p )
{
int n , parm ;
if ( * p + + ! = ' ( ' )
goto bad_param ;
for ( n = 0 , parm = 0 ; * p & & n < 8 ; ) {
( * dest [ parm ] ) [ n ] = simple_strtol ( p , & p , 0 ) ;
if ( ! * p | | * p = = ' ) ' )
return 1 ;
if ( * p = = ' ; ' )
+ + p , + + n , parm = 0 ;
else if ( * p + + ! = ' , ' )
break ;
else
if ( + + parm > = 5 )
break ;
}
bad_param :
printk ( KERN_ERR " Error in sbni kernel parameter! \n " ) ;
return 0 ;
}
__setup ( " sbni= " , sbni_setup ) ;
# endif /* MODULE */
/* -------------------------------------------------------------------------- */
# ifdef ASM_CRC
static u32
calc_crc32 ( u32 crc , u8 * p , u32 len )
{
register u32 _crc ;
_crc = crc ;
__asm__ __volatile__ (
" xorl %%ebx, %%ebx \n "
" movl %2, %%esi \n "
" movl %3, %%ecx \n "
" movl $crc32tab, %%edi \n "
" shrl $2, %%ecx \n "
" jz 1f \n "
" .align 4 \n "
" 0: \n "
" movb %%al, %%bl \n "
" movl (%%esi), %%edx \n "
" shrl $8, %%eax \n "
" xorb %%dl, %%bl \n "
" shrl $8, %%edx \n "
" xorl (%%edi,%%ebx,4), %%eax \n "
" movb %%al, %%bl \n "
" shrl $8, %%eax \n "
" xorb %%dl, %%bl \n "
" shrl $8, %%edx \n "
" xorl (%%edi,%%ebx,4), %%eax \n "
" movb %%al, %%bl \n "
" shrl $8, %%eax \n "
" xorb %%dl, %%bl \n "
" movb %%dh, %%dl \n "
" xorl (%%edi,%%ebx,4), %%eax \n "
" movb %%al, %%bl \n "
" shrl $8, %%eax \n "
" xorb %%dl, %%bl \n "
" addl $4, %%esi \n "
" xorl (%%edi,%%ebx,4), %%eax \n "
" decl %%ecx \n "
" jnz 0b \n "
" 1: \n "
" movl %3, %%ecx \n "
" andl $3, %%ecx \n "
" jz 2f \n "
" movb %%al, %%bl \n "
" shrl $8, %%eax \n "
" xorb (%%esi), %%bl \n "
" xorl (%%edi,%%ebx,4), %%eax \n "
" decl %%ecx \n "
" jz 2f \n "
" movb %%al, %%bl \n "
" shrl $8, %%eax \n "
" xorb 1(%%esi), %%bl \n "
" xorl (%%edi,%%ebx,4), %%eax \n "
" decl %%ecx \n "
" jz 2f \n "
" movb %%al, %%bl \n "
" shrl $8, %%eax \n "
" xorb 2(%%esi), %%bl \n "
" xorl (%%edi,%%ebx,4), %%eax \n "
" 2: \n "
: " =a " ( _crc )
: " 0 " ( _crc ) , " g " ( p ) , " g " ( len )
: " bx " , " cx " , " dx " , " si " , " di "
) ;
return _crc ;
}
# else /* ASM_CRC */
static u32
calc_crc32 ( u32 crc , u8 * p , u32 len )
{
while ( len - - )
crc = CRC32 ( * p + + , crc ) ;
return crc ;
}
# endif /* ASM_CRC */
static u32 crc32tab [ ] __attribute__ ( ( aligned ( 8 ) ) ) = {
0xD202EF8D , 0xA505DF1B , 0x3C0C8EA1 , 0x4B0BBE37 ,
0xD56F2B94 , 0xA2681B02 , 0x3B614AB8 , 0x4C667A2E ,
0xDCD967BF , 0xABDE5729 , 0x32D70693 , 0x45D03605 ,
0xDBB4A3A6 , 0xACB39330 , 0x35BAC28A , 0x42BDF21C ,
0xCFB5FFE9 , 0xB8B2CF7F , 0x21BB9EC5 , 0x56BCAE53 ,
0xC8D83BF0 , 0xBFDF0B66 , 0x26D65ADC , 0x51D16A4A ,
0xC16E77DB , 0xB669474D , 0x2F6016F7 , 0x58672661 ,
0xC603B3C2 , 0xB1048354 , 0x280DD2EE , 0x5F0AE278 ,
0xE96CCF45 , 0x9E6BFFD3 , 0x0762AE69 , 0x70659EFF ,
0xEE010B5C , 0x99063BCA , 0x000F6A70 , 0x77085AE6 ,
0xE7B74777 , 0x90B077E1 , 0x09B9265B , 0x7EBE16CD ,
0xE0DA836E , 0x97DDB3F8 , 0x0ED4E242 , 0x79D3D2D4 ,
0xF4DBDF21 , 0x83DCEFB7 , 0x1AD5BE0D , 0x6DD28E9B ,
0xF3B61B38 , 0x84B12BAE , 0x1DB87A14 , 0x6ABF4A82 ,
0xFA005713 , 0x8D076785 , 0x140E363F , 0x630906A9 ,
0xFD6D930A , 0x8A6AA39C , 0x1363F226 , 0x6464C2B0 ,
0xA4DEAE1D , 0xD3D99E8B , 0x4AD0CF31 , 0x3DD7FFA7 ,
0xA3B36A04 , 0xD4B45A92 , 0x4DBD0B28 , 0x3ABA3BBE ,
0xAA05262F , 0xDD0216B9 , 0x440B4703 , 0x330C7795 ,
0xAD68E236 , 0xDA6FD2A0 , 0x4366831A , 0x3461B38C ,
0xB969BE79 , 0xCE6E8EEF , 0x5767DF55 , 0x2060EFC3 ,
0xBE047A60 , 0xC9034AF6 , 0x500A1B4C , 0x270D2BDA ,
0xB7B2364B , 0xC0B506DD , 0x59BC5767 , 0x2EBB67F1 ,
0xB0DFF252 , 0xC7D8C2C4 , 0x5ED1937E , 0x29D6A3E8 ,
0x9FB08ED5 , 0xE8B7BE43 , 0x71BEEFF9 , 0x06B9DF6F ,
0x98DD4ACC , 0xEFDA7A5A , 0x76D32BE0 , 0x01D41B76 ,
0x916B06E7 , 0xE66C3671 , 0x7F6567CB , 0x0862575D ,
0x9606C2FE , 0xE101F268 , 0x7808A3D2 , 0x0F0F9344 ,
0x82079EB1 , 0xF500AE27 , 0x6C09FF9D , 0x1B0ECF0B ,
0x856A5AA8 , 0xF26D6A3E , 0x6B643B84 , 0x1C630B12 ,
0x8CDC1683 , 0xFBDB2615 , 0x62D277AF , 0x15D54739 ,
0x8BB1D29A , 0xFCB6E20C , 0x65BFB3B6 , 0x12B88320 ,
0x3FBA6CAD , 0x48BD5C3B , 0xD1B40D81 , 0xA6B33D17 ,
0x38D7A8B4 , 0x4FD09822 , 0xD6D9C998 , 0xA1DEF90E ,
0x3161E49F , 0x4666D409 , 0xDF6F85B3 , 0xA868B525 ,
0x360C2086 , 0x410B1010 , 0xD80241AA , 0xAF05713C ,
0x220D7CC9 , 0x550A4C5F , 0xCC031DE5 , 0xBB042D73 ,
0x2560B8D0 , 0x52678846 , 0xCB6ED9FC , 0xBC69E96A ,
0x2CD6F4FB , 0x5BD1C46D , 0xC2D895D7 , 0xB5DFA541 ,
0x2BBB30E2 , 0x5CBC0074 , 0xC5B551CE , 0xB2B26158 ,
0x04D44C65 , 0x73D37CF3 , 0xEADA2D49 , 0x9DDD1DDF ,
0x03B9887C , 0x74BEB8EA , 0xEDB7E950 , 0x9AB0D9C6 ,
0x0A0FC457 , 0x7D08F4C1 , 0xE401A57B , 0x930695ED ,
0x0D62004E , 0x7A6530D8 , 0xE36C6162 , 0x946B51F4 ,
0x19635C01 , 0x6E646C97 , 0xF76D3D2D , 0x806A0DBB ,
0x1E0E9818 , 0x6909A88E , 0xF000F934 , 0x8707C9A2 ,
0x17B8D433 , 0x60BFE4A5 , 0xF9B6B51F , 0x8EB18589 ,
0x10D5102A , 0x67D220BC , 0xFEDB7106 , 0x89DC4190 ,
0x49662D3D , 0x3E611DAB , 0xA7684C11 , 0xD06F7C87 ,
0x4E0BE924 , 0x390CD9B2 , 0xA0058808 , 0xD702B89E ,
0x47BDA50F , 0x30BA9599 , 0xA9B3C423 , 0xDEB4F4B5 ,
0x40D06116 , 0x37D75180 , 0xAEDE003A , 0xD9D930AC ,
0x54D13D59 , 0x23D60DCF , 0xBADF5C75 , 0xCDD86CE3 ,
0x53BCF940 , 0x24BBC9D6 , 0xBDB2986C , 0xCAB5A8FA ,
0x5A0AB56B , 0x2D0D85FD , 0xB404D447 , 0xC303E4D1 ,
0x5D677172 , 0x2A6041E4 , 0xB369105E , 0xC46E20C8 ,
0x72080DF5 , 0x050F3D63 , 0x9C066CD9 , 0xEB015C4F ,
0x7565C9EC , 0x0262F97A , 0x9B6BA8C0 , 0xEC6C9856 ,
0x7CD385C7 , 0x0BD4B551 , 0x92DDE4EB , 0xE5DAD47D ,
0x7BBE41DE , 0x0CB97148 , 0x95B020F2 , 0xE2B71064 ,
0x6FBF1D91 , 0x18B82D07 , 0x81B17CBD , 0xF6B64C2B ,
0x68D2D988 , 0x1FD5E91E , 0x86DCB8A4 , 0xF1DB8832 ,
0x616495A3 , 0x1663A535 , 0x8F6AF48F , 0xF86DC419 ,
0x660951BA , 0x110E612C , 0x88073096 , 0xFF000000
} ;