2005-04-17 02:20:36 +04:00
/* drivers/atm/zatm.c - ZeitNet ZN122x device driver */
/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
# include <linux/config.h>
# include <linux/module.h>
# include <linux/sched.h>
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/pci.h>
# include <linux/errno.h>
# include <linux/atm.h>
# include <linux/atmdev.h>
# include <linux/sonet.h>
# include <linux/skbuff.h>
# include <linux/netdevice.h>
# include <linux/delay.h>
# include <linux/uio.h>
# include <linux/init.h>
2005-07-20 23:01:46 +04:00
# include <linux/dma-mapping.h>
2005-04-17 02:20:36 +04:00
# include <linux/atm_zatm.h>
# include <linux/capability.h>
# include <linux/bitops.h>
# include <linux/wait.h>
# include <asm/byteorder.h>
# include <asm/system.h>
# include <asm/string.h>
# include <asm/io.h>
# include <asm/atomic.h>
# include <asm/uaccess.h>
# include "uPD98401.h"
# include "uPD98402.h"
# include "zeprom.h"
# include "zatm.h"
/*
* TODO :
*
* Minor features
* - support 64 kB SDUs ( will have to use multibuffer batches then : - ( )
* - proper use of CDV , credit = max ( 1 , CDVT * PCR )
* - AAL0
* - better receive timestamps
* - OAM
*/
# define ZATM_COPPER 1
#if 0
# define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
# else
# define DPRINTK(format,args...)
# endif
# ifndef CONFIG_ATM_ZATM_DEBUG
# define NULLCHECK(x)
# define EVENT(s,a,b)
static void event_dump ( void )
{
}
# else
/*
* NULL pointer checking
*/
# define NULLCHECK(x) \
if ( ( unsigned long ) ( x ) < 0x30 ) printk ( KERN_CRIT # x " ==0x%x \n " , ( int ) ( x ) )
/*
* Very extensive activity logging . Greatly improves bug detection speed but
* costs a few Mbps if enabled .
*/
# define EV 64
static const char * ev [ EV ] ;
static unsigned long ev_a [ EV ] , ev_b [ EV ] ;
static int ec = 0 ;
static void EVENT ( const char * s , unsigned long a , unsigned long b )
{
ev [ ec ] = s ;
ev_a [ ec ] = a ;
ev_b [ ec ] = b ;
ec = ( ec + 1 ) % EV ;
}
static void event_dump ( void )
{
int n , i ;
printk ( KERN_NOTICE " ----- event dump follows ----- \n " ) ;
for ( n = 0 ; n < EV ; n + + ) {
i = ( ec + n ) % EV ;
printk ( KERN_NOTICE ) ;
printk ( ev [ i ] ? ev [ i ] : " (null) " , ev_a [ i ] , ev_b [ i ] ) ;
}
printk ( KERN_NOTICE " ----- event dump ends here ----- \n " ) ;
}
# endif /* CONFIG_ATM_ZATM_DEBUG */
# define RING_BUSY 1 / * indication from do_tx that PDU has to be
backlogged */
static struct atm_dev * zatm_boards = NULL ;
static unsigned long dummy [ 2 ] = { 0 , 0 } ;
# define zin_n(r) inl(zatm_dev->base+r*4)
# define zin(r) inl(zatm_dev->base+uPD98401_##r*4)
# define zout(v,r) outl(v,zatm_dev->base+uPD98401_##r*4)
# define zwait while (zin(CMR) & uPD98401_BUSY)
/* RX0, RX1, TX0, TX1 */
static const int mbx_entries [ NR_MBX ] = { 1024 , 1024 , 1024 , 1024 } ;
static const int mbx_esize [ NR_MBX ] = { 16 , 16 , 4 , 4 } ; /* entry size in bytes */
# define MBX_SIZE(i) (mbx_entries[i]*mbx_esize[i])
/*-------------------------------- utilities --------------------------------*/
static void zpokel ( struct zatm_dev * zatm_dev , u32 value , u32 addr )
{
zwait ;
zout ( value , CER ) ;
zout ( uPD98401_IND_ACC | uPD98401_IA_BALL |
( uPD98401_IA_TGT_CM < < uPD98401_IA_TGT_SHIFT ) | addr , CMR ) ;
}
static u32 zpeekl ( struct zatm_dev * zatm_dev , u32 addr )
{
zwait ;
zout ( uPD98401_IND_ACC | uPD98401_IA_BALL | uPD98401_IA_RW |
( uPD98401_IA_TGT_CM < < uPD98401_IA_TGT_SHIFT ) | addr , CMR ) ;
zwait ;
return zin ( CER ) ;
}
/*------------------------------- free lists --------------------------------*/
/*
* Free buffer head structure :
* [ 0 ] pointer to buffer ( for SAR )
* [ 1 ] buffer descr link pointer ( for SAR )
* [ 2 ] back pointer to skb ( for poll_rx )
* [ 3 ] data
* . . .
*/
struct rx_buffer_head {
u32 buffer ; /* pointer to buffer (for SAR) */
u32 link ; /* buffer descriptor link pointer (for SAR) */
struct sk_buff * skb ; /* back pointer to skb (for poll_rx) */
} ;
static void refill_pool ( struct atm_dev * dev , int pool )
{
struct zatm_dev * zatm_dev ;
struct sk_buff * skb ;
struct rx_buffer_head * first ;
unsigned long flags ;
int align , offset , free , count , size ;
EVENT ( " refill_pool \n " , 0 , 0 ) ;
zatm_dev = ZATM_DEV ( dev ) ;
size = ( 64 < < ( pool < = ZATM_AAL5_POOL_BASE ? 0 :
pool - ZATM_AAL5_POOL_BASE ) ) + sizeof ( struct rx_buffer_head ) ;
if ( size < PAGE_SIZE ) {
align = 32 ; /* for 32 byte alignment */
offset = sizeof ( struct rx_buffer_head ) ;
}
else {
align = 4096 ;
offset = zatm_dev - > pool_info [ pool ] . offset +
sizeof ( struct rx_buffer_head ) ;
}
size + = align ;
spin_lock_irqsave ( & zatm_dev - > lock , flags ) ;
free = zpeekl ( zatm_dev , zatm_dev - > pool_base + 2 * pool ) &
uPD98401_RXFP_REMAIN ;
spin_unlock_irqrestore ( & zatm_dev - > lock , flags ) ;
if ( free > = zatm_dev - > pool_info [ pool ] . low_water ) return ;
EVENT ( " starting ... POOL: 0x%x, 0x%x \n " ,
zpeekl ( zatm_dev , zatm_dev - > pool_base + 2 * pool ) ,
zpeekl ( zatm_dev , zatm_dev - > pool_base + 2 * pool + 1 ) ) ;
EVENT ( " dummy: 0x%08lx, 0x%08lx \n " , dummy [ 0 ] , dummy [ 1 ] ) ;
count = 0 ;
first = NULL ;
while ( free < zatm_dev - > pool_info [ pool ] . high_water ) {
struct rx_buffer_head * head ;
skb = alloc_skb ( size , GFP_ATOMIC ) ;
if ( ! skb ) {
printk ( KERN_WARNING DEV_LABEL " (Itf %d): got no new "
" skb (%d) with %d free \n " , dev - > number , size , free ) ;
break ;
}
skb_reserve ( skb , ( unsigned char * ) ( ( ( ( unsigned long ) skb - > data +
align + offset - 1 ) & ~ ( unsigned long ) ( align - 1 ) ) - offset ) -
skb - > data ) ;
head = ( struct rx_buffer_head * ) skb - > data ;
skb_reserve ( skb , sizeof ( struct rx_buffer_head ) ) ;
if ( ! first ) first = head ;
count + + ;
head - > buffer = virt_to_bus ( skb - > data ) ;
head - > link = 0 ;
head - > skb = skb ;
EVENT ( " enq skb 0x%08lx/0x%08lx \n " , ( unsigned long ) skb ,
( unsigned long ) head ) ;
spin_lock_irqsave ( & zatm_dev - > lock , flags ) ;
if ( zatm_dev - > last_free [ pool ] )
( ( struct rx_buffer_head * ) ( zatm_dev - > last_free [ pool ] - >
data ) ) [ - 1 ] . link = virt_to_bus ( head ) ;
zatm_dev - > last_free [ pool ] = skb ;
skb_queue_tail ( & zatm_dev - > pool [ pool ] , skb ) ;
spin_unlock_irqrestore ( & zatm_dev - > lock , flags ) ;
free + + ;
}
if ( first ) {
spin_lock_irqsave ( & zatm_dev - > lock , flags ) ;
zwait ;
zout ( virt_to_bus ( first ) , CER ) ;
zout ( uPD98401_ADD_BAT | ( pool < < uPD98401_POOL_SHIFT ) | count ,
CMR ) ;
spin_unlock_irqrestore ( & zatm_dev - > lock , flags ) ;
EVENT ( " POOL: 0x%x, 0x%x \n " ,
zpeekl ( zatm_dev , zatm_dev - > pool_base + 2 * pool ) ,
zpeekl ( zatm_dev , zatm_dev - > pool_base + 2 * pool + 1 ) ) ;
EVENT ( " dummy: 0x%08lx, 0x%08lx \n " , dummy [ 0 ] , dummy [ 1 ] ) ;
}
}
static void drain_free ( struct atm_dev * dev , int pool )
{
skb_queue_purge ( & ZATM_DEV ( dev ) - > pool [ pool ] ) ;
}
static int pool_index ( int max_pdu )
{
int i ;
if ( max_pdu % ATM_CELL_PAYLOAD )
printk ( KERN_ERR DEV_LABEL " : driver error in pool_index: "
" max_pdu is %d \n " , max_pdu ) ;
if ( max_pdu > 65536 ) return - 1 ;
for ( i = 0 ; ( 64 < < i ) < max_pdu ; i + + ) ;
return i + ZATM_AAL5_POOL_BASE ;
}
/* use_pool isn't reentrant */
static void use_pool ( struct atm_dev * dev , int pool )
{
struct zatm_dev * zatm_dev ;
unsigned long flags ;
int size ;
zatm_dev = ZATM_DEV ( dev ) ;
if ( ! ( zatm_dev - > pool_info [ pool ] . ref_count + + ) ) {
skb_queue_head_init ( & zatm_dev - > pool [ pool ] ) ;
size = pool - ZATM_AAL5_POOL_BASE ;
if ( size < 0 ) size = 0 ; /* 64B... */
else if ( size > 10 ) size = 10 ; /* ... 64kB */
spin_lock_irqsave ( & zatm_dev - > lock , flags ) ;
zpokel ( zatm_dev , ( ( zatm_dev - > pool_info [ pool ] . low_water / 4 ) < <
uPD98401_RXFP_ALERT_SHIFT ) |
( 1 < < uPD98401_RXFP_BTSZ_SHIFT ) |
( size < < uPD98401_RXFP_BFSZ_SHIFT ) ,
zatm_dev - > pool_base + pool * 2 ) ;
zpokel ( zatm_dev , ( unsigned long ) dummy , zatm_dev - > pool_base +
pool * 2 + 1 ) ;
spin_unlock_irqrestore ( & zatm_dev - > lock , flags ) ;
zatm_dev - > last_free [ pool ] = NULL ;
refill_pool ( dev , pool ) ;
}
DPRINTK ( " pool %d: %d \n " , pool , zatm_dev - > pool_info [ pool ] . ref_count ) ;
}
static void unuse_pool ( struct atm_dev * dev , int pool )
{
if ( ! ( - - ZATM_DEV ( dev ) - > pool_info [ pool ] . ref_count ) )
drain_free ( dev , pool ) ;
}
/*----------------------------------- RX ------------------------------------*/
#if 0
static void exception ( struct atm_vcc * vcc )
{
static int count = 0 ;
struct zatm_dev * zatm_dev = ZATM_DEV ( vcc - > dev ) ;
struct zatm_vcc * zatm_vcc = ZATM_VCC ( vcc ) ;
unsigned long * qrp ;
int i ;
if ( count + + > 2 ) return ;
for ( i = 0 ; i < 8 ; i + + )
printk ( " TX%d: 0x%08lx \n " , i ,
zpeekl ( zatm_dev , zatm_vcc - > tx_chan * VC_SIZE / 4 + i ) ) ;
for ( i = 0 ; i < 5 ; i + + )
printk ( " SH%d: 0x%08lx \n " , i ,
zpeekl ( zatm_dev , uPD98401_IM ( zatm_vcc - > shaper ) + 16 * i ) ) ;
qrp = ( unsigned long * ) zpeekl ( zatm_dev , zatm_vcc - > tx_chan * VC_SIZE / 4 +
uPD98401_TXVC_QRP ) ;
printk ( " qrp=0x%08lx \n " , ( unsigned long ) qrp ) ;
for ( i = 0 ; i < 4 ; i + + ) printk ( " QRP[%d]: 0x%08lx " , i , qrp [ i ] ) ;
}
# endif
static const char * err_txt [ ] = {
" No error " ,
" RX buf underflow " ,
" RX FIFO overrun " ,
" Maximum len violation " ,
" CRC error " ,
" User abort " ,
" Length violation " ,
" T1 error " ,
" Deactivated " ,
" ??? " ,
" ??? " ,
" ??? " ,
" ??? " ,
" ??? " ,
" ??? " ,
" ??? "
} ;
static void poll_rx ( struct atm_dev * dev , int mbx )
{
struct zatm_dev * zatm_dev ;
unsigned long pos ;
u32 x ;
int error ;
EVENT ( " poll_rx \n " , 0 , 0 ) ;
zatm_dev = ZATM_DEV ( dev ) ;
pos = ( zatm_dev - > mbx_start [ mbx ] & ~ 0xffffUL ) | zin ( MTA ( mbx ) ) ;
while ( x = zin ( MWA ( mbx ) ) , ( pos & 0xffff ) ! = x ) {
u32 * here ;
struct sk_buff * skb ;
struct atm_vcc * vcc ;
int cells , size , chan ;
EVENT ( " MBX: host 0x%lx, nic 0x%x \n " , pos , x ) ;
here = ( u32 * ) pos ;
if ( ( ( pos + = 16 ) & 0xffff ) = = zatm_dev - > mbx_end [ mbx ] )
pos = zatm_dev - > mbx_start [ mbx ] ;
cells = here [ 0 ] & uPD98401_AAL5_SIZE ;
#if 0
printk ( " RX IND: 0x%x, 0x%x, 0x%x, 0x%x \n " , here [ 0 ] , here [ 1 ] , here [ 2 ] , here [ 3 ] ) ;
{
unsigned long * x ;
printk ( " POOL: 0x%08x, 0x%08x \n " , zpeekl ( zatm_dev ,
zatm_dev - > pool_base ) ,
zpeekl ( zatm_dev , zatm_dev - > pool_base + 1 ) ) ;
x = ( unsigned long * ) here [ 2 ] ;
printk ( " [0..3] = 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx \n " ,
x [ 0 ] , x [ 1 ] , x [ 2 ] , x [ 3 ] ) ;
}
# endif
error = 0 ;
if ( here [ 3 ] & uPD98401_AAL5_ERR ) {
error = ( here [ 3 ] & uPD98401_AAL5_ES ) > >
uPD98401_AAL5_ES_SHIFT ;
if ( error = = uPD98401_AAL5_ES_DEACT | |
error = = uPD98401_AAL5_ES_FREE ) continue ;
}
EVENT ( " error code 0x%x/0x%x \n " , ( here [ 3 ] & uPD98401_AAL5_ES ) > >
uPD98401_AAL5_ES_SHIFT , error ) ;
skb = ( ( struct rx_buffer_head * ) bus_to_virt ( here [ 2 ] ) ) - > skb ;
2005-08-15 04:24:31 +04:00
__net_timestamp ( skb ) ;
2005-04-17 02:20:36 +04:00
#if 0
printk ( " [-3..0] 0x%08lx 0x%08lx 0x%08lx 0x%08lx \n " , ( ( unsigned * ) skb - > data ) [ - 3 ] ,
( ( unsigned * ) skb - > data ) [ - 2 ] , ( ( unsigned * ) skb - > data ) [ - 1 ] ,
( ( unsigned * ) skb - > data ) [ 0 ] ) ;
# endif
EVENT ( " skb 0x%lx, here 0x%lx \n " , ( unsigned long ) skb ,
( unsigned long ) here ) ;
#if 0
printk ( " dummy: 0x%08lx, 0x%08lx \n " , dummy [ 0 ] , dummy [ 1 ] ) ;
# endif
size = error ? 0 : ntohs ( ( ( __be16 * ) skb - > data ) [ cells *
ATM_CELL_PAYLOAD / sizeof ( u16 ) - 3 ] ) ;
EVENT ( " got skb 0x%lx, size %d \n " , ( unsigned long ) skb , size ) ;
chan = ( here [ 3 ] & uPD98401_AAL5_CHAN ) > >
uPD98401_AAL5_CHAN_SHIFT ;
if ( chan < zatm_dev - > chans & & zatm_dev - > rx_map [ chan ] ) {
2005-09-02 23:18:03 +04:00
int pos ;
2005-04-17 02:20:36 +04:00
vcc = zatm_dev - > rx_map [ chan ] ;
2005-09-02 23:18:03 +04:00
pos = ZATM_VCC ( vcc ) - > pool ;
2005-08-10 06:25:21 +04:00
if ( skb = = zatm_dev - > last_free [ pos ] )
zatm_dev - > last_free [ pos ] = NULL ;
skb_unlink ( skb , zatm_dev - > pool + pos ) ;
2005-04-17 02:20:36 +04:00
}
else {
printk ( KERN_ERR DEV_LABEL " (itf %d): RX indication "
" for non-existing channel \n " , dev - > number ) ;
size = 0 ;
vcc = NULL ;
event_dump ( ) ;
}
if ( error ) {
static unsigned long silence = 0 ;
static int last_error = 0 ;
if ( error ! = last_error | |
time_after ( jiffies , silence ) | | silence = = 0 ) {
printk ( KERN_WARNING DEV_LABEL " (itf %d): "
" chan %d error %s \n " , dev - > number , chan ,
err_txt [ error ] ) ;
last_error = error ;
silence = ( jiffies + 2 * HZ ) | 1 ;
}
size = 0 ;
}
if ( size & & ( size > cells * ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER | |
size < = ( cells - 1 ) * ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER ) ) {
printk ( KERN_ERR DEV_LABEL " (itf %d): size %d with %d "
" cells \n " , dev - > number , size , cells ) ;
size = 0 ;
event_dump ( ) ;
}
if ( size > ATM_MAX_AAL5_PDU ) {
printk ( KERN_ERR DEV_LABEL " (itf %d): size too big "
" (%d) \n " , dev - > number , size ) ;
size = 0 ;
event_dump ( ) ;
}
if ( ! size ) {
dev_kfree_skb_irq ( skb ) ;
if ( vcc ) atomic_inc ( & vcc - > stats - > rx_err ) ;
continue ;
}
if ( ! atm_charge ( vcc , skb - > truesize ) ) {
dev_kfree_skb_irq ( skb ) ;
continue ;
}
skb - > len = size ;
ATM_SKB ( skb ) - > vcc = vcc ;
vcc - > push ( vcc , skb ) ;
atomic_inc ( & vcc - > stats - > rx ) ;
}
zout ( pos & 0xffff , MTA ( mbx ) ) ;
#if 0 /* probably a stupid idea */
refill_pool ( dev , zatm_vcc - > pool ) ;
/* maybe this saves us a few interrupts */
# endif
}
static int open_rx_first ( struct atm_vcc * vcc )
{
struct zatm_dev * zatm_dev ;
struct zatm_vcc * zatm_vcc ;
unsigned long flags ;
unsigned short chan ;
int cells ;
DPRINTK ( " open_rx_first (0x%x) \n " , inb_p ( 0xc053 ) ) ;
zatm_dev = ZATM_DEV ( vcc - > dev ) ;
zatm_vcc = ZATM_VCC ( vcc ) ;
zatm_vcc - > rx_chan = 0 ;
if ( vcc - > qos . rxtp . traffic_class = = ATM_NONE ) return 0 ;
if ( vcc - > qos . aal = = ATM_AAL5 ) {
if ( vcc - > qos . rxtp . max_sdu > 65464 )
vcc - > qos . rxtp . max_sdu = 65464 ;
/* fix this - we may want to receive 64kB SDUs
later */
cells = ( vcc - > qos . rxtp . max_sdu + ATM_AAL5_TRAILER +
ATM_CELL_PAYLOAD - 1 ) / ATM_CELL_PAYLOAD ;
zatm_vcc - > pool = pool_index ( cells * ATM_CELL_PAYLOAD ) ;
}
else {
cells = 1 ;
zatm_vcc - > pool = ZATM_AAL0_POOL ;
}
if ( zatm_vcc - > pool < 0 ) return - EMSGSIZE ;
spin_lock_irqsave ( & zatm_dev - > lock , flags ) ;
zwait ;
zout ( uPD98401_OPEN_CHAN , CMR ) ;
zwait ;
DPRINTK ( " 0x%x 0x%x \n " , zin ( CMR ) , zin ( CER ) ) ;
chan = ( zin ( CMR ) & uPD98401_CHAN_ADDR ) > > uPD98401_CHAN_ADDR_SHIFT ;
spin_unlock_irqrestore ( & zatm_dev - > lock , flags ) ;
DPRINTK ( " chan is %d \n " , chan ) ;
if ( ! chan ) return - EAGAIN ;
use_pool ( vcc - > dev , zatm_vcc - > pool ) ;
DPRINTK ( " pool %d \n " , zatm_vcc - > pool ) ;
/* set up VC descriptor */
spin_lock_irqsave ( & zatm_dev - > lock , flags ) ;
zpokel ( zatm_dev , zatm_vcc - > pool < < uPD98401_RXVC_POOL_SHIFT ,
chan * VC_SIZE / 4 ) ;
zpokel ( zatm_dev , uPD98401_RXVC_OD | ( vcc - > qos . aal = = ATM_AAL5 ?
uPD98401_RXVC_AR : 0 ) | cells , chan * VC_SIZE / 4 + 1 ) ;
zpokel ( zatm_dev , 0 , chan * VC_SIZE / 4 + 2 ) ;
zatm_vcc - > rx_chan = chan ;
zatm_dev - > rx_map [ chan ] = vcc ;
spin_unlock_irqrestore ( & zatm_dev - > lock , flags ) ;
return 0 ;
}
static int open_rx_second ( struct atm_vcc * vcc )
{
struct zatm_dev * zatm_dev ;
struct zatm_vcc * zatm_vcc ;
unsigned long flags ;
int pos , shift ;
DPRINTK ( " open_rx_second (0x%x) \n " , inb_p ( 0xc053 ) ) ;
zatm_dev = ZATM_DEV ( vcc - > dev ) ;
zatm_vcc = ZATM_VCC ( vcc ) ;
if ( ! zatm_vcc - > rx_chan ) return 0 ;
spin_lock_irqsave ( & zatm_dev - > lock , flags ) ;
/* should also handle VPI @@@ */
pos = vcc - > vci > > 1 ;
shift = ( 1 - ( vcc - > vci & 1 ) ) < < 4 ;
zpokel ( zatm_dev , ( zpeekl ( zatm_dev , pos ) & ~ ( 0xffff < < shift ) ) |
( ( zatm_vcc - > rx_chan | uPD98401_RXLT_ENBL ) < < shift ) , pos ) ;
spin_unlock_irqrestore ( & zatm_dev - > lock , flags ) ;
return 0 ;
}
static void close_rx ( struct atm_vcc * vcc )
{
struct zatm_dev * zatm_dev ;
struct zatm_vcc * zatm_vcc ;
unsigned long flags ;
int pos , shift ;
zatm_vcc = ZATM_VCC ( vcc ) ;
zatm_dev = ZATM_DEV ( vcc - > dev ) ;
if ( ! zatm_vcc - > rx_chan ) return ;
DPRINTK ( " close_rx \n " ) ;
/* disable receiver */
if ( vcc - > vpi ! = ATM_VPI_UNSPEC & & vcc - > vci ! = ATM_VCI_UNSPEC ) {
spin_lock_irqsave ( & zatm_dev - > lock , flags ) ;
pos = vcc - > vci > > 1 ;
shift = ( 1 - ( vcc - > vci & 1 ) ) < < 4 ;
zpokel ( zatm_dev , zpeekl ( zatm_dev , pos ) & ~ ( 0xffff < < shift ) , pos ) ;
zwait ;
zout ( uPD98401_NOP , CMR ) ;
zwait ;
zout ( uPD98401_NOP , CMR ) ;
spin_unlock_irqrestore ( & zatm_dev - > lock , flags ) ;
}
spin_lock_irqsave ( & zatm_dev - > lock , flags ) ;
zwait ;
zout ( uPD98401_DEACT_CHAN | uPD98401_CHAN_RT | ( zatm_vcc - > rx_chan < <
uPD98401_CHAN_ADDR_SHIFT ) , CMR ) ;
zwait ;
udelay ( 10 ) ; /* why oh why ... ? */
zout ( uPD98401_CLOSE_CHAN | uPD98401_CHAN_RT | ( zatm_vcc - > rx_chan < <
uPD98401_CHAN_ADDR_SHIFT ) , CMR ) ;
zwait ;
if ( ! ( zin ( CMR ) & uPD98401_CHAN_ADDR ) )
printk ( KERN_CRIT DEV_LABEL " (itf %d): can't close RX channel "
" %d \n " , vcc - > dev - > number , zatm_vcc - > rx_chan ) ;
spin_unlock_irqrestore ( & zatm_dev - > lock , flags ) ;
zatm_dev - > rx_map [ zatm_vcc - > rx_chan ] = NULL ;
zatm_vcc - > rx_chan = 0 ;
unuse_pool ( vcc - > dev , zatm_vcc - > pool ) ;
}
static int start_rx ( struct atm_dev * dev )
{
struct zatm_dev * zatm_dev ;
int size , i ;
DPRINTK ( " start_rx \n " ) ;
zatm_dev = ZATM_DEV ( dev ) ;
size = sizeof ( struct atm_vcc * ) * zatm_dev - > chans ;
zatm_dev - > rx_map = ( struct atm_vcc * * ) kmalloc ( size , GFP_KERNEL ) ;
if ( ! zatm_dev - > rx_map ) return - ENOMEM ;
memset ( zatm_dev - > rx_map , 0 , size ) ;
/* set VPI/VCI split (use all VCIs and give what's left to VPIs) */
zpokel ( zatm_dev , ( 1 < < dev - > ci_range . vci_bits ) - 1 , uPD98401_VRR ) ;
/* prepare free buffer pools */
for ( i = 0 ; i < = ZATM_LAST_POOL ; i + + ) {
zatm_dev - > pool_info [ i ] . ref_count = 0 ;
zatm_dev - > pool_info [ i ] . rqa_count = 0 ;
zatm_dev - > pool_info [ i ] . rqu_count = 0 ;
zatm_dev - > pool_info [ i ] . low_water = LOW_MARK ;
zatm_dev - > pool_info [ i ] . high_water = HIGH_MARK ;
zatm_dev - > pool_info [ i ] . offset = 0 ;
zatm_dev - > pool_info [ i ] . next_off = 0 ;
zatm_dev - > pool_info [ i ] . next_cnt = 0 ;
zatm_dev - > pool_info [ i ] . next_thres = OFF_CNG_THRES ;
}
return 0 ;
}
/*----------------------------------- TX ------------------------------------*/
static int do_tx ( struct sk_buff * skb )
{
struct atm_vcc * vcc ;
struct zatm_dev * zatm_dev ;
struct zatm_vcc * zatm_vcc ;
u32 * dsc ;
unsigned long flags ;
EVENT ( " do_tx \n " , 0 , 0 ) ;
DPRINTK ( " sending skb %p \n " , skb ) ;
vcc = ATM_SKB ( skb ) - > vcc ;
zatm_dev = ZATM_DEV ( vcc - > dev ) ;
zatm_vcc = ZATM_VCC ( vcc ) ;
EVENT ( " iovcnt=%d \n " , skb_shinfo ( skb ) - > nr_frags , 0 ) ;
spin_lock_irqsave ( & zatm_dev - > lock , flags ) ;
if ( ! skb_shinfo ( skb ) - > nr_frags ) {
if ( zatm_vcc - > txing = = RING_ENTRIES - 1 ) {
spin_unlock_irqrestore ( & zatm_dev - > lock , flags ) ;
return RING_BUSY ;
}
zatm_vcc - > txing + + ;
dsc = zatm_vcc - > ring + zatm_vcc - > ring_curr ;
zatm_vcc - > ring_curr = ( zatm_vcc - > ring_curr + RING_WORDS ) &
( RING_ENTRIES * RING_WORDS - 1 ) ;
dsc [ 1 ] = 0 ;
dsc [ 2 ] = skb - > len ;
dsc [ 3 ] = virt_to_bus ( skb - > data ) ;
mb ( ) ;
dsc [ 0 ] = uPD98401_TXPD_V | uPD98401_TXPD_DP | uPD98401_TXPD_SM
| ( vcc - > qos . aal = = ATM_AAL5 ? uPD98401_TXPD_AAL5 : 0 |
( ATM_SKB ( skb ) - > atm_options & ATM_ATMOPT_CLP ?
uPD98401_CLPM_1 : uPD98401_CLPM_0 ) ) ;
EVENT ( " dsc (0x%lx) \n " , ( unsigned long ) dsc , 0 ) ;
}
else {
printk ( " NONONONOO!!!! \n " ) ;
dsc = NULL ;
#if 0
u32 * put ;
int i ;
dsc = ( u32 * ) kmalloc ( uPD98401_TXPD_SIZE * 2 +
uPD98401_TXBD_SIZE * ATM_SKB ( skb ) - > iovcnt , GFP_ATOMIC ) ;
if ( ! dsc ) {
if ( vcc - > pop ) vcc - > pop ( vcc , skb ) ;
else dev_kfree_skb_irq ( skb ) ;
return - EAGAIN ;
}
/* @@@ should check alignment */
put = dsc + 8 ;
dsc [ 0 ] = uPD98401_TXPD_V | uPD98401_TXPD_DP |
( vcc - > aal = = ATM_AAL5 ? uPD98401_TXPD_AAL5 : 0 |
( ATM_SKB ( skb ) - > atm_options & ATM_ATMOPT_CLP ?
uPD98401_CLPM_1 : uPD98401_CLPM_0 ) ) ;
dsc [ 1 ] = 0 ;
dsc [ 2 ] = ATM_SKB ( skb ) - > iovcnt * uPD98401_TXBD_SIZE ;
dsc [ 3 ] = virt_to_bus ( put ) ;
for ( i = 0 ; i < ATM_SKB ( skb ) - > iovcnt ; i + + ) {
* put + + = ( ( struct iovec * ) skb - > data ) [ i ] . iov_len ;
* put + + = virt_to_bus ( ( ( struct iovec * )
skb - > data ) [ i ] . iov_base ) ;
}
put [ - 2 ] | = uPD98401_TXBD_LAST ;
# endif
}
ZATM_PRV_DSC ( skb ) = dsc ;
skb_queue_tail ( & zatm_vcc - > tx_queue , skb ) ;
DPRINTK ( " QRP=0x%08lx \n " , zpeekl ( zatm_dev , zatm_vcc - > tx_chan * VC_SIZE / 4 +
uPD98401_TXVC_QRP ) ) ;
zwait ;
zout ( uPD98401_TX_READY | ( zatm_vcc - > tx_chan < <
uPD98401_CHAN_ADDR_SHIFT ) , CMR ) ;
spin_unlock_irqrestore ( & zatm_dev - > lock , flags ) ;
EVENT ( " done \n " , 0 , 0 ) ;
return 0 ;
}
static inline void dequeue_tx ( struct atm_vcc * vcc )
{
struct zatm_vcc * zatm_vcc ;
struct sk_buff * skb ;
EVENT ( " dequeue_tx \n " , 0 , 0 ) ;
zatm_vcc = ZATM_VCC ( vcc ) ;
skb = skb_dequeue ( & zatm_vcc - > tx_queue ) ;
if ( ! skb ) {
printk ( KERN_CRIT DEV_LABEL " (itf %d): dequeue_tx but not "
" txing \n " , vcc - > dev - > number ) ;
return ;
}
#if 0 /* @@@ would fail on CLP */
if ( * ZATM_PRV_DSC ( skb ) ! = ( uPD98401_TXPD_V | uPD98401_TXPD_DP |
uPD98401_TXPD_SM | uPD98401_TXPD_AAL5 ) ) printk ( " @#*$!!!! (%08x) \n " ,
* ZATM_PRV_DSC ( skb ) ) ;
# endif
* ZATM_PRV_DSC ( skb ) = 0 ; /* mark as invalid */
zatm_vcc - > txing - - ;
if ( vcc - > pop ) vcc - > pop ( vcc , skb ) ;
else dev_kfree_skb_irq ( skb ) ;
while ( ( skb = skb_dequeue ( & zatm_vcc - > backlog ) ) )
if ( do_tx ( skb ) = = RING_BUSY ) {
skb_queue_head ( & zatm_vcc - > backlog , skb ) ;
break ;
}
atomic_inc ( & vcc - > stats - > tx ) ;
wake_up ( & zatm_vcc - > tx_wait ) ;
}
static void poll_tx ( struct atm_dev * dev , int mbx )
{
struct zatm_dev * zatm_dev ;
unsigned long pos ;
u32 x ;
EVENT ( " poll_tx \n " , 0 , 0 ) ;
zatm_dev = ZATM_DEV ( dev ) ;
pos = ( zatm_dev - > mbx_start [ mbx ] & ~ 0xffffUL ) | zin ( MTA ( mbx ) ) ;
while ( x = zin ( MWA ( mbx ) ) , ( pos & 0xffff ) ! = x ) {
int chan ;
# if 1
u32 data , * addr ;
EVENT ( " MBX: host 0x%lx, nic 0x%x \n " , pos , x ) ;
addr = ( u32 * ) pos ;
data = * addr ;
chan = ( data & uPD98401_TXI_CONN ) > > uPD98401_TXI_CONN_SHIFT ;
EVENT ( " addr = 0x%lx, data = 0x%08x, " , ( unsigned long ) addr ,
data ) ;
EVENT ( " chan = %d \n " , chan , 0 ) ;
# else
NO !
chan = ( zatm_dev - > mbx_start [ mbx ] [ pos > > 2 ] & uPD98401_TXI_CONN )
> > uPD98401_TXI_CONN_SHIFT ;
# endif
if ( chan < zatm_dev - > chans & & zatm_dev - > tx_map [ chan ] )
dequeue_tx ( zatm_dev - > tx_map [ chan ] ) ;
else {
printk ( KERN_CRIT DEV_LABEL " (itf %d): TX indication "
" for non-existing channel %d \n " , dev - > number , chan ) ;
event_dump ( ) ;
}
if ( ( ( pos + = 4 ) & 0xffff ) = = zatm_dev - > mbx_end [ mbx ] )
pos = zatm_dev - > mbx_start [ mbx ] ;
}
zout ( pos & 0xffff , MTA ( mbx ) ) ;
}
/*
* BUG BUG BUG : Doesn ' t handle " new-style " rate specification yet .
*/
static int alloc_shaper ( struct atm_dev * dev , int * pcr , int min , int max , int ubr )
{
struct zatm_dev * zatm_dev ;
unsigned long flags ;
unsigned long i , m , c ;
int shaper ;
DPRINTK ( " alloc_shaper (min = %d, max = %d) \n " , min , max ) ;
zatm_dev = ZATM_DEV ( dev ) ;
if ( ! zatm_dev - > free_shapers ) return - EAGAIN ;
for ( shaper = 0 ; ! ( ( zatm_dev - > free_shapers > > shaper ) & 1 ) ; shaper + + ) ;
zatm_dev - > free_shapers & = ~ 1 < < shaper ;
if ( ubr ) {
c = 5 ;
i = m = 1 ;
zatm_dev - > ubr_ref_cnt + + ;
zatm_dev - > ubr = shaper ;
}
else {
if ( min ) {
if ( min < = 255 ) {
i = min ;
m = ATM_OC3_PCR ;
}
else {
i = 255 ;
m = ATM_OC3_PCR * 255 / min ;
}
}
else {
if ( max > zatm_dev - > tx_bw ) max = zatm_dev - > tx_bw ;
if ( max < = 255 ) {
i = max ;
m = ATM_OC3_PCR ;
}
else {
i = 255 ;
m = ( ATM_OC3_PCR * 255 + max - 1 ) / max ;
}
}
if ( i > m ) {
printk ( KERN_CRIT DEV_LABEL " shaper algorithm botched "
" [%d,%d] -> i=%ld,m=%ld \n " , min , max , i , m ) ;
m = i ;
}
* pcr = i * ATM_OC3_PCR / m ;
c = 20 ; /* @@@ should use max_cdv ! */
if ( ( min & & * pcr < min ) | | ( max & & * pcr > max ) ) return - EINVAL ;
if ( zatm_dev - > tx_bw < * pcr ) return - EAGAIN ;
zatm_dev - > tx_bw - = * pcr ;
}
spin_lock_irqsave ( & zatm_dev - > lock , flags ) ;
DPRINTK ( " i = %d, m = %d, PCR = %d \n " , i , m , * pcr ) ;
zpokel ( zatm_dev , ( i < < uPD98401_IM_I_SHIFT ) | m , uPD98401_IM ( shaper ) ) ;
zpokel ( zatm_dev , c < < uPD98401_PC_C_SHIFT , uPD98401_PC ( shaper ) ) ;
zpokel ( zatm_dev , 0 , uPD98401_X ( shaper ) ) ;
zpokel ( zatm_dev , 0 , uPD98401_Y ( shaper ) ) ;
zpokel ( zatm_dev , uPD98401_PS_E , uPD98401_PS ( shaper ) ) ;
spin_unlock_irqrestore ( & zatm_dev - > lock , flags ) ;
return shaper ;
}
static void dealloc_shaper ( struct atm_dev * dev , int shaper )
{
struct zatm_dev * zatm_dev ;
unsigned long flags ;
zatm_dev = ZATM_DEV ( dev ) ;
if ( shaper = = zatm_dev - > ubr ) {
if ( - - zatm_dev - > ubr_ref_cnt ) return ;
zatm_dev - > ubr = - 1 ;
}
spin_lock_irqsave ( & zatm_dev - > lock , flags ) ;
zpokel ( zatm_dev , zpeekl ( zatm_dev , uPD98401_PS ( shaper ) ) & ~ uPD98401_PS_E ,
uPD98401_PS ( shaper ) ) ;
spin_unlock_irqrestore ( & zatm_dev - > lock , flags ) ;
zatm_dev - > free_shapers | = 1 < < shaper ;
}
static void close_tx ( struct atm_vcc * vcc )
{
struct zatm_dev * zatm_dev ;
struct zatm_vcc * zatm_vcc ;
unsigned long flags ;
int chan ;
zatm_vcc = ZATM_VCC ( vcc ) ;
zatm_dev = ZATM_DEV ( vcc - > dev ) ;
chan = zatm_vcc - > tx_chan ;
if ( ! chan ) return ;
DPRINTK ( " close_tx \n " ) ;
if ( skb_peek ( & zatm_vcc - > backlog ) ) {
printk ( " waiting for backlog to drain ... \n " ) ;
event_dump ( ) ;
wait_event ( zatm_vcc - > tx_wait , ! skb_peek ( & zatm_vcc - > backlog ) ) ;
}
if ( skb_peek ( & zatm_vcc - > tx_queue ) ) {
printk ( " waiting for TX queue to drain ... \n " ) ;
event_dump ( ) ;
wait_event ( zatm_vcc - > tx_wait , ! skb_peek ( & zatm_vcc - > tx_queue ) ) ;
}
spin_lock_irqsave ( & zatm_dev - > lock , flags ) ;
#if 0
zwait ;
zout ( uPD98401_DEACT_CHAN | ( chan < < uPD98401_CHAN_ADDR_SHIFT ) , CMR ) ;
# endif
zwait ;
zout ( uPD98401_CLOSE_CHAN | ( chan < < uPD98401_CHAN_ADDR_SHIFT ) , CMR ) ;
zwait ;
if ( ! ( zin ( CMR ) & uPD98401_CHAN_ADDR ) )
printk ( KERN_CRIT DEV_LABEL " (itf %d): can't close TX channel "
" %d \n " , vcc - > dev - > number , chan ) ;
spin_unlock_irqrestore ( & zatm_dev - > lock , flags ) ;
zatm_vcc - > tx_chan = 0 ;
zatm_dev - > tx_map [ chan ] = NULL ;
if ( zatm_vcc - > shaper ! = zatm_dev - > ubr ) {
zatm_dev - > tx_bw + = vcc - > qos . txtp . min_pcr ;
dealloc_shaper ( vcc - > dev , zatm_vcc - > shaper ) ;
}
2005-06-03 00:04:07 +04:00
kfree ( zatm_vcc - > ring ) ;
2005-04-17 02:20:36 +04:00
}
static int open_tx_first ( struct atm_vcc * vcc )
{
struct zatm_dev * zatm_dev ;
struct zatm_vcc * zatm_vcc ;
unsigned long flags ;
u32 * loop ;
unsigned short chan ;
int pcr , unlimited ;
DPRINTK ( " open_tx_first \n " ) ;
zatm_dev = ZATM_DEV ( vcc - > dev ) ;
zatm_vcc = ZATM_VCC ( vcc ) ;
zatm_vcc - > tx_chan = 0 ;
if ( vcc - > qos . txtp . traffic_class = = ATM_NONE ) return 0 ;
spin_lock_irqsave ( & zatm_dev - > lock , flags ) ;
zwait ;
zout ( uPD98401_OPEN_CHAN , CMR ) ;
zwait ;
DPRINTK ( " 0x%x 0x%x \n " , zin ( CMR ) , zin ( CER ) ) ;
chan = ( zin ( CMR ) & uPD98401_CHAN_ADDR ) > > uPD98401_CHAN_ADDR_SHIFT ;
spin_unlock_irqrestore ( & zatm_dev - > lock , flags ) ;
DPRINTK ( " chan is %d \n " , chan ) ;
if ( ! chan ) return - EAGAIN ;
unlimited = vcc - > qos . txtp . traffic_class = = ATM_UBR & &
( ! vcc - > qos . txtp . max_pcr | | vcc - > qos . txtp . max_pcr = = ATM_MAX_PCR | |
vcc - > qos . txtp . max_pcr > = ATM_OC3_PCR ) ;
if ( unlimited & & zatm_dev - > ubr ! = - 1 ) zatm_vcc - > shaper = zatm_dev - > ubr ;
else {
if ( unlimited ) vcc - > qos . txtp . max_sdu = ATM_MAX_AAL5_PDU ;
if ( ( zatm_vcc - > shaper = alloc_shaper ( vcc - > dev , & pcr ,
vcc - > qos . txtp . min_pcr , vcc - > qos . txtp . max_pcr , unlimited ) )
< 0 ) {
close_tx ( vcc ) ;
return zatm_vcc - > shaper ;
}
if ( pcr > ATM_OC3_PCR ) pcr = ATM_OC3_PCR ;
vcc - > qos . txtp . min_pcr = vcc - > qos . txtp . max_pcr = pcr ;
}
zatm_vcc - > tx_chan = chan ;
skb_queue_head_init ( & zatm_vcc - > tx_queue ) ;
init_waitqueue_head ( & zatm_vcc - > tx_wait ) ;
/* initialize ring */
zatm_vcc - > ring = kmalloc ( RING_SIZE , GFP_KERNEL ) ;
if ( ! zatm_vcc - > ring ) return - ENOMEM ;
memset ( zatm_vcc - > ring , 0 , RING_SIZE ) ;
loop = zatm_vcc - > ring + RING_ENTRIES * RING_WORDS ;
loop [ 0 ] = uPD98401_TXPD_V ;
loop [ 1 ] = loop [ 2 ] = 0 ;
loop [ 3 ] = virt_to_bus ( zatm_vcc - > ring ) ;
zatm_vcc - > ring_curr = 0 ;
zatm_vcc - > txing = 0 ;
skb_queue_head_init ( & zatm_vcc - > backlog ) ;
zpokel ( zatm_dev , virt_to_bus ( zatm_vcc - > ring ) ,
chan * VC_SIZE / 4 + uPD98401_TXVC_QRP ) ;
return 0 ;
}
static int open_tx_second ( struct atm_vcc * vcc )
{
struct zatm_dev * zatm_dev ;
struct zatm_vcc * zatm_vcc ;
unsigned long flags ;
DPRINTK ( " open_tx_second \n " ) ;
zatm_dev = ZATM_DEV ( vcc - > dev ) ;
zatm_vcc = ZATM_VCC ( vcc ) ;
if ( ! zatm_vcc - > tx_chan ) return 0 ;
/* set up VC descriptor */
spin_lock_irqsave ( & zatm_dev - > lock , flags ) ;
zpokel ( zatm_dev , 0 , zatm_vcc - > tx_chan * VC_SIZE / 4 ) ;
zpokel ( zatm_dev , uPD98401_TXVC_L | ( zatm_vcc - > shaper < <
uPD98401_TXVC_SHP_SHIFT ) | ( vcc - > vpi < < uPD98401_TXVC_VPI_SHIFT ) |
vcc - > vci , zatm_vcc - > tx_chan * VC_SIZE / 4 + 1 ) ;
zpokel ( zatm_dev , 0 , zatm_vcc - > tx_chan * VC_SIZE / 4 + 2 ) ;
spin_unlock_irqrestore ( & zatm_dev - > lock , flags ) ;
zatm_dev - > tx_map [ zatm_vcc - > tx_chan ] = vcc ;
return 0 ;
}
static int start_tx ( struct atm_dev * dev )
{
struct zatm_dev * zatm_dev ;
int i ;
DPRINTK ( " start_tx \n " ) ;
zatm_dev = ZATM_DEV ( dev ) ;
zatm_dev - > tx_map = ( struct atm_vcc * * ) kmalloc ( sizeof ( struct atm_vcc * ) *
zatm_dev - > chans , GFP_KERNEL ) ;
if ( ! zatm_dev - > tx_map ) return - ENOMEM ;
zatm_dev - > tx_bw = ATM_OC3_PCR ;
zatm_dev - > free_shapers = ( 1 < < NR_SHAPERS ) - 1 ;
zatm_dev - > ubr = - 1 ;
zatm_dev - > ubr_ref_cnt = 0 ;
/* initialize shapers */
for ( i = 0 ; i < NR_SHAPERS ; i + + ) zpokel ( zatm_dev , 0 , uPD98401_PS ( i ) ) ;
return 0 ;
}
/*------------------------------- interrupts --------------------------------*/
static irqreturn_t zatm_int ( int irq , void * dev_id , struct pt_regs * regs )
{
struct atm_dev * dev ;
struct zatm_dev * zatm_dev ;
u32 reason ;
int handled = 0 ;
dev = dev_id ;
zatm_dev = ZATM_DEV ( dev ) ;
while ( ( reason = zin ( GSR ) ) ) {
handled = 1 ;
EVENT ( " reason 0x%x \n " , reason , 0 ) ;
if ( reason & uPD98401_INT_PI ) {
EVENT ( " PHY int \n " , 0 , 0 ) ;
dev - > phy - > interrupt ( dev ) ;
}
if ( reason & uPD98401_INT_RQA ) {
unsigned long pools ;
int i ;
pools = zin ( RQA ) ;
EVENT ( " RQA (0x%08x) \n " , pools , 0 ) ;
for ( i = 0 ; pools ; i + + ) {
if ( pools & 1 ) {
refill_pool ( dev , i ) ;
zatm_dev - > pool_info [ i ] . rqa_count + + ;
}
pools > > = 1 ;
}
}
if ( reason & uPD98401_INT_RQU ) {
unsigned long pools ;
int i ;
pools = zin ( RQU ) ;
printk ( KERN_WARNING DEV_LABEL " (itf %d): RQU 0x%08lx \n " ,
dev - > number , pools ) ;
event_dump ( ) ;
for ( i = 0 ; pools ; i + + ) {
if ( pools & 1 ) {
refill_pool ( dev , i ) ;
zatm_dev - > pool_info [ i ] . rqu_count + + ;
}
pools > > = 1 ;
}
}
/* don't handle RD */
if ( reason & uPD98401_INT_SPE )
printk ( KERN_ALERT DEV_LABEL " (itf %d): system parity "
" error at 0x%08x \n " , dev - > number , zin ( ADDR ) ) ;
if ( reason & uPD98401_INT_CPE )
printk ( KERN_ALERT DEV_LABEL " (itf %d): control memory "
" parity error at 0x%08x \n " , dev - > number , zin ( ADDR ) ) ;
if ( reason & uPD98401_INT_SBE ) {
printk ( KERN_ALERT DEV_LABEL " (itf %d): system bus "
" error at 0x%08x \n " , dev - > number , zin ( ADDR ) ) ;
event_dump ( ) ;
}
/* don't handle IND */
if ( reason & uPD98401_INT_MF ) {
printk ( KERN_CRIT DEV_LABEL " (itf %d): mailbox full "
" (0x%x) \n " , dev - > number , ( reason & uPD98401_INT_MF )
> > uPD98401_INT_MF_SHIFT ) ;
event_dump ( ) ;
/* @@@ should try to recover */
}
if ( reason & uPD98401_INT_MM ) {
if ( reason & 1 ) poll_rx ( dev , 0 ) ;
if ( reason & 2 ) poll_rx ( dev , 1 ) ;
if ( reason & 4 ) poll_tx ( dev , 2 ) ;
if ( reason & 8 ) poll_tx ( dev , 3 ) ;
}
/* @@@ handle RCRn */
}
return IRQ_RETVAL ( handled ) ;
}
/*----------------------------- (E)EPROM access -----------------------------*/
static void __devinit eprom_set ( struct zatm_dev * zatm_dev , unsigned long value ,
unsigned short cmd )
{
int error ;
if ( ( error = pci_write_config_dword ( zatm_dev - > pci_dev , cmd , value ) ) )
printk ( KERN_ERR DEV_LABEL " : PCI write failed (0x%02x) \n " ,
error ) ;
}
static unsigned long __devinit eprom_get ( struct zatm_dev * zatm_dev ,
unsigned short cmd )
{
unsigned int value ;
int error ;
if ( ( error = pci_read_config_dword ( zatm_dev - > pci_dev , cmd , & value ) ) )
printk ( KERN_ERR DEV_LABEL " : PCI read failed (0x%02x) \n " ,
error ) ;
return value ;
}
static void __devinit eprom_put_bits ( struct zatm_dev * zatm_dev ,
unsigned long data , int bits , unsigned short cmd )
{
unsigned long value ;
int i ;
for ( i = bits - 1 ; i > = 0 ; i - - ) {
value = ZEPROM_CS | ( ( ( data > > i ) & 1 ) ? ZEPROM_DI : 0 ) ;
eprom_set ( zatm_dev , value , cmd ) ;
eprom_set ( zatm_dev , value | ZEPROM_SK , cmd ) ;
eprom_set ( zatm_dev , value , cmd ) ;
}
}
static void __devinit eprom_get_byte ( struct zatm_dev * zatm_dev ,
unsigned char * byte , unsigned short cmd )
{
int i ;
* byte = 0 ;
for ( i = 8 ; i ; i - - ) {
eprom_set ( zatm_dev , ZEPROM_CS , cmd ) ;
eprom_set ( zatm_dev , ZEPROM_CS | ZEPROM_SK , cmd ) ;
* byte < < = 1 ;
if ( eprom_get ( zatm_dev , cmd ) & ZEPROM_DO ) * byte | = 1 ;
eprom_set ( zatm_dev , ZEPROM_CS , cmd ) ;
}
}
static unsigned char __devinit eprom_try_esi ( struct atm_dev * dev ,
unsigned short cmd , int offset , int swap )
{
unsigned char buf [ ZEPROM_SIZE ] ;
struct zatm_dev * zatm_dev ;
int i ;
zatm_dev = ZATM_DEV ( dev ) ;
for ( i = 0 ; i < ZEPROM_SIZE ; i + = 2 ) {
eprom_set ( zatm_dev , ZEPROM_CS , cmd ) ; /* select EPROM */
eprom_put_bits ( zatm_dev , ZEPROM_CMD_READ , ZEPROM_CMD_LEN , cmd ) ;
eprom_put_bits ( zatm_dev , i > > 1 , ZEPROM_ADDR_LEN , cmd ) ;
eprom_get_byte ( zatm_dev , buf + i + swap , cmd ) ;
eprom_get_byte ( zatm_dev , buf + i + 1 - swap , cmd ) ;
eprom_set ( zatm_dev , 0 , cmd ) ; /* deselect EPROM */
}
memcpy ( dev - > esi , buf + offset , ESI_LEN ) ;
return memcmp ( dev - > esi , " \0 \0 \0 \0 \0 " , ESI_LEN ) ; /* assumes ESI_LEN == 6 */
}
static void __devinit eprom_get_esi ( struct atm_dev * dev )
{
if ( eprom_try_esi ( dev , ZEPROM_V1_REG , ZEPROM_V1_ESI_OFF , 1 ) ) return ;
( void ) eprom_try_esi ( dev , ZEPROM_V2_REG , ZEPROM_V2_ESI_OFF , 0 ) ;
}
/*--------------------------------- entries ---------------------------------*/
static int __init zatm_init ( struct atm_dev * dev )
{
struct zatm_dev * zatm_dev ;
struct pci_dev * pci_dev ;
unsigned short command ;
unsigned char revision ;
int error , i , last ;
unsigned long t0 , t1 , t2 ;
DPRINTK ( " >zatm_init \n " ) ;
zatm_dev = ZATM_DEV ( dev ) ;
spin_lock_init ( & zatm_dev - > lock ) ;
pci_dev = zatm_dev - > pci_dev ;
zatm_dev - > base = pci_resource_start ( pci_dev , 0 ) ;
zatm_dev - > irq = pci_dev - > irq ;
if ( ( error = pci_read_config_word ( pci_dev , PCI_COMMAND , & command ) ) | |
( error = pci_read_config_byte ( pci_dev , PCI_REVISION_ID , & revision ) ) ) {
printk ( KERN_ERR DEV_LABEL " (itf %d): init error 0x%02x \n " ,
dev - > number , error ) ;
return - EINVAL ;
}
if ( ( error = pci_write_config_word ( pci_dev , PCI_COMMAND ,
command | PCI_COMMAND_IO | PCI_COMMAND_MASTER ) ) ) {
printk ( KERN_ERR DEV_LABEL " (itf %d): can't enable IO (0x%02x) "
" \n " , dev - > number , error ) ;
return - EIO ;
}
eprom_get_esi ( dev ) ;
printk ( KERN_NOTICE DEV_LABEL " (itf %d): rev.%d,base=0x%x,irq=%d, " ,
dev - > number , revision , zatm_dev - > base , zatm_dev - > irq ) ;
/* reset uPD98401 */
zout ( 0 , SWR ) ;
while ( ! ( zin ( GSR ) & uPD98401_INT_IND ) ) ;
zout ( uPD98401_GMR_ONE /*uPD98401_BURST4*/ , GMR ) ;
last = MAX_CRAM_SIZE ;
for ( i = last - RAM_INCREMENT ; i > = 0 ; i - = RAM_INCREMENT ) {
zpokel ( zatm_dev , 0x55555555 , i ) ;
if ( zpeekl ( zatm_dev , i ) ! = 0x55555555 ) last = i ;
else {
zpokel ( zatm_dev , 0xAAAAAAAA , i ) ;
if ( zpeekl ( zatm_dev , i ) ! = 0xAAAAAAAA ) last = i ;
else zpokel ( zatm_dev , i , i ) ;
}
}
for ( i = 0 ; i < last ; i + = RAM_INCREMENT )
if ( zpeekl ( zatm_dev , i ) ! = i ) break ;
zatm_dev - > mem = i < < 2 ;
while ( i ) zpokel ( zatm_dev , 0 , - - i ) ;
/* reset again to rebuild memory pointers */
zout ( 0 , SWR ) ;
while ( ! ( zin ( GSR ) & uPD98401_INT_IND ) ) ;
zout ( uPD98401_GMR_ONE | uPD98401_BURST8 | uPD98401_BURST4 |
uPD98401_BURST2 | uPD98401_GMR_PM | uPD98401_GMR_DR , GMR ) ;
/* TODO: should shrink allocation now */
printk ( " mem=%dkB,%s ( " , zatm_dev - > mem > > 10 , zatm_dev - > copper ? " UTP " :
" MMF " ) ;
for ( i = 0 ; i < ESI_LEN ; i + + )
printk ( " %02X%s " , dev - > esi [ i ] , i = = ESI_LEN - 1 ? " ) \n " : " - " ) ;
do {
unsigned long flags ;
spin_lock_irqsave ( & zatm_dev - > lock , flags ) ;
t0 = zpeekl ( zatm_dev , uPD98401_TSR ) ;
udelay ( 10 ) ;
t1 = zpeekl ( zatm_dev , uPD98401_TSR ) ;
udelay ( 1010 ) ;
t2 = zpeekl ( zatm_dev , uPD98401_TSR ) ;
spin_unlock_irqrestore ( & zatm_dev - > lock , flags ) ;
}
while ( t0 > t1 | | t1 > t2 ) ; /* loop if wrapping ... */
zatm_dev - > khz = t2 - 2 * t1 + t0 ;
printk ( KERN_NOTICE DEV_LABEL " (itf %d): uPD98401 %d.%d at %d.%03d "
" MHz \n " , dev - > number ,
( zin ( VER ) & uPD98401_MAJOR ) > > uPD98401_MAJOR_SHIFT ,
zin ( VER ) & uPD98401_MINOR , zatm_dev - > khz / 1000 , zatm_dev - > khz % 1000 ) ;
return uPD98402_init ( dev ) ;
}
static int __init zatm_start ( struct atm_dev * dev )
{
2005-07-20 23:01:46 +04:00
struct zatm_dev * zatm_dev = ZATM_DEV ( dev ) ;
struct pci_dev * pdev = zatm_dev - > pci_dev ;
2005-04-17 02:20:36 +04:00
unsigned long curr ;
int pools , vccs , rx ;
2005-07-20 23:01:46 +04:00
int error , i , ld ;
2005-04-17 02:20:36 +04:00
DPRINTK ( " zatm_start \n " ) ;
zatm_dev - > rx_map = zatm_dev - > tx_map = NULL ;
2005-07-20 23:01:46 +04:00
for ( i = 0 ; i < NR_MBX ; i + + )
zatm_dev - > mbx_start [ i ] = 0 ;
error = request_irq ( zatm_dev - > irq , zatm_int , SA_SHIRQ , DEV_LABEL , dev ) ;
if ( error < 0 ) {
printk ( KERN_ERR DEV_LABEL " (itf %d): IRQ%d is already in use \n " ,
dev - > number , zatm_dev - > irq ) ;
goto done ;
2005-04-17 02:20:36 +04:00
}
/* define memory regions */
pools = NR_POOLS ;
if ( NR_SHAPERS * SHAPER_SIZE > pools * POOL_SIZE )
pools = NR_SHAPERS * SHAPER_SIZE / POOL_SIZE ;
vccs = ( zatm_dev - > mem - NR_SHAPERS * SHAPER_SIZE - pools * POOL_SIZE ) /
( 2 * VC_SIZE + RX_SIZE ) ;
ld = - 1 ;
for ( rx = 1 ; rx < vccs ; rx < < = 1 ) ld + + ;
dev - > ci_range . vpi_bits = 0 ; /* @@@ no VPI for now */
dev - > ci_range . vci_bits = ld ;
dev - > link_rate = ATM_OC3_PCR ;
zatm_dev - > chans = vccs ; /* ??? */
curr = rx * RX_SIZE / 4 ;
DPRINTK ( " RX pool 0x%08lx \n " , curr ) ;
zpokel ( zatm_dev , curr , uPD98401_PMA ) ; /* receive pool */
zatm_dev - > pool_base = curr ;
curr + = pools * POOL_SIZE / 4 ;
DPRINTK ( " Shapers 0x%08lx \n " , curr ) ;
zpokel ( zatm_dev , curr , uPD98401_SMA ) ; /* shapers */
curr + = NR_SHAPERS * SHAPER_SIZE / 4 ;
DPRINTK ( " Free 0x%08lx \n " , curr ) ;
zpokel ( zatm_dev , curr , uPD98401_TOS ) ; /* free pool */
printk ( KERN_INFO DEV_LABEL " (itf %d): %d shapers, %d pools, %d RX, "
" %ld VCs \n " , dev - > number , NR_SHAPERS , pools , rx ,
( zatm_dev - > mem - curr * 4 ) / VC_SIZE ) ;
/* create mailboxes */
2005-07-20 23:01:46 +04:00
for ( i = 0 ; i < NR_MBX ; i + + ) {
void * mbx ;
dma_addr_t mbx_dma ;
if ( ! mbx_entries [ i ] )
continue ;
mbx = pci_alloc_consistent ( pdev , 2 * MBX_SIZE ( i ) , & mbx_dma ) ;
if ( ! mbx ) {
error = - ENOMEM ;
goto out ;
}
/*
* Alignment provided by pci_alloc_consistent ( ) isn ' t enough
* for this device .
*/
if ( ( ( unsigned long ) mbx ^ mbx_dma ) & 0xffff ) {
printk ( KERN_ERR DEV_LABEL " (itf %d): system "
" bus incompatible with driver \n " , dev - > number ) ;
pci_free_consistent ( pdev , 2 * MBX_SIZE ( i ) , mbx , mbx_dma ) ;
error = - ENODEV ;
goto out ;
2005-04-17 02:20:36 +04:00
}
2005-07-20 23:01:46 +04:00
DPRINTK ( " mbx@0x%08lx-0x%08lx \n " , mbx , mbx + MBX_SIZE ( i ) ) ;
zatm_dev - > mbx_start [ i ] = ( unsigned long ) mbx ;
zatm_dev - > mbx_dma [ i ] = mbx_dma ;
zatm_dev - > mbx_end [ i ] = ( zatm_dev - > mbx_start [ i ] + MBX_SIZE ( i ) ) &
0xffff ;
zout ( mbx_dma > > 16 , MSH ( i ) ) ;
zout ( mbx_dma , MSL ( i ) ) ;
zout ( zatm_dev - > mbx_end [ i ] , MBA ( i ) ) ;
zout ( ( unsigned long ) mbx & 0xffff , MTA ( i ) ) ;
zout ( ( unsigned long ) mbx & 0xffff , MWA ( i ) ) ;
}
2005-04-17 02:20:36 +04:00
error = start_tx ( dev ) ;
2005-07-20 23:01:46 +04:00
if ( error )
goto out ;
2005-04-17 02:20:36 +04:00
error = start_rx ( dev ) ;
2005-07-20 23:01:46 +04:00
if ( error )
goto out_tx ;
2005-04-17 02:20:36 +04:00
error = dev - > phy - > start ( dev ) ;
2005-07-20 23:01:46 +04:00
if ( error )
goto out_rx ;
2005-04-17 02:20:36 +04:00
zout ( 0xffffffff , IMR ) ; /* enable interrupts */
/* enable TX & RX */
zout ( zin ( GMR ) | uPD98401_GMR_SE | uPD98401_GMR_RE , GMR ) ;
2005-07-20 23:01:46 +04:00
done :
return error ;
out_rx :
2005-06-03 00:04:07 +04:00
kfree ( zatm_dev - > rx_map ) ;
2005-07-20 23:01:46 +04:00
out_tx :
2005-06-03 00:04:07 +04:00
kfree ( zatm_dev - > tx_map ) ;
2005-07-20 23:01:46 +04:00
out :
while ( i - - > 0 ) {
pci_free_consistent ( pdev , 2 * MBX_SIZE ( i ) ,
( void * ) zatm_dev - > mbx_start [ i ] ,
zatm_dev - > mbx_dma [ i ] ) ;
}
2005-04-17 02:20:36 +04:00
free_irq ( zatm_dev - > irq , dev ) ;
2005-07-20 23:01:46 +04:00
goto done ;
2005-04-17 02:20:36 +04:00
}
static void zatm_close ( struct atm_vcc * vcc )
{
DPRINTK ( " >zatm_close \n " ) ;
if ( ! ZATM_VCC ( vcc ) ) return ;
clear_bit ( ATM_VF_READY , & vcc - > flags ) ;
close_rx ( vcc ) ;
EVENT ( " close_tx \n " , 0 , 0 ) ;
close_tx ( vcc ) ;
DPRINTK ( " zatm_close: done waiting \n " ) ;
/* deallocate memory */
kfree ( ZATM_VCC ( vcc ) ) ;
vcc - > dev_data = NULL ;
clear_bit ( ATM_VF_ADDR , & vcc - > flags ) ;
}
static int zatm_open ( struct atm_vcc * vcc )
{
struct zatm_dev * zatm_dev ;
struct zatm_vcc * zatm_vcc ;
short vpi = vcc - > vpi ;
int vci = vcc - > vci ;
int error ;
DPRINTK ( " >zatm_open \n " ) ;
zatm_dev = ZATM_DEV ( vcc - > dev ) ;
if ( ! test_bit ( ATM_VF_PARTIAL , & vcc - > flags ) )
vcc - > dev_data = NULL ;
if ( vci ! = ATM_VPI_UNSPEC & & vpi ! = ATM_VCI_UNSPEC )
set_bit ( ATM_VF_ADDR , & vcc - > flags ) ;
if ( vcc - > qos . aal ! = ATM_AAL5 ) return - EINVAL ; /* @@@ AAL0 */
DPRINTK ( DEV_LABEL " (itf %d): open %d.%d \n " , vcc - > dev - > number , vcc - > vpi ,
vcc - > vci ) ;
if ( ! test_bit ( ATM_VF_PARTIAL , & vcc - > flags ) ) {
zatm_vcc = kmalloc ( sizeof ( struct zatm_vcc ) , GFP_KERNEL ) ;
if ( ! zatm_vcc ) {
clear_bit ( ATM_VF_ADDR , & vcc - > flags ) ;
return - ENOMEM ;
}
vcc - > dev_data = zatm_vcc ;
ZATM_VCC ( vcc ) - > tx_chan = 0 ; /* for zatm_close after open_rx */
if ( ( error = open_rx_first ( vcc ) ) ) {
zatm_close ( vcc ) ;
return error ;
}
if ( ( error = open_tx_first ( vcc ) ) ) {
zatm_close ( vcc ) ;
return error ;
}
}
if ( vci = = ATM_VPI_UNSPEC | | vpi = = ATM_VCI_UNSPEC ) return 0 ;
if ( ( error = open_rx_second ( vcc ) ) ) {
zatm_close ( vcc ) ;
return error ;
}
if ( ( error = open_tx_second ( vcc ) ) ) {
zatm_close ( vcc ) ;
return error ;
}
set_bit ( ATM_VF_READY , & vcc - > flags ) ;
return 0 ;
}
static int zatm_change_qos ( struct atm_vcc * vcc , struct atm_qos * qos , int flags )
{
printk ( " Not yet implemented \n " ) ;
return - ENOSYS ;
/* @@@ */
}
static int zatm_ioctl ( struct atm_dev * dev , unsigned int cmd , void __user * arg )
{
struct zatm_dev * zatm_dev ;
unsigned long flags ;
zatm_dev = ZATM_DEV ( dev ) ;
switch ( cmd ) {
case ZATM_GETPOOLZ :
if ( ! capable ( CAP_NET_ADMIN ) ) return - EPERM ;
/* fall through */
case ZATM_GETPOOL :
{
struct zatm_pool_info info ;
int pool ;
if ( get_user ( pool ,
& ( ( struct zatm_pool_req __user * ) arg ) - > pool_num ) )
return - EFAULT ;
if ( pool < 0 | | pool > ZATM_LAST_POOL )
return - EINVAL ;
spin_lock_irqsave ( & zatm_dev - > lock , flags ) ;
info = zatm_dev - > pool_info [ pool ] ;
if ( cmd = = ZATM_GETPOOLZ ) {
zatm_dev - > pool_info [ pool ] . rqa_count = 0 ;
zatm_dev - > pool_info [ pool ] . rqu_count = 0 ;
}
spin_unlock_irqrestore ( & zatm_dev - > lock , flags ) ;
return copy_to_user (
& ( ( struct zatm_pool_req __user * ) arg ) - > info ,
& info , sizeof ( info ) ) ? - EFAULT : 0 ;
}
case ZATM_SETPOOL :
{
struct zatm_pool_info info ;
int pool ;
if ( ! capable ( CAP_NET_ADMIN ) ) return - EPERM ;
if ( get_user ( pool ,
& ( ( struct zatm_pool_req __user * ) arg ) - > pool_num ) )
return - EFAULT ;
if ( pool < 0 | | pool > ZATM_LAST_POOL )
return - EINVAL ;
if ( copy_from_user ( & info ,
& ( ( struct zatm_pool_req __user * ) arg ) - > info ,
sizeof ( info ) ) ) return - EFAULT ;
if ( ! info . low_water )
info . low_water = zatm_dev - >
pool_info [ pool ] . low_water ;
if ( ! info . high_water )
info . high_water = zatm_dev - >
pool_info [ pool ] . high_water ;
if ( ! info . next_thres )
info . next_thres = zatm_dev - >
pool_info [ pool ] . next_thres ;
if ( info . low_water > = info . high_water | |
info . low_water < 0 )
return - EINVAL ;
spin_lock_irqsave ( & zatm_dev - > lock , flags ) ;
zatm_dev - > pool_info [ pool ] . low_water =
info . low_water ;
zatm_dev - > pool_info [ pool ] . high_water =
info . high_water ;
zatm_dev - > pool_info [ pool ] . next_thres =
info . next_thres ;
spin_unlock_irqrestore ( & zatm_dev - > lock , flags ) ;
return 0 ;
}
default :
if ( ! dev - > phy - > ioctl ) return - ENOIOCTLCMD ;
return dev - > phy - > ioctl ( dev , cmd , arg ) ;
}
}
static int zatm_getsockopt ( struct atm_vcc * vcc , int level , int optname ,
void __user * optval , int optlen )
{
return - EINVAL ;
}
static int zatm_setsockopt ( struct atm_vcc * vcc , int level , int optname ,
void __user * optval , int optlen )
{
return - EINVAL ;
}
static int zatm_send ( struct atm_vcc * vcc , struct sk_buff * skb )
{
int error ;
EVENT ( " >zatm_send 0x%lx \n " , ( unsigned long ) skb , 0 ) ;
if ( ! ZATM_VCC ( vcc ) - > tx_chan | | ! test_bit ( ATM_VF_READY , & vcc - > flags ) ) {
if ( vcc - > pop ) vcc - > pop ( vcc , skb ) ;
else dev_kfree_skb ( skb ) ;
return - EINVAL ;
}
if ( ! skb ) {
printk ( KERN_CRIT " !skb in zatm_send ? \n " ) ;
if ( vcc - > pop ) vcc - > pop ( vcc , skb ) ;
return - EINVAL ;
}
ATM_SKB ( skb ) - > vcc = vcc ;
error = do_tx ( skb ) ;
if ( error ! = RING_BUSY ) return error ;
skb_queue_tail ( & ZATM_VCC ( vcc ) - > backlog , skb ) ;
return 0 ;
}
static void zatm_phy_put ( struct atm_dev * dev , unsigned char value ,
unsigned long addr )
{
struct zatm_dev * zatm_dev ;
zatm_dev = ZATM_DEV ( dev ) ;
zwait ;
zout ( value , CER ) ;
zout ( uPD98401_IND_ACC | uPD98401_IA_B0 |
( uPD98401_IA_TGT_PHY < < uPD98401_IA_TGT_SHIFT ) | addr , CMR ) ;
}
static unsigned char zatm_phy_get ( struct atm_dev * dev , unsigned long addr )
{
struct zatm_dev * zatm_dev ;
zatm_dev = ZATM_DEV ( dev ) ;
zwait ;
zout ( uPD98401_IND_ACC | uPD98401_IA_B0 | uPD98401_IA_RW |
( uPD98401_IA_TGT_PHY < < uPD98401_IA_TGT_SHIFT ) | addr , CMR ) ;
zwait ;
return zin ( CER ) & 0xff ;
}
static const struct atmdev_ops ops = {
. open = zatm_open ,
. close = zatm_close ,
. ioctl = zatm_ioctl ,
. getsockopt = zatm_getsockopt ,
. setsockopt = zatm_setsockopt ,
. send = zatm_send ,
. phy_put = zatm_phy_put ,
. phy_get = zatm_phy_get ,
. change_qos = zatm_change_qos ,
} ;
static int __devinit zatm_init_one ( struct pci_dev * pci_dev ,
const struct pci_device_id * ent )
{
struct atm_dev * dev ;
struct zatm_dev * zatm_dev ;
int ret = - ENOMEM ;
zatm_dev = ( struct zatm_dev * ) kmalloc ( sizeof ( * zatm_dev ) , GFP_KERNEL ) ;
if ( ! zatm_dev ) {
printk ( KERN_EMERG " %s: memory shortage \n " , DEV_LABEL ) ;
goto out ;
}
dev = atm_dev_register ( DEV_LABEL , & ops , - 1 , NULL ) ;
if ( ! dev )
goto out_free ;
ret = pci_enable_device ( pci_dev ) ;
if ( ret < 0 )
goto out_deregister ;
ret = pci_request_regions ( pci_dev , DEV_LABEL ) ;
if ( ret < 0 )
goto out_disable ;
zatm_dev - > pci_dev = pci_dev ;
dev - > dev_data = zatm_dev ;
zatm_dev - > copper = ( int ) ent - > driver_data ;
if ( ( ret = zatm_init ( dev ) ) | | ( ret = zatm_start ( dev ) ) )
goto out_release ;
pci_set_drvdata ( pci_dev , dev ) ;
zatm_dev - > more = zatm_boards ;
zatm_boards = dev ;
ret = 0 ;
out :
return ret ;
out_release :
pci_release_regions ( pci_dev ) ;
out_disable :
pci_disable_device ( pci_dev ) ;
out_deregister :
atm_dev_deregister ( dev ) ;
out_free :
kfree ( zatm_dev ) ;
goto out ;
}
MODULE_LICENSE ( " GPL " ) ;
static struct pci_device_id zatm_pci_tbl [ ] __devinitdata = {
{ PCI_VENDOR_ID_ZEITNET , PCI_DEVICE_ID_ZEITNET_1221 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , ZATM_COPPER } ,
{ PCI_VENDOR_ID_ZEITNET , PCI_DEVICE_ID_ZEITNET_1225 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 0 } ,
{ 0 , }
} ;
MODULE_DEVICE_TABLE ( pci , zatm_pci_tbl ) ;
static struct pci_driver zatm_driver = {
. name = DEV_LABEL ,
. id_table = zatm_pci_tbl ,
. probe = zatm_init_one ,
} ;
static int __init zatm_init_module ( void )
{
return pci_register_driver ( & zatm_driver ) ;
}
module_init ( zatm_init_module ) ;
/* module_exit not defined so not unloadable */