📄 smc91111.c
字号:
/*
* Open and Initialize the board
*
* Set up everything, reset the card, etc ..
*
*/
static int smc_open(struct net_device *dev)
{
struct smc_local *lp = (struct smc_local *)dev->priv;
int ioaddr = dev->base_addr;
int i; /* used to set hw ethernet address */
PRINTK2("%s:smc_open\n", dev->name);
/* clear out all the junk that was put here before... */
memset(dev->priv, 0, sizeof(struct smc_local));
/* => Store the ChipID and ChipRev, to be used in resolving the Odd-Byte issue in Rev > A of LAN91C111; FM */
SMC_SELECT_BANK(3);
lp->ChipID = (inw( ioaddr + REV_REG ) >> 4) & 0xF;
lp->ChipRev = inw( ioaddr + REV_REG ) & 0xF;
netif_start_queue(dev);
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
// Setup the default Register Modes
lp->tcr_cur_mode = TCR_DEFAULT;
lp->rcr_cur_mode = RCR_DEFAULT;
lp->rpc_cur_mode = RPC_DEFAULT;
// Set default parameters (files)
lp->ctl_swfdup = link_mode&(LINK_SPEED_10FD|LINK_SPEED_100FD)? 1:0;
lp->ctl_ephloop = 0;
lp->ctl_miiop = 0;
lp->ctl_autoneg = link_mode&(LINK_AUTO_NEGOTIATE)? 1:0;
lp->ctl_rfduplx = 1;
lp->ctl_rspeed = link_mode&(LINK_SPEED_100HD|LINK_SPEED_100FD)? 100:10;
lp->ctl_afduplx = 1;
lp->ctl_aspeed = 100;
lp->ctl_lnkfail = 1;
lp->ctl_forcol = 0;
lp->ctl_filtcar = 0;
// Reconfigure default Register Modes from the insmod cmd line link_mode
if (lp->ctl_swfdup)
lp->tcr_cur_mode |= TCR_SWFDUP;
else
lp->tcr_cur_mode &= ~TCR_SWFDUP;
if (lp->ctl_autoneg)
lp->rpc_cur_mode |= RPC_ANEG;
else
lp->rpc_cur_mode &= ~RPC_ANEG;
if (lp->ctl_rfduplx)
lp->rpc_cur_mode |= RPC_DPLX;
else
lp->rpc_cur_mode &= ~RPC_DPLX;
if (lp->ctl_rspeed == 100)
lp->rpc_cur_mode |= RPC_SPEED;
else
lp->rpc_cur_mode &= ~RPC_SPEED;
/* reset the hardware */
smc_reset( dev );
smc_enable( dev );
/* Configure the PHY */
smc_phy_configure(dev);
smc_enable( dev );
/*
According to Becker, I have to set the hardware address
at this point, because the (l)user can set it with an
ioctl. Easily done...
*/
SMC_SELECT_BANK( 1 );
for ( i = 0; i < 6; i += 2 ) {
word address;
address = dev->dev_addr[ i + 1 ] << 8 ;
address |= dev->dev_addr[ i ];
outw( address, ioaddr + ADDR0_REG + i );
}
#ifdef CONFIG_SYSCTL
smc_sysctl_register(dev);
#endif /* CONFIG_SYSCTL */
netif_start_queue(dev);
return 0;
}
/*--------------------------------------------------------
. Called by the kernel to send a packet out into the void
. of the net. This routine is largely based on
. skeleton.c, from Becker.
.--------------------------------------------------------
*/
static void smc_timeout (struct net_device *dev)
{
struct smc_local *lp = (struct smc_local *)dev->priv;
PRINTK3("%s:smc_send_packet\n", dev->name);
/* If we get here, some higher level has decided we are broken.
There should really be a "kick me" function call instead. */
printk(KERN_WARNING "%s: transmit timed out, %s?\n",dev->name, tx_done(dev) ? "IRQ conflict" :"network cable problem");
/* clear anything saved */
if( lp->saved_skb ) {
dev_kfree_skb (lp->saved_skb);
lp->saved_skb = NULL;
}
/* "kick" the adaptor */
smc_reset( dev );
/* Reconfigure the PHY */
smc_phy_configure(dev);
smc_enable( dev );
/* Reconfigure the PROMISC and the MULTICAST mode */
smc_set_multicast_list(dev);
dev->trans_start = jiffies;
netif_wake_queue(dev);
}
/*--------------------------------------------------------------------
.
. This is the main routine of the driver, to handle the net_device when
. it needs some attention.
.
. So:
. first, save state of the chipset
. branch off into routines to handle each case, and acknowledge
. each to the interrupt register
. and finally restore state.
.
---------------------------------------------------------------------*/
static void smc_interrupt(int irq, void * dev_id, struct pt_regs * regs)
{
struct net_device *dev = dev_id;
int ioaddr = dev->base_addr;
struct smc_local *lp = (struct smc_local *)dev->priv;
byte status;
word card_stats;
byte mask;
int timeout;
/* state registers */
word saved_bank;
word saved_pointer;
PRINTK3("%s: SMC interrupt started \n", dev->name);
if (dev == NULL) {
printk(KERN_WARNING "%s: irq %d for unknown device.\n",
dev->name, irq);
return;
}
/* will Linux let this happen ?? If not, this costs some speed
if ( dev->interrupt ) {
printk(KERN_WARNING "%s: interrupt inside interrupt.\n",
dev->name);
return;
}
dev->interrupt = 1; */
saved_bank = inw( ioaddr + BANK_SELECT );
SMC_SELECT_BANK(2);
saved_pointer = inw( ioaddr + PTR_REG );
/* read the interrupt status register */
mask = inb( ioaddr + IM_REG );
/* disable all interrupts */
outb( 0, ioaddr + IM_REG );
/* set a timeout value, so I don't stay here forever */
timeout = 4;
PRINTK2(KERN_WARNING "%s: MASK IS %x \n", dev->name, mask);
do {
/* read the status flag, and mask it */
status = inb( ioaddr + INT_REG ) & mask;
if (!status )
break;
PRINTK3(KERN_WARNING "%s: Handling interrupt status %x \n",
dev->name, status);
if (status & IM_RCV_INT) {
/* Got a packet(s). */
PRINTK2(KERN_WARNING
"%s: Receive Interrupt\n", dev->name);
smc_rcv(dev);
} else if (status & IM_TX_INT ) {
PRINTK2(KERN_WARNING "%s: TX ERROR handled\n",
dev->name);
smc_tx(dev);
// Acknowledge the interrupt
outb(IM_TX_INT, ioaddr + INT_REG );
} else if (status & IM_TX_EMPTY_INT ) {
/* update stats */
SMC_SELECT_BANK( 0 );
card_stats = inw( ioaddr + COUNTER_REG );
/* single collisions */
lp->stats.collisions += card_stats & 0xF;
card_stats >>= 4;
/* multiple collisions */
lp->stats.collisions += card_stats & 0xF;
/* these are for when linux supports these statistics */
#if 0
card_stats >>= 4;
/* deferred */
card_stats >>= 4;
/* excess deferred */
#endif
SMC_SELECT_BANK( 2 );
PRINTK2(KERN_WARNING "%s: TX_BUFFER_EMPTY handled\n",
dev->name);
// Acknowledge the interrupt
outb( IM_TX_EMPTY_INT, ioaddr + INT_REG );
mask &= ~IM_TX_EMPTY_INT;
lp->stats.tx_packets += lp->packets_waiting;
lp->packets_waiting = 0;
} else if (status & IM_ALLOC_INT ) {
PRINTK2(KERN_DEBUG "%s: Allocation interrupt \n",
dev->name);
/* clear this interrupt so it doesn't happen again */
mask &= ~IM_ALLOC_INT;
smc_hardware_send_packet( dev );
/* enable xmit interrupts based on this */
mask |= ( IM_TX_EMPTY_INT | IM_TX_INT );
PRINTK2("%s: Handoff done successfully.\n",
dev->name);
} else if (status & IM_RX_OVRN_INT ) {
lp->stats.rx_errors++;
lp->stats.rx_fifo_errors++;
// Acknowledge the interrupt
outb( IM_RX_OVRN_INT, ioaddr + INT_REG );
} else if (status & IM_EPH_INT ) {
PRINTK("%s: UNSUPPORTED: EPH INTERRUPT \n",
dev->name);
} else if (status & IM_MDINT ) {
smc_phy_interrupt(dev);
// Acknowledge the interrupt
outb(IM_MDINT, ioaddr + INT_REG );
} else if (status & IM_ERCV_INT ) {
PRINTK("%s: UNSUPPORTED: ERCV INTERRUPT \n",
dev->name);
// Acknowledge the interrupt
outb( IM_ERCV_INT, ioaddr + INT_REG );
}
} while ( timeout -- );
/* restore register states */
SMC_SELECT_BANK( 2 );
outb( mask, ioaddr + IM_REG );
PRINTK3( KERN_WARNING "%s: MASK is now %x \n", dev->name, mask);
outw( saved_pointer, ioaddr + PTR_REG );
SMC_SELECT_BANK( saved_bank );
//dev->interrupt = 0;
PRINTK3("%s: Interrupt done\n", dev->name);
return;
}
/*-------------------------------------------------------------
.
. smc_rcv - receive a packet from the card
.
. There is ( at least ) a packet waiting to be read from
. chip-memory.
.
. o Read the status
. o If an error, record it
. o otherwise, read in the packet
--------------------------------------------------------------
*/
static void smc_rcv(struct net_device *dev)
{
struct smc_local *lp = (struct smc_local *)dev->priv;
int ioaddr = dev->base_addr;
int packet_number;
word status;
word packet_length;
PRINTK3("%s:smc_rcv\n", dev->name);
/* assume bank 2 */
packet_number = inw( ioaddr + RXFIFO_REG );
if ( packet_number & RXFIFO_REMPTY ) {
/* we got called , but nothing was on the FIFO */
PRINTK("%s: WARNING: smc_rcv with nothing on FIFO. \n",
dev->name);
/* don't need to restore anything */
return;
}
/* start reading from the start of the packet */
outw( PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + PTR_REG );
/* First two words are status and packet_length */
status = inw( ioaddr + DATA_REG );
packet_length = inw( ioaddr + DATA_REG );
packet_length &= 0x07ff; /* mask off top bits */
PRINTK2("RCV: STATUS %4x LENGTH %4x\n", status, packet_length );
if ( !(status & RS_ERRORS ) ){
/* do stuff to make a new packet */
struct sk_buff * skb;
byte * data;
/* set multicast stats */
if ( status & RS_MULTICAST )
lp->stats.multicast++;
/* Adjust for having already read the first two words */
packet_length -= 4;
// Allocate enough memory for entire receive frame, to be safe
skb = dev_alloc_skb( packet_length + 2 );
if ( skb == NULL ) {
printk(KERN_NOTICE "%s: Low memory, packet dropped.\n",
dev->name);
lp->stats.rx_dropped++;
goto done;
}
/*
! This should work without alignment, but it could be
! in the worse case
*/
/* TODO: Should I use 32bit alignment here ? */
skb_reserve( skb, 2 ); /* 16 bit alignment */
skb->dev = dev;
/* =>
ODD-BYTE ISSUE : The odd byte problem has been fixed in the LAN91C111 Rev B.
So we check if the Chip Revision, stored in smsc_local->ChipRev, is >= 1.
If so then we increment the packet length only if RS_ODDFRAME is set.
If the Chip's revision is equal to 0, then we blindly increment the packet length
by 1, thus always assuming that the packet is odd length, leaving the higher layer
to decide the actual length.
-- Pramod
<= */
// set odd length for bug in LAN91C111, REV A
// which never sets RS_ODDFRAME
if ((lp->ChipID == 9) && (lp->ChipRev == 0))
status |= RS_ODDFRAME;
if (status & RS_ODDFRAME)
packet_length += 1;
data = skb_put( skb, packet_length );
#ifdef USE_32_BIT
PRINTK3(" Reading %d dwords (and %d bytes) \n",
packet_length >> 2, packet_length & 3 );
/* QUESTION: Like in the TX routine, do I want
to send the DWORDs or the bytes first, or some
mixture. A mixture might improve already slow PIO
performance */
insl(ioaddr + DATA_REG , data, packet_length >> 2 );
/* read the left over bytes */
insb( ioaddr + DATA_REG, data + (packet_length & 0xFFFFFC),
packet_length & 0x3 );
#else
PRINTK3(" Reading %d words and %d byte(s) \n",
(packet_length >> 1 ), packet_length & 1 );
insw(ioaddr + DATA_REG , data, packet_length >> 1);
#endif // USE_32_BIT
#if SMC_DEBUG > 2
printk("Receiving Packet\n");
print_packet( data, packet_length );
#endif
skb->protocol = eth_type_trans(skb, dev );
netif_rx(skb);
lp->stats.rx_packets++;
} else {
/* error ... */
lp->stats.rx_errors++;
if ( status & RS_ALGNERR ) lp->stats.rx_frame_errors++;
if ( status & (RS_TOOSHORT | RS_TOOLONG ) )
lp->stats.rx_length_errors++;
if ( status & RS_BADCRC) lp->stats.rx_crc_errors++;
}
while ( inw( ioaddr + MMU_CMD_REG ) & MC_BUSY )
udelay(1); // Wait until not busy
done:
/* error or good, tell the card to get rid of this packet */
outw( MC_RELEASE, ioaddr + MMU_CMD_REG );
return;
}
/*************************************************************************
. smc_tx
.
. Purpose: Handle a transmit error message. This will only be called
. when an error, because of the AUTO_RELEASE mode.
.
. Algorithm:
. Save pointer and packet no
. Get the packet no from the top of the queue
. check if it's valid ( if not, is this an error??? )
. read the status word
. record the error
. ( resend? Not really, since we don't want old packets around )
. Restore saved values
************************************************************************/
static void smc_tx( struct net_device * dev )
{
int ioaddr = dev->base_addr;
struct smc_local *lp = (struct smc_local *)dev->priv;
byte saved_packet;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -