📄 smc91111.c
字号:
/*
this routine will set the hardware multicast table to the specified
values given it by the higher level routines
*/
static void smc_setmulticast( int ioaddr, int count, struct dev_mc_list * );
static int crc32( char *, int );
/* Routines to Read and Write the PHY Registers across the
MII Management Interface
*/
static word smc_read_phy_register(int ioaddr, byte phyaddr, byte phyreg);
static void smc_write_phy_register(int ioaddr, byte phyaddr, byte phyreg, word phydata);
/* Initilizes our device's sysctl proc filesystem */
#ifdef CONFIG_SYSCTL
static void smc_sysctl_register(struct net_device *);
static void smc_sysctl_unregister(struct net_device *);
#endif /* CONFIG_SYSCTL */
static int eth_change_mtu(struct net_device *dev, int new_mtu);
#ifdef MODULE
static struct net_device devSMC91111;
int io = 0;
int irq = 0;
int nowait = 0;
static int link_mode=LINK_MODE_INIT;
MODULE_PARM(io, "i");
MODULE_PARM(irq, "i");
MODULE_PARM(nowait, "i");
MODULE_PARM(link_mode,"i");
MODULE_PARM_DESC(io, "SMC 91111 I/O base address");
MODULE_PARM_DESC(irq, "SMC 91111 IRQ number");
MODULE_PARM_DESC(nowait, "0: normal wait states 1:eliminate additional wait states");
MODULE_PARM_DESC(link_mode,"Set Link speed and Duplex, 1=10HD,2=10FD,4=100HD,8=100FD,40=ANEG,default=0x4F");
#endif // MODULE
/*
. Function: smc_reset( struct device* dev )
. Purpose:
. This sets the SMC91111 chip to its normal state, hopefully from whatever
. mess that any other DOS driver has put it in.
.
. Maybe I should reset more registers to defaults in here? SOFTRST should
. do that for me.
.
. Method:
. 1. send a SOFT RESET
. 2. wait for it to finish
. 3. enable autorelease mode
. 4. reset the memory management unit
. 5. clear all interrupts
.
*/
static void smc_reset( struct net_device* dev )
{
//struct smc_local *lp = (struct smc_local *)dev->priv;
int ioaddr = dev->base_addr;
PRINTK2("%s:smc_reset\n", dev->name);
/* This resets the registers mostly to defaults, but doesn't
affect EEPROM. That seems unnecessary */
SMC_SELECT_BANK( 0 );
outw( RCR_SOFTRST, ioaddr + RCR_REG );
/* Setup the Configuration Register */
/* This is necessary because the CONFIG_REG is not affected */
/* by a soft reset */
SMC_SELECT_BANK( 1 );
outw( CONFIG_DEFAULT, ioaddr + CONFIG_REG);
/* Setup for fast accesses if requested */
/* If the card/system can't handle it then there will */
/* be no recovery except for a hard reset or power cycle */
if (dev->dma)
outw( inw( ioaddr + CONFIG_REG ) | CONFIG_NO_WAIT,
ioaddr + CONFIG_REG );
#ifdef POWER_DOWN
/* Release from possible power-down state */
/* Configuration register is not affected by Soft Reset */
SMC_SELECT_BANK( 1 );
outw( inw( ioaddr + CONFIG_REG ) | CONFIG_EPH_POWER_EN,
ioaddr + CONFIG_REG );
#endif
SMC_SELECT_BANK( 0 );
/* this should pause enough for the chip to be happy */
mdelay(10);
/* Disable transmit and receive functionality */
outw( RCR_CLEAR, ioaddr + RCR_REG );
outw( TCR_CLEAR, ioaddr + TCR_REG );
/* set the control register to automatically
release successfully transmitted packets, to make the best
use out of our limited memory */
SMC_SELECT_BANK( 1 );
outw( inw( ioaddr + CTL_REG ) | CTL_AUTO_RELEASE , ioaddr + CTL_REG );
/* Reset the MMU */
SMC_SELECT_BANK( 2 );
outw( MC_RESET, ioaddr + MMU_CMD_REG );
/* Note: It doesn't seem that waiting for the MMU busy is needed here,
but this is a place where future chipsets _COULD_ break. Be wary
of issuing another MMU command right after this */
/* Disable all interrupts */
outb( 0, ioaddr + IM_REG );
}
/*
. Function: smc_enable
. Purpose: let the chip talk to the outside work
. Method:
. 1. Enable the transmitter
. 2. Enable the receiver
. 3. Enable interrupts
*/
static void smc_enable( struct net_device *dev )
{
unsigned short ioaddr = dev->base_addr;
struct smc_local *lp = (struct smc_local *)dev->priv;
PRINTK2("%s:smc_enable\n", dev->name);
SMC_SELECT_BANK( 0 );
/* see the header file for options in TCR/RCR DEFAULT*/
outw( lp->tcr_cur_mode, ioaddr + TCR_REG );
outw( lp->rcr_cur_mode, ioaddr + RCR_REG );
/* now, enable interrupts */
SMC_SELECT_BANK( 2 );
outb( SMC_INTERRUPT_MASK, ioaddr + IM_REG );
}
/*
. Function: smc_shutdown
. Purpose: closes down the SMC91xxx chip.
. Method:
. 1. zero the interrupt mask
. 2. clear the enable receive flag
. 3. clear the enable xmit flags
.
. TODO:
. (1) maybe utilize power down mode.
. Why not yet? Because while the chip will go into power down mode,
. the manual says that it will wake up in response to any I/O requests
. in the register space. Empirical results do not show this working.
*/
static void smc_shutdown( int ioaddr )
{
PRINTK2("CARDNAME:smc_shutdown\n");
/* no more interrupts for me */
SMC_SELECT_BANK( 2 );
outb( 0, ioaddr + IM_REG );
/* and tell the card to stay away from that nasty outside world */
SMC_SELECT_BANK( 0 );
outb( RCR_CLEAR, ioaddr + RCR_REG );
outb( TCR_CLEAR, ioaddr + TCR_REG );
#ifdef POWER_DOWN
/* finally, shut the chip down */
SMC_SELECT_BANK( 1 );
outw( inw( ioaddr + CONFIG_REG ) & ~CONFIG_EPH_POWER_EN,
ioaddr + CONFIG_REG );
#endif
}
/*
. Function: smc_setmulticast( int ioaddr, int count, dev_mc_list * adds )
. Purpose:
. This sets the internal hardware table to filter out unwanted multicast
. packets before they take up memory.
.
. The SMC chip uses a hash table where the high 6 bits of the CRC of
. address are the offset into the table. If that bit is 1, then the
. multicast packet is accepted. Otherwise, it's dropped silently.
.
. To use the 6 bits as an offset into the table, the high 3 bits are the
. number of the 8 bit register, while the low 3 bits are the bit within
. that register.
.
. This routine is based very heavily on the one provided by Peter Cammaert.
*/
static void smc_setmulticast( int ioaddr, int count, struct dev_mc_list * addrs ) {
int i;
unsigned char multicast_table[ 8 ];
struct dev_mc_list * cur_addr;
/* table for flipping the order of 3 bits */
unsigned char invert3[] = { 0, 4, 2, 6, 1, 5, 3, 7 };
PRINTK2("CARDNAME:smc_setmulticast\n");
/* start with a table of all zeros: reject all */
memset( multicast_table, 0, sizeof( multicast_table ) );
cur_addr = addrs;
for ( i = 0; i < count ; i ++, cur_addr = cur_addr->next ) {
int position;
/* do we have a pointer here? */
if ( !cur_addr )
break;
/* make sure this is a multicast address - shouldn't this
be a given if we have it here ? */
if ( !( *cur_addr->dmi_addr & 1 ) )
continue;
/* only use the low order bits */
position = crc32( cur_addr->dmi_addr, 6 ) & 0x3f;
/* do some messy swapping to put the bit in the right spot */
multicast_table[invert3[position&7]] |=
(1<<invert3[(position>>3)&7]);
}
/* now, the table can be loaded into the chipset */
SMC_SELECT_BANK( 3 );
for ( i = 0; i < 8 ; i++ ) {
outb( multicast_table[i], ioaddr + MCAST_REG1 + i );
}
}
/*
Finds the CRC32 of a set of bytes.
Again, from Peter Cammaert's code.
*/
static int crc32( char * s, int length ) {
/* indices */
int perByte;
int perBit;
/* crc polynomial for Ethernet */
const unsigned long poly = 0xedb88320;
/* crc value - preinitialized to all 1's */
unsigned long crc_value = 0xffffffff;
for ( perByte = 0; perByte < length; perByte ++ ) {
unsigned char c;
c = *(s++);
for ( perBit = 0; perBit < 8; perBit++ ) {
crc_value = (crc_value>>1)^
(((crc_value^c)&0x01)?poly:0);
c >>= 1;
}
}
return crc_value;
}
/*
. Function: smc_wait_to_send_packet( struct sk_buff * skb, struct device * )
. Purpose:
. Attempt to allocate memory for a packet, if chip-memory is not
. available, then tell the card to generate an interrupt when it
. is available.
.
. Algorithm:
.
. o if the saved_skb is not currently null, then drop this packet
. on the floor. This should never happen, because of TBUSY.
. o if the saved_skb is null, then replace it with the current packet,
. o See if I can sending it now.
. o (NO): Enable interrupts and let the interrupt handler deal with it.
. o (YES):Send it now.
*/
static int smc_wait_to_send_packet( struct sk_buff * skb, struct net_device * dev )
{
struct smc_local *lp = (struct smc_local *)dev->priv;
unsigned short ioaddr = dev->base_addr;
word length;
unsigned short numPages;
word time_out;
word status;
PRINTK3("%s:smc_wait_to_send_packet\n", dev->name);
netif_stop_queue(dev);
if ( lp->saved_skb) {
struct sk_buff *stuck=lp->saved_skb;
/* THIS SHOULD NEVER HAPPEN. */
lp->stats.tx_aborted_errors++;
printk("%s: smc_wait_to_send_packet error - sent packet while busy.\n",
dev->name);
lp->saved_skb = NULL; // beat the race condition, if any
dev_kfree_skb (stuck); // dump the stuck one, start the new one
}
lp->saved_skb = skb;
length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
/*
** The MMU wants the number of pages to be the number of 256 bytes
** 'pages', minus 1 ( since a packet can't ever have 0 pages :) )
**
** The 91C111 ignores the size bits, but the code is left intact
** for backwards and future compatibility.
**
** Pkt size for allocating is data length +6 (for additional status
** words, length and ctl!)
**
** If odd size then last byte is included in this header.
*/
numPages = ((length & 0xfffe) + 6);
numPages >>= 8; // Divide by 256
if (numPages > 7 ) {
printk("%s: Far too big packet error. \n", dev->name);
/* freeing the packet is a good thing here... but should
. any packets of this size get down here? */
dev_kfree_skb (skb);
lp->saved_skb = NULL;
/* this IS an error, but, i don't want the skb saved */
netif_wake_queue(dev);
return 0;
}
/* either way, a packet is waiting now */
lp->packets_waiting++;
/* now, try to allocate the memory */
SMC_SELECT_BANK( 2 );
outw( MC_ALLOC | numPages, ioaddr + MMU_CMD_REG );
/*
. Performance Hack
.
. wait a short amount of time.. if I can send a packet now, I send
. it now. Otherwise, I enable an interrupt and wait for one to be
. available.
.
. I could have handled this a slightly different way, by checking to
. see if any memory was available in the FREE MEMORY register. However,
. either way, I need to generate an allocation, and the allocation works
. no matter what, so I saw no point in checking free memory.
*/
time_out = MEMORY_WAIT_TIME;
do {
status = inb( ioaddr + INT_REG );
if ( status & IM_ALLOC_INT ) {
/* acknowledge the interrupt */
outb( IM_ALLOC_INT, ioaddr + INT_REG );
break;
}
} while ( -- time_out );
if ( !time_out ) {
/* oh well, wait until the chip finds memory later */
SMC_ENABLE_INT( IM_ALLOC_INT );
/* Check the status bit one more time just in case */
/* it snuk in between the time we last checked it */
/* and when we set the interrupt bit */
status = inb( ioaddr + INT_REG );
if ( !(status & IM_ALLOC_INT) ) {
PRINTK2("%s: memory allocation deferred. \n",
dev->name);
/* it's deferred, but I'll handle it later */
return 0;
}
/* Looks like it did sneak in, so disable */
/* the interrupt */
SMC_DISABLE_INT( IM_ALLOC_INT );
}
/* or YES! I can send the packet now.. */
smc_hardware_send_packet(dev);
return 0;
}
/*
. Function: smc_hardware_send_packet(struct device * )
. Purpose:
. This sends the actual packet to the SMC9xxx chip.
.
. Algorithm:
. First, see if a saved_skb is available.
. ( this should NOT be called if there is no 'saved_skb'
. Now, find the packet number that the chip allocated
. Point the data pointers at it in memory
. Set the length word in the chip's memory
. Dump the packet to chip memory
. Check if a last byte is needed ( odd length packet )
. if so, set the control flag right
. Tell the card to send it
. Enable the transmit interrupt, so I know if it failed
. Free the kernel data if I actually sent it.
*/
static void smc_hardware_send_packet( struct net_device * dev )
{
struct smc_local *lp = (struct smc_local *)dev->priv;
byte packet_no;
struct sk_buff * skb = lp->saved_skb;
word length;
unsigned short ioaddr;
byte * buf;
PRINTK3("%s:smc_hardware_send_packet\n", dev->name);
ioaddr = dev->base_addr;
if ( !skb ) {
PRINTK("%s: In XMIT with no packet to send \n", dev->name);
return;
}
length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
buf = skb->data;
/* If I get here, I _know_ there is a packet slot waiting for me */
packet_no = inb( ioaddr + AR_REG );
if ( packet_no & AR_FAILED ) {
/* or isn't there? BAD CHIP! */
printk(KERN_DEBUG "%s: Memory allocation failed. \n",
dev->name);
dev_kfree_skb_any (skb);
lp->saved_skb = NULL;
netif_wake_queue(dev);
return;
}
/* we have a packet address, so tell the card to use it */
outb( packet_no, ioaddr + PN_REG );
/* point to the beginning of the packet */
outw( PTR_AUTOINC , ioaddr + PTR_REG );
PRINTK3("%s: Trying to xmit packet of length %x\n",
dev->name, length);
#if SMC_DEBUG > 2
printk("Transmitting Packet\n");
print_packet( buf, length );
#endif
/* send the packet length ( +6 for status, length and ctl byte )
and the status word ( set to zeros ) */
#ifdef USE_32_BIT
outl( (length +6 ) << 16 , ioaddr + DATA_REG );
#else
outw( 0, ioaddr + DATA_REG );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -