📄 smc91111.c
字号:
byte packet_no;
word tx_status;
PRINTK3("%s:smc_tx\n", dev->name);
/* assume bank 2 */
saved_packet = inb( ioaddr + PN_REG );
packet_no = inw( ioaddr + RXFIFO_REG );
packet_no &= 0x7F;
/* If the TX FIFO is empty then nothing to do */
if ( packet_no & TXFIFO_TEMPTY )
return;
/* select this as the packet to read from */
outb( packet_no, ioaddr + PN_REG );
/* read the first word (status word) from this packet */
outw( PTR_AUTOINC | PTR_READ, ioaddr + PTR_REG );
tx_status = inw( ioaddr + DATA_REG );
PRINTK3("%s: TX DONE STATUS: %4x \n", dev->name, tx_status);
lp->stats.tx_errors++;
if ( tx_status & TS_LOSTCAR ) lp->stats.tx_carrier_errors++;
if ( tx_status & TS_LATCOL ) {
printk(KERN_DEBUG
"%s: Late collision occurred on last xmit.\n",
dev->name);
lp->stats.tx_window_errors++;
lp->ctl_forcol = 0; // Reset forced collsion
}
#if 0
if ( tx_status & TS_16COL ) { ... }
#endif
if ( tx_status & TS_SUCCESS ) {
printk("%s: Successful packet caused interrupt \n", dev->name);
}
/* re-enable transmit */
SMC_SELECT_BANK( 0 );
outw( inw( ioaddr + TCR_REG ) | TCR_ENABLE, ioaddr + TCR_REG );
/* kill the packet */
SMC_SELECT_BANK( 2 );
outw( MC_FREEPKT, ioaddr + MMU_CMD_REG );
/* one less packet waiting for me */
lp->packets_waiting--;
/* Don't change Packet Number Reg until busy bit is cleared */
/* Per LAN91C111 Spec, Page 50 */
while ( inw( ioaddr + MMU_CMD_REG ) & MC_BUSY );
outb( saved_packet, ioaddr + PN_REG );
return;
}
/*----------------------------------------------------
. smc_close
.
. this makes the board clean up everything that it can
. and not talk to the outside world. Caused by
. an 'ifconfig ethX down'
.
-----------------------------------------------------*/
static int smc_close(struct net_device *dev)
{
netif_stop_queue(dev);
//dev->start = 0;
PRINTK2("%s:smc_close\n", dev->name);
#ifdef CONFIG_SYSCTL
smc_sysctl_unregister(dev);
#endif /* CONFIG_SYSCTL */
/* clear everything */
smc_shutdown( dev->base_addr );
/* Update the statistics here. */
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
return 0;
}
/*------------------------------------------------------------
. Get the current statistics.
. This may be called with the card open or closed.
.-------------------------------------------------------------*/
static struct net_device_stats* smc_query_statistics(struct net_device *dev) {
struct smc_local *lp = (struct smc_local *)dev->priv;
PRINTK2("%s:smc_query_statistics\n", dev->name);
return &lp->stats;
}
/*-----------------------------------------------------------
. smc_set_multicast_list
.
. This routine will, depending on the values passed to it,
. either make it accept multicast packets, go into
. promiscuous mode ( for TCPDUMP and cousins ) or accept
. a select set of multicast packets
*/
static void smc_set_multicast_list(struct net_device *dev)
{
short ioaddr = dev->base_addr;
PRINTK2("%s:smc_set_multicast_list\n", dev->name);
SMC_SELECT_BANK(0);
if ( dev->flags & IFF_PROMISC )
{
PRINTK2("%s:smc_set_multicast_list:RCR_PRMS\n", dev->name);
outw( inw(ioaddr + RCR_REG ) | RCR_PRMS, ioaddr + RCR_REG );
}
/* BUG? I never disable promiscuous mode if multicasting was turned on.
Now, I turn off promiscuous mode, but I don't do anything to multicasting
when promiscuous mode is turned on.
*/
/* Here, I am setting this to accept all multicast packets.
I don't need to zero the multicast table, because the flag is
checked before the table is
*/
else if (dev->flags & IFF_ALLMULTI)
{
outw( inw(ioaddr + RCR_REG ) | RCR_ALMUL, ioaddr + RCR_REG );
PRINTK2("%s:smc_set_multicast_list:RCR_ALMUL\n", dev->name);
}
/* We just get all multicast packets even if we only want them
. from one source. This will be changed at some future
. point. */
else if (dev->mc_count ) {
/* support hardware multicasting */
/* be sure I get rid of flags I might have set */
outw( inw( ioaddr + RCR_REG ) & ~(RCR_PRMS | RCR_ALMUL),
ioaddr + RCR_REG );
/* NOTE: this has to set the bank, so make sure it is the
last thing called. The bank is set to zero at the top */
smc_setmulticast( ioaddr, dev->mc_count, dev->mc_list );
} else {
PRINTK2("%s:smc_set_multicast_list:~(RCR_PRMS|RCR_ALMUL)\n",
dev->name);
outw( inw( ioaddr + RCR_REG ) & ~(RCR_PRMS | RCR_ALMUL),
ioaddr + RCR_REG );
/*
since I'm disabling all multicast entirely, I need to
clear the multicast list
*/
SMC_SELECT_BANK( 3 );
outw( 0, ioaddr + MCAST_REG1 );
outw( 0, ioaddr + MCAST_REG2 );
outw( 0, ioaddr + MCAST_REG3 );
outw( 0, ioaddr + MCAST_REG4 );
}
}
#ifdef MODULE
/*------------------------------------------------------------
. Module initialization function
.-------------------------------------------------------------*/
int init_module(void)
{
int result;
PRINTK2("CARDNAME:init_module\n");
if (io == 0)
printk(KERN_WARNING
CARDNAME": You shouldn't use auto-probing with insmod!\n" );
/* copy the parameters from insmod into the device structure */
devSMC91111.base_addr = io;
devSMC91111.irq = irq;
devSMC91111.dma = nowait; // Use DMA field for nowait
devSMC91111.init = smc_init;/* Kernel 2.4 Changes - Pramod */
if ((result = register_netdev(&devSMC91111)) != 0)
return result;
return 0;
}
/*------------------------------------------------------------
. Cleanup when module is removed with rmmod
.-------------------------------------------------------------*/
void cleanup_module(void)
{
/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
unregister_netdev(&devSMC91111);
free_irq(devSMC91111.irq, &devSMC91111);
release_region(devSMC91111.base_addr, SMC_IO_EXTENT);
if (devSMC91111.priv)
kfree(devSMC91111.priv); /* Kernel 2.4 Changes - Pramod */
}
#else
static int link_mode=LINK_MODE_INIT; // in the code, whether MODULE or not
#endif /* MODULE */
#ifdef CONFIG_SYSCTL
/*------------------------------------------------------------
. Modify a bit in the LAN91C111 register set
.-------------------------------------------------------------*/
static word smc_modify_regbit(int bank, int ioaddr, int reg,
unsigned int bit, int val)
{
word regval;
SMC_SELECT_BANK( bank );
regval = inw( ioaddr+reg );
if (val)
regval |= bit;
else
regval &= ~bit;
outw( regval, ioaddr );
return(regval);
}
/*------------------------------------------------------------
. Retrieve a bit in the LAN91C111 register set
.-------------------------------------------------------------*/
static int smc_get_regbit(int bank, int ioaddr, int reg, unsigned int bit)
{
SMC_SELECT_BANK( bank );
if ( inw( ioaddr+reg ) & bit)
return(1);
else
return(0);
}
/*------------------------------------------------------------
. Modify a LAN91C111 register (word access only)
.-------------------------------------------------------------*/
static void smc_modify_reg(int bank, int ioaddr, int reg, word val)
{
SMC_SELECT_BANK( bank );
outw( val, ioaddr+reg );
}
/*------------------------------------------------------------
. Retrieve a LAN91C111 register (word access only)
.-------------------------------------------------------------*/
static int smc_get_reg(int bank, int ioaddr, int reg)
{
SMC_SELECT_BANK( bank );
return(inw( ioaddr+reg ));
}
static const char smc_info_string[] =
"\n"
"info Provides this information blurb\n"
"swver Prints the software version information of this driver\n"
"autoneg Auto-negotiate Mode = 1\n"
"rspeed Requested Speed, 100=100Mbps, 10=10Mpbs\n"
"rfduplx Requested Full Duplex Operation\n"
"aspeed Actual Speed, 100=100Mbps, 10=10Mpbs\n"
"afduplx Actual Full Duplex Operation\n"
"lnkfail PHY Link Failure when 1\n"
"miiop External MII when 1, Internal PHY when 0\n"
"swfdup Switched Full Duplex Mode (allowed only in MII operation)\n"
"ephloop EPH Block Loopback\n"
"forcol Force a collision\n"
"filtcar Filter leading edge of carrier sense for 12 bit times\n"
"freemem Free buffer memory in bytes\n"
"totmem Total buffer memory in bytes\n"
"leda Output of LED-A (green)\n"
"ledb Output of LED-B (yellow)\n"
"chiprev Revision ID of the LAN91C111 chip\n"
"";
/*------------------------------------------------------------
. Sysctl handler for all integer parameters
.-------------------------------------------------------------*/
static int smc_sysctl_handler(ctl_table *ctl, int write, struct file * filp,
void *buffer, size_t *lenp)
{
struct net_device *dev = (struct net_device*)ctl->extra1;
struct smc_local *lp = (struct smc_local *)ctl->extra2;
int ioaddr = dev->base_addr;
int *valp = ctl->data;
int val;
int ret;
// Update parameters from the real registers
switch (ctl->ctl_name)
{
case CTL_SMC_FORCOL:
*valp = smc_get_regbit(0, ioaddr, TCR_REG, TCR_FORCOL);
break;
case CTL_SMC_FREEMEM:
*valp = ( (word)smc_get_reg(0, ioaddr, MIR_REG) >> 8 )
* LAN91C111_MEMORY_MULTIPLIER;
break;
case CTL_SMC_TOTMEM:
*valp = ( smc_get_reg(0, ioaddr, MIR_REG) & (word)0x00ff )
* LAN91C111_MEMORY_MULTIPLIER;
break;
case CTL_SMC_CHIPREV:
*valp = smc_get_reg(3, ioaddr, REV_REG);
break;
case CTL_SMC_AFDUPLX:
*valp = (lp->lastPhy18 & PHY_INT_DPLXDET) ? 1 : 0;
break;
case CTL_SMC_ASPEED:
*valp = (lp->lastPhy18 & PHY_INT_SPDDET) ? 100 : 10;
break;
case CTL_SMC_LNKFAIL:
*valp = (lp->lastPhy18 & PHY_INT_LNKFAIL) ? 1 : 0;
break;
case CTL_SMC_LEDA:
*valp = (lp->rpc_cur_mode >> RPC_LSXA_SHFT) & (word)0x0007;
break;
case CTL_SMC_LEDB:
*valp = (lp->rpc_cur_mode >> RPC_LSXB_SHFT) & (word)0x0007;
break;
case CTL_SMC_MIIOP:
*valp = smc_get_regbit(1, ioaddr, CONFIG_REG, CONFIG_EXT_PHY);
break;
#ifdef SMC_DEBUG
case CTL_SMC_REG_BSR: // Bank Select
*valp = smc_get_reg(0, ioaddr, BSR_REG);
break;
case CTL_SMC_REG_TCR: // Transmit Control
*valp = smc_get_reg(0, ioaddr, TCR_REG);
break;
case CTL_SMC_REG_ESR: // EPH Status
*valp = smc_get_reg(0, ioaddr, EPH_STATUS_REG);
break;
case CTL_SMC_REG_RCR: // Receive Control
*valp = smc_get_reg(0, ioaddr, RCR_REG);
break;
case CTL_SMC_REG_CTRR: // Counter
*valp = smc_get_reg(0, ioaddr, COUNTER_REG);
break;
case CTL_SMC_REG_MIR: // Memory Information
*valp = smc_get_reg(0, ioaddr, MIR_REG);
break;
case CTL_SMC_REG_RPCR: // Receive/Phy Control
*valp = smc_get_reg(0, ioaddr, RPC_REG);
break;
case CTL_SMC_REG_CFGR: // Configuration
*valp = smc_get_reg(1, ioaddr, CONFIG_REG);
break;
case CTL_SMC_REG_BAR: // Base Address
*valp = smc_get_reg(1, ioaddr, BASE_REG);
break;
case CTL_SMC_REG_IAR0: // Individual Address
*valp = smc_get_reg(1, ioaddr, ADDR0_REG);
break;
case CTL_SMC_REG_IAR1: // Individual Address
*valp = smc_get_reg(1, ioaddr, ADDR1_REG);
break;
case CTL_SMC_REG_IAR2: // Individual Address
*valp = smc_get_reg(1, ioaddr, ADDR2_REG);
break;
case CTL_SMC_REG_GPR: // General Purpose
*valp = smc_get_reg(1, ioaddr, GP_REG);
break;
case CTL_SMC_REG_CTLR: // Control
*valp = smc_get_reg(1, ioaddr, CTL_REG);
break;
case CTL_SMC_REG_MCR: // MMU Command
*valp = smc_get_reg(2, ioaddr, MMU_CMD_REG);
break;
case CTL_SMC_REG_PNR: // Packet Number
*valp = smc_get_reg(2, ioaddr, PN_REG);
break;
case CTL_SMC_REG_FPR: // Allocation Result/FIFO Ports
*valp = smc_get_reg(2, ioaddr, RXFIFO_REG);
break;
case CTL_SMC_REG_PTR: // Pointer
*valp = smc_get_reg(2, ioaddr, PTR_REG);
break;
case CTL_SMC_REG_DR: // Data
*valp = smc_get_reg(2, ioaddr, DATA_REG);
break;
case CTL_SMC_REG_ISR: // Interrupt Status/Mask
*valp = smc_get_reg(2, ioaddr, INT_REG);
break;
case CTL_SMC_REG_MTR1: // Multicast Table Entry 1
*valp = smc_get_reg(3, ioaddr, MCAST_REG1);
break;
case CTL_SMC_REG_MTR2: // Multicast Table Entry 2
*valp = smc_get_reg(3, ioaddr, MCAST_REG2);
break;
case CTL_SMC_REG_MTR3: // Multicast Table Entry 3
*valp = smc_get_reg(3, ioaddr, MCAST_REG3);
break;
case CTL_SMC_REG_MTR4: // Multicast Table Entry 4
*valp = smc_get_reg(3, ioaddr, MCAST_REG4);
break;
case CTL_SMC_REG_MIIR: // Management Interface
*valp = smc_get_reg(3, ioaddr, MII_REG);
break;
case CTL_SMC_REG_REVR: // Revision
*valp = smc_get_reg(3, ioaddr, REV_REG
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -