alistair23-linux/drivers/net/wan/sbni.c
Tejun Heo 5a0e3ad6af include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files.  percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.

percpu.h -> slab.h dependency is about to be removed.  Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability.  As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.

  http://userweb.kernel.org/~tj/misc/slabh-sweep.py

The script does the followings.

* Scan files for gfp and slab usages and update includes such that
  only the necessary includes are there.  ie. if only gfp is used,
  gfp.h, if slab is used, slab.h.

* When the script inserts a new include, it looks at the include
  blocks and try to put the new include such that its order conforms
  to its surrounding.  It's put in the include block which contains
  core kernel includes, in the same order that the rest are ordered -
  alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
  doesn't seem to be any matching order.

* If the script can't find a place to put a new include (mostly
  because the file doesn't have fitting include block), it prints out
  an error message indicating which .h file needs to be added to the
  file.

The conversion was done in the following steps.

1. The initial automatic conversion of all .c files updated slightly
   over 4000 files, deleting around 700 includes and adding ~480 gfp.h
   and ~3000 slab.h inclusions.  The script emitted errors for ~400
   files.

2. Each error was manually checked.  Some didn't need the inclusion,
   some needed manual addition while adding it to implementation .h or
   embedding .c file was more appropriate for others.  This step added
   inclusions to around 150 files.

3. The script was run again and the output was compared to the edits
   from #2 to make sure no file was left behind.

4. Several build tests were done and a couple of problems were fixed.
   e.g. lib/decompress_*.c used malloc/free() wrappers around slab
   APIs requiring slab.h to be added manually.

5. The script was run on all .h files but without automatically
   editing them as sprinkling gfp.h and slab.h inclusions around .h
   files could easily lead to inclusion dependency hell.  Most gfp.h
   inclusion directives were ignored as stuff from gfp.h was usually
   wildly available and often used in preprocessor macros.  Each
   slab.h inclusion directive was examined and added manually as
   necessary.

6. percpu.h was updated not to include slab.h.

7. Build test were done on the following configurations and failures
   were fixed.  CONFIG_GCOV_KERNEL was turned off for all tests (as my
   distributed build env didn't work with gcov compiles) and a few
   more options had to be turned off depending on archs to make things
   build (like ipr on powerpc/64 which failed due to missing writeq).

   * x86 and x86_64 UP and SMP allmodconfig and a custom test config.
   * powerpc and powerpc64 SMP allmodconfig
   * sparc and sparc64 SMP allmodconfig
   * ia64 SMP allmodconfig
   * s390 SMP allmodconfig
   * alpha SMP allmodconfig
   * um on x86_64 SMP allmodconfig

8. percpu.h modifications were reverted so that it could be applied as
   a separate patch and serve as bisection point.

Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.

Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-30 22:02:32 +09:00

1721 lines
42 KiB
C

/* 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/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/net_namespace.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 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 netdev_tx_t sbni_start_xmit(struct sk_buff *,
struct net_device * );
static int sbni_ioctl( struct net_device *, struct ifreq *, int );
static void set_multicast_list( struct net_device * );
static irqreturn_t sbni_interrupt( int, void * );
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 };
#define NET_LOCAL_LOCK(dev) (((struct net_local *)netdev_priv(dev))->lock)
/*
* 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 const struct net_device_ops sbni_netdev_ops = {
.ndo_open = sbni_open,
.ndo_stop = sbni_close,
.ndo_start_xmit = sbni_start_xmit,
.ndo_set_multicast_list = set_multicast_list,
.ndo_do_ioctl = sbni_ioctl,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
};
static void __init sbni_devsetup(struct net_device *dev)
{
ether_setup( dev );
dev->netdev_ops = &sbni_netdev_ops;
}
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;
dev->netdev_ops = &sbni_netdev_ops;
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;
}
static 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"
" 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;
/* Fill in sbni-specific dev fields. */
nl = netdev_priv(dev);
if( !nl ) {
printk( KERN_ERR "%s: unable to get memory!\n", dev->name );
release_region( ioaddr, SBNI_IO_EXTENT );
return NULL;
}
memset( nl, 0, sizeof(struct net_local) );
spin_lock_init( &nl->lock );
/* store MAC address (generate if that isn't known) */
*(__be16 *)dev->dev_addr = htons( 0x00ff );
*(__be32 *)(dev->dev_addr + 2) = htonl( 0x01000000 |
((mac[num] ?
mac[num] :
(u32)((long)netdev_priv(dev))) & 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 netdev_tx_t
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 = netdev_priv(p);
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 NETDEV_TX_OK;
}
}
return NETDEV_TX_BUSY;
}
#else /* CONFIG_SBNI_MULTILINE */
static netdev_tx_t
sbni_start_xmit( struct sk_buff *skb, struct net_device *dev )
{
struct net_local *nl = netdev_priv(dev);
netif_stop_queue( dev );
spin_lock( &nl->lock );
prepare_to_send( skb, dev );
spin_unlock( &nl->lock );
return NETDEV_TX_OK;
}
#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 net_device *dev = dev_id;
struct net_local *nl = netdev_priv(dev);
int repeat;
spin_lock( &nl->lock );
if( nl->second )
spin_lock(&NET_LOCAL_LOCK(nl->second));
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(&NET_LOCAL_LOCK(nl->second));
spin_unlock( &nl->lock );
return IRQ_HANDLED;
}
static void
handle_channel( struct net_device *dev )
{
struct net_local *nl = netdev_priv(dev);
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(&NET_LOCAL_LOCK(nl->master));
#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(&NET_LOCAL_LOCK(nl->master));
#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 = netdev_priv(dev);
unsigned long ioaddr = dev->base_addr;
u32 crc = CRC32_INITIAL;
unsigned framelen = 0, frameno, ack;
unsigned is_first, frame_ok = 0;
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 );
}
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 = netdev_priv(dev);
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 = netdev_priv(dev);
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 = netdev_priv(dev);
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
nl->master->stats.rx_errors++,
nl->master->stats.rx_missed_errors++;
#else
dev->stats.rx_errors++,
dev->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
nl->master->stats.rx_errors++,
nl->master->stats.rx_crc_errors++;
#else
dev->stats.rx_errors++,
dev->stats.rx_crc_errors++;
#endif
return frame_ok;
}
static inline void
send_complete( struct net_device *dev )
{
struct net_local *nl = netdev_priv(dev);
#ifdef CONFIG_SBNI_MULTILINE
nl->master->stats.tx_packets++;
nl->master->stats.tx_bytes += nl->tx_buf_p->len;
#else
dev->stats.tx_packets++;
dev->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 = netdev_priv(dev);
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( dev ),
#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 = netdev_priv(dev);
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 = netdev_priv(dev);
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 = DIV_ROUND_UP(len, 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 = netdev_priv(dev);
if( nl->tx_buf_p )
dev_kfree_skb_any( nl->tx_buf_p ),
nl->tx_buf_p = NULL,
#ifdef CONFIG_SBNI_MULTILINE
nl->master->stats.tx_errors++,
nl->master->stats.tx_carrier_errors++;
#else
dev->stats.tx_errors++,
dev->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 = netdev_priv(dev);
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;
skb_reserve( skb, 2 ); /* Align IP on longword boundaries */
return skb;
}
static void
indicate_pkt( struct net_device *dev )
{
struct net_local *nl = netdev_priv(dev);
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 );
++nl->master->stats.rx_packets;
nl->master->stats.rx_bytes += nl->inppos;
#else
skb->protocol = eth_type_trans( skb, dev );
netif_rx( skb );
++dev->stats.rx_packets;
dev->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 = netdev_priv(dev);
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 = netdev_priv(dev);
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 = netdev_priv(dev);
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 = netdev_priv(dev);
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 = netdev_priv(dev);
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 *) (netdev_priv(*p)))
->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, IRQF_SHARED, 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( &dev->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 = netdev_priv(dev);
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 = netdev_priv(dev);
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 (!capable(CAP_NET_ADMIN))
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 (!capable(CAP_NET_ADMIN))
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 (!capable(CAP_NET_ADMIN))
return -EPERM;
if (copy_from_user( slave_name, ifr->ifr_data, sizeof slave_name ))
return -EFAULT;
slave_dev = dev_get_by_name(&init_net, 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 (!capable(CAP_NET_ADMIN))
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 = netdev_priv(dev);
struct net_local *snl = netdev_priv(slave_dev);
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( &slave_dev->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 = netdev_priv(dev);
struct net_device *p = snl->master;
struct net_local *nl = netdev_priv(p);
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 = netdev_priv(p);
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 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");
int __init init_module( void )
{
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)
{
int i;
for (i = 0; i < SBNI_MAX_NUM_CARDS; ++i) {
struct net_device *dev = sbni_cards[i];
if (dev != 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
};