📄 smc91x.c
字号:
smc_phy_check_media(dev, 1);smc_phy_configure_exit: // Exit auto-negotiation spin_unlock_irq(&lp->lock);}/************************************************************************* . smc_phy_interrupt . . Purpose: Handle interrupts relating to PHY register 18. This is . called from the "hard" interrupt handler under our private spinlock. . ************************************************************************/static voidsmc_phy_interrupt(struct net_device* dev){ struct smc_local *lp = (struct smc_local *)dev->priv; int phyaddr = lp->mii.phy_id; int phy18; PRINTK2("%s: %s\n", dev->name, __FUNCTION__); if (lp->phy_type == 0) return; for(;;) { smc_phy_check_media(dev, 0); // Read PHY Register 18, Status Output phy18 = smc_phy_read(dev, phyaddr, PHY_INT_REG); if ((phy18 & PHY_INT_INT) == 0) break; }}//--- END PHY CONTROL AND CONFIGURATION-------------------------------------static void smc_10bt_check_media(struct net_device *dev, int init){ struct smc_local *lp = (struct smc_local *)dev->priv; unsigned long ioaddr = dev->base_addr; unsigned int old_carrier, new_carrier, old_bank; old_bank = SMC_CURRENT_BANK(); SMC_SELECT_BANK(0); old_carrier = netif_carrier_ok(dev) ? 1 : 0; new_carrier = SMC_inw(ioaddr, EPH_STATUS_REG) & ES_LINK_OK ? 1 : 0; if (init || (old_carrier != new_carrier)) { if (!new_carrier) { netif_carrier_off(dev); } else { netif_carrier_on(dev); } if (netif_msg_link(lp)) printk(KERN_INFO "%s: link %s\n", dev->name, new_carrier ? "up" : "down"); } SMC_SELECT_BANK(old_bank);}static void smc_eph_interrupt(struct net_device *dev){ unsigned long ioaddr = dev->base_addr; unsigned int old_bank, ctl; smc_10bt_check_media(dev, 0); old_bank = SMC_CURRENT_BANK(); SMC_SELECT_BANK(1); ctl = SMC_GET_CTL(); SMC_SET_CTL(ctl & ~CTL_LE_ENABLE); SMC_SET_CTL(ctl); SMC_SELECT_BANK(old_bank);}/* * This is the main routine of the driver, to handle the device when * it needs some attention. */static irqreturn_tsmc_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct net_device *dev = dev_id; unsigned long ioaddr = dev->base_addr; struct smc_local *lp = (struct smc_local *)dev->priv; int status, mask, timeout, card_stats, int_status; int saved_bank, saved_pointer, handled = 0; PRINTK3("%s: %s\n", dev->name, __FUNCTION__); saved_bank = SMC_CURRENT_BANK(); SMC_SELECT_BANK(2); saved_pointer = SMC_GET_PTR(); mask = SMC_GET_INT_MASK(); SMC_SET_INT_MASK( 0 ); /* set a timeout value, so I don't stay here forever */ timeout = 8; do { int_status = status = SMC_GET_INT(); PRINTK2("%s: IRQ 0x%02x MASK 0x%02x MEM 0x%04x FIFO 0x%04x\n", dev->name, status, mask, ({ int meminfo; SMC_SELECT_BANK(0); meminfo = SMC_GET_MIR(); SMC_SELECT_BANK(2); meminfo; }), SMC_GET_FIFO()); status &= mask; if (!status) break; spin_lock(&lp->lock); handled = 1; if (status & IM_RCV_INT) { PRINTK3("%s: RX irq\n", dev->name); smc_rcv(dev); } else if (status & IM_TX_INT) { PRINTK3("%s: TX int\n", dev->name); smc_tx(dev); SMC_ACK_INT( IM_TX_INT );#if THROTTLE_TX_PKTS netif_wake_queue(dev);#endif } else if (status & IM_ALLOC_INT) { PRINTK3("%s: Allocation irq\n", dev->name); smc_hardware_send_packet(dev); mask |= (IM_TX_INT | IM_TX_EMPTY_INT); mask &= ~IM_ALLOC_INT;#if ! THROTTLE_TX_PKTS netif_wake_queue(dev);#endif } else if (status & IM_TX_EMPTY_INT) { PRINTK3("%s: TX empty\n", dev->name); mask &= ~IM_TX_EMPTY_INT; /* update stats */ SMC_SELECT_BANK( 0 ); card_stats = SMC_GET_COUNTER(); SMC_SELECT_BANK( 2 ); /* single collisions */ lp->stats.collisions += card_stats & 0xF; card_stats >>= 4; /* multiple collisions */ lp->stats.collisions += card_stats & 0xF; } else if (status & IM_RX_OVRN_INT) { PRINTK1( "%s: RX overrun\n", dev->name); SMC_ACK_INT( IM_RX_OVRN_INT ); lp->stats.rx_errors++; lp->stats.rx_fifo_errors++; } else if (status & IM_EPH_INT) { smc_eph_interrupt(dev); } else if (status & IM_MDINT) { SMC_ACK_INT( IM_MDINT ); smc_phy_interrupt(dev); } else if (status & IM_ERCV_INT ) { SMC_ACK_INT( IM_ERCV_INT ); PRINTK("%s: UNSUPPORTED: ERCV INTERRUPT \n", dev->name); } spin_unlock(&lp->lock); } while (--timeout); /* restore register states */ SMC_SET_INT_MASK( mask ); SMC_SET_PTR( saved_pointer ); SMC_SELECT_BANK( saved_bank ); PRINTK3("%s: Interrupt done\n", dev->name); //return IRQ_RETVAL(handled); if(!handled) printk(KERN_DEBUG "Invalid int status 0x%x, mask 0x%x\n", int_status, mask); return IRQ_HANDLED;}/* Our watchdog timed out. Called by the networking layer */static voidsmc_timeout(struct net_device *dev){ struct smc_local *lp = (struct smc_local *)dev->priv; PRINTK2("%s: %s\n", dev->name, __FUNCTION__); smc_reset(dev); smc_enable(dev);#if 0 /* Reconfiguring the PHY doesn't seem like a bad idea here, but * it introduced a problem. Now that this is a timeout routine, * we are getting called from within an interrupt context. * smc_phy_configure() calls smc_wait_ms() which calls * schedule_timeout() which calls schedule(). When schedule() * is called from an interrupt context, it prints out * "Scheduling in interrupt" and then calls BUG(). This is * obviously not desirable. This was worked around by removing * the call to smc_phy_configure() here because it didn't seem * absolutely necessary. Ultimately, if smc_wait_ms() is * supposed to be usable from an interrupt context (which it * looks like it thinks it should handle), it should be fixed. */ /* Reconfigure the PHY */ if (lp->phy_type != 0) smc_phy_configure(dev);#endif /* clear anything saved */ if (lp->saved_skb != NULL) { dev_kfree_skb (lp->saved_skb); lp->saved_skb = NULL; lp->stats.tx_errors++; lp->stats.tx_aborted_errors++; } dev->trans_start = jiffies; netif_wake_queue(dev);}/* . 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 voidsmc_setmulticast(unsigned long 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 */ static unsigned char invert3[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; /* 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_le(~0, 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 ); SMC_SET_MCAST( multicast_table );}/* . 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){ struct smc_local *lp = (struct smc_local *)dev->priv; unsigned long ioaddr = dev->base_addr; PRINTK2("%s: %s\n", dev->name, __FUNCTION__); SMC_SELECT_BANK(0); if ( dev->flags & IFF_PROMISC ) { PRINTK2("%s: RCR_PRMS\n", dev->name); lp->rcr_cur_mode |= RCR_PRMS; SMC_SET_RCR( lp->rcr_cur_mode ); }/* 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) { lp->rcr_cur_mode |= RCR_ALMUL; SMC_SET_RCR( lp->rcr_cur_mode ); PRINTK2("%s: 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 */ lp->rcr_cur_mode &= ~(RCR_PRMS | RCR_ALMUL); SMC_SET_RCR( lp->rcr_cur_mode ); /* 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: ~(RCR_PRMS|RCR_ALMUL)\n", dev->name); lp->rcr_cur_mode &= ~(RCR_PRMS | RCR_ALMUL); SMC_SET_RCR( lp->rcr_cur_mode ); /* since I'm disabling all multicast entirely, I need to clear the multicast list */ SMC_SELECT_BANK( 3 ); SMC_CLEAR_MCAST(); }}/* * Open and Initialize the board * * Set up everything, reset the card, etc .. */static intsmc_open(struct net_device *dev){ struct smc_local *lp = (struct smc_local *)dev->priv; unsigned long ioaddr = dev->base_addr; PRINTK2("%s: %s\n", dev->name, __FUNCTION__); /* * Check that the address is valid. If its not, refuse * to bring the device up. The user must specify an * address using ifconfig eth0 hw ether xx:xx:xx:xx:xx:xx */ if (!is_valid_ether_addr(dev->dev_addr)) { PRINTK2((KERN_DEBUG "smc_open: no valid ethernet hw addr\n")); return -EINVAL; } /* clear out all the junk that was put here before... */ lp->saved_skb = NULL; // Setup the default Register Modes lp->tcr_cur_mode = TCR_DEFAULT; lp->rcr_cur_mode = RCR_DEFAULT; lp->rpc_cur_mode = RPC_DEFAULT; /* * If we are not using a MII interface, we need to * monitor our own carrier signal to detect faults. */ if (lp->phy_type == 0) lp->tcr_cur_mode |= TCR_MON_CSN; /* reset the hardware */ smc_reset(dev); smc_enable(dev); SMC_SELECT_BANK( 1 ); SMC_SET_MAC_ADDR(dev->dev_addr); /* Configure the PHY */ if (lp->phy_type != 0) smc_phy_configure(dev); else { spin_lock_irq(&lp->lock); smc_10bt_check_media(dev, 1); spin_unlock_irq(&lp->lock); } netif_start_queue(dev); return 0;}/*---------------------------------------------------- . 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 intsmc_close(struct net_device *dev){ struct smc_local *lp = (struct smc_local *)dev->priv; PRINTK2("%s: %s\n", dev->name, __FUNCTION__); netif_stop_queue(dev); netif_carrier_off(dev); /* clear everything */ smc_shutdown(dev->base_addr); spin_lock_irq(&lp->lock); if (lp->phy_type != 0) smc_phy_powerdown(dev, lp->mii.phy_id); spin_unlock_irq(&lp->lock); 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: %s\n", dev->name, __FUNCTION__); return &lp->stats;}/*------------------------------------------------------------ . Ethtool support .-------------------------------------------------------------*/static intsmc_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd){ struct smc_local *lp = (struct smc_local *)dev->priv; int ret; cmd->maxtxpkt = 1; cmd->maxrxpkt = 1; if (lp->phy_type != 0) { spin_lock_irq(&lp->lock); ret = mii_ethtool_gset(&lp->mii, cmd); spin_unlock_irq(&lp->lock); } else { cmd->supported = SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_TP | SUPPORTED_AUI; if (lp->ctl_rspeed == 10) cmd->speed = SPEED_10; else if (lp->ctl_rspeed == 100) cmd->speed = SPEED_100; cmd->autoneg = AUTONEG_DISABLE; cmd->transceiver = XCVR_INTERNAL; cmd->port = 0; cmd->duplex = lp->tcr_cur_mode & TCR_SWFDUP ? DUPLEX_FULL : DUPLEX_HALF; ret = 0; } return ret;}static intsmc_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd){ struct smc_local *lp = (struct smc_local *)dev->priv; int ret; if (lp->phy_type != 0) { spin_lock_irq(&lp->lock); ret = mii_ethtool_sset(&lp->mii, cmd); spin_unlock_irq(&lp->lock); } else { if (cmd->autoneg != AUTONEG_DISABLE || cmd->speed != SPEED_10 || (cmd->duplex != DUPLEX_HALF && cmd->duplex != DUPLEX_FULL) || (cmd->port != PORT_TP && cmd->port != PORT_AUI)) return -EINVAL;// lp->port = cmd->port; lp->ctl_rfduplx = cmd->duplex == DUPLEX_FULL;// if (netif_running(dev))// smc_set_port(dev); ret = 0; } return ret;}static voidsmc_ethtool_getdrvinfo(struct net_device *dev, struct ethtool_drvinfo *info){ strncpy(info->driver, CARDNAME, sizeof(info->driver)); strncpy(info->version, version, sizeof(info->version)); strncpy(info->bus_info, dev->class_dev.dev->bus_id, sizeof(info->bus_info));}static int smc_ethtool_nwayreset(struct net_device *dev){ struct smc_local *lp = (struct smc_local *)dev->priv; int ret = -EINVAL; if (lp->phy_type != 0) { spin_lock_irq(&lp->lock); ret = mii_nway_restart(&lp->mii); spin_unlock_irq(&lp->lock); } return ret;}static u32 smc_ethtool_getmsglevel(struct net_device *dev){ struct smc_local *lp = (struct smc_local *)dev->priv; return lp->msg_enable;}static void smc_ethtool_setmsglevel(struct net_device *dev, u32 level){ struct smc_local *lp = (struct smc_local *)dev->priv; lp->msg_enable = level;}static struct ethtool_ops smc_ethtool_ops = { .get_settings = smc_ethtool_getsettings, .set_settings = smc_ethtool_setsettings, .get_drvinfo = smc_ethtool_getdrvinfo, .get_msglevel = smc_ethtool_getmsglevel, .set_msglevel = smc_ethtool_setmsglevel, .nway_reset = smc_ethtool_nwayreset,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -