3c501.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 941 行 · 第 1/2 页
C
941 行
while(pad--) /* Zero fill buffer tail */ outb(0, DATAPORT); } outw(gp_start, GP_LOW); /* the board reuses the same register */ if(lp->loading != 2) { outb(AX_XMIT, AX_CMD); /* fire ... Trigger xmit. */ lp->loading=0; dev->trans_start = jiffies; if (el_debug > 2) printk(KERN_DEBUG " queued xmit.\n"); dev_kfree_skb (skb); return 0; } /* A receive upset our load, despite our best efforts */ if(el_debug>2) printk(KERN_DEBUG "%s: burped during tx load.\n", dev->name); spin_lock_irqsave(&lp->lock, flags); } while(1);}/** * el_interrupt: * @irq: Interrupt number * @dev_id: The 3c501 that burped * @regs: Register data (surplus to our requirements) * * Handle the ether interface interrupts. The 3c501 needs a lot more * hand holding than most cards. In particular we get a transmit interrupt * with a collision error because the board firmware isnt capable of rewinding * its own transmit buffer pointers. It can however count to 16 for us. * * On the receive side the card is also very dumb. It has no buffering to * speak of. We simply pull the packet out of its PIO buffer (which is slow) * and queue it for the kernel. Then we reset the card for the next packet. * * We sometimes get suprise interrupts late both because the SMP IRQ delivery * is message passing and because the card sometimes seems to deliver late. I * think if it is part way through a receive and the mode is changed it carries * on receiving and sends us an interrupt. We have to band aid all these cases * to get a sensible 150kbytes/second performance. Even then you want a small * TCP window. */static irqreturn_t el_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct net_device *dev = dev_id; struct net_local *lp; int ioaddr; int axsr; /* Aux. status reg. */ ioaddr = dev->base_addr; lp = netdev_priv(dev); spin_lock(&lp->lock); /* * What happened ? */ axsr = inb(AX_STATUS); /* * Log it */ if (el_debug > 3) printk(KERN_DEBUG "%s: el_interrupt() aux=%#02x", dev->name, axsr); if(lp->loading==1 && !lp->txing) printk(KERN_WARNING "%s: Inconsistent state loading while not in tx\n", dev->name); if (lp->txing) { /* * Board in transmit mode. May be loading. If we are * loading we shouldn't have got this. */ int txsr = inb(TX_STATUS); if(lp->loading==1) { if(el_debug > 2) { printk(KERN_DEBUG "%s: Interrupt while loading [", dev->name); printk(KERN_DEBUG " txsr=%02x gp=%04x rp=%04x]\n", txsr, inw(GP_LOW),inw(RX_LOW)); } lp->loading=2; /* Force a reload */ spin_unlock(&lp->lock); goto out; } if (el_debug > 6) printk(KERN_DEBUG " txsr=%02x gp=%04x rp=%04x", txsr, inw(GP_LOW),inw(RX_LOW)); if ((axsr & 0x80) && (txsr & TX_READY) == 0) { /* * FIXME: is there a logic to whether to keep on trying or * reset immediately ? */ if(el_debug>1) printk(KERN_DEBUG "%s: Unusual interrupt during Tx, txsr=%02x axsr=%02x" " gp=%03x rp=%03x.\n", dev->name, txsr, axsr, inw(ioaddr + EL1_DATAPTR), inw(ioaddr + EL1_RXPTR)); lp->txing = 0; netif_wake_queue(dev); } else if (txsr & TX_16COLLISIONS) { /* * Timed out */ if (el_debug) printk (KERN_DEBUG "%s: Transmit failed 16 times, Ethernet jammed?\n",dev->name); outb(AX_SYS, AX_CMD); lp->txing = 0; lp->stats.tx_aborted_errors++; netif_wake_queue(dev); } else if (txsr & TX_COLLISION) { /* * Retrigger xmit. */ if (el_debug > 6) printk(KERN_DEBUG " retransmitting after a collision.\n"); /* * Poor little chip can't reset its own start pointer */ outb(AX_SYS, AX_CMD); outw(lp->tx_pkt_start, GP_LOW); outb(AX_XMIT, AX_CMD); lp->stats.collisions++; spin_unlock(&lp->lock); goto out; } else { /* * It worked.. we will now fall through and receive */ lp->stats.tx_packets++; if (el_debug > 6) printk(KERN_DEBUG " Tx succeeded %s\n", (txsr & TX_RDY) ? "." : "but tx is busy!"); /* * This is safe the interrupt is atomic WRT itself. */ lp->txing = 0; netif_wake_queue(dev); /* In case more to transmit */ } } else { /* * In receive mode. */ int rxsr = inb(RX_STATUS); if (el_debug > 5) printk(KERN_DEBUG " rxsr=%02x txsr=%02x rp=%04x", rxsr, inb(TX_STATUS),inw(RX_LOW)); /* * Just reading rx_status fixes most errors. */ if (rxsr & RX_MISSED) lp->stats.rx_missed_errors++; else if (rxsr & RX_RUNT) { /* Handled to avoid board lock-up. */ lp->stats.rx_length_errors++; if (el_debug > 5) printk(KERN_DEBUG " runt.\n"); } else if (rxsr & RX_GOOD) { /* * Receive worked. */ el_receive(dev); } else { /* * Nothing? Something is broken! */ if (el_debug > 2) printk(KERN_DEBUG "%s: No packet seen, rxsr=%02x **resetting 3c501***\n", dev->name, rxsr); el_reset(dev); } if (el_debug > 3) printk(KERN_DEBUG ".\n"); } /* * Move into receive mode */ outb(AX_RX, AX_CMD); outw(0x00, RX_BUF_CLR); inb(RX_STATUS); /* Be certain that interrupts are cleared. */ inb(TX_STATUS); spin_unlock(&lp->lock);out: return IRQ_HANDLED;}/** * el_receive: * @dev: Device to pull the packets from * * We have a good packet. Well, not really "good", just mostly not broken. * We must check everything to see if it is good. In particular we occasionally * get wild packet sizes from the card. If the packet seems sane we PIO it * off the card and queue it for the protocol layers. */static void el_receive(struct net_device *dev){ struct net_local *lp = netdev_priv(dev); int ioaddr = dev->base_addr; int pkt_len; struct sk_buff *skb; pkt_len = inw(RX_LOW); if (el_debug > 4) printk(KERN_DEBUG " el_receive %d.\n", pkt_len); if ((pkt_len < 60) || (pkt_len > 1536)) { if (el_debug) printk(KERN_DEBUG "%s: bogus packet, length=%d\n", dev->name, pkt_len); lp->stats.rx_over_errors++; return; } /* * Command mode so we can empty the buffer */ outb(AX_SYS, AX_CMD); skb = dev_alloc_skb(pkt_len+2); /* * Start of frame */ outw(0x00, GP_LOW); if (skb == NULL) { printk(KERN_INFO "%s: Memory squeeze, dropping packet.\n", dev->name); lp->stats.rx_dropped++; return; } else { skb_reserve(skb,2); /* Force 16 byte alignment */ skb->dev = dev; /* * The read increments through the bytes. The interrupt * handler will fix the pointer when it returns to * receive mode. */ insb(DATAPORT, skb_put(skb,pkt_len), pkt_len); skb->protocol=eth_type_trans(skb,dev); netif_rx(skb); dev->last_rx = jiffies; lp->stats.rx_packets++; lp->stats.rx_bytes+=pkt_len; } return;}/** * el_reset: Reset a 3c501 card * @dev: The 3c501 card about to get zapped * * Even resetting a 3c501 isnt simple. When you activate reset it loses all * its configuration. You must hold the lock when doing this. The function * cannot take the lock itself as it is callable from the irq handler. */static void el_reset(struct net_device *dev){ struct net_local *lp = netdev_priv(dev); int ioaddr = dev->base_addr; if (el_debug> 2) printk(KERN_INFO "3c501 reset..."); outb(AX_RESET, AX_CMD); /* Reset the chip */ outb(AX_LOOP, AX_CMD); /* Aux control, irq and loopback enabled */ { int i; for (i = 0; i < 6; i++) /* Set the station address. */ outb(dev->dev_addr[i], ioaddr + i); } outw(0, RX_BUF_CLR); /* Set rx packet area to 0. */ outb(TX_NORM, TX_CMD); /* tx irq on done, collision */ outb(RX_NORM, RX_CMD); /* Set Rx commands. */ inb(RX_STATUS); /* Clear status. */ inb(TX_STATUS); lp->txing = 0;}/** * el1_close: * @dev: 3c501 card to shut down * * Close a 3c501 card. The IFF_UP flag has been cleared by the user via * the SIOCSIFFLAGS ioctl. We stop any further transmissions being queued, * and then disable the interrupts. Finally we reset the chip. The effects * of the rest will be cleaned up by #el1_open. Always returns 0 indicating * a success. */ static int el1_close(struct net_device *dev){ int ioaddr = dev->base_addr; if (el_debug > 2) printk(KERN_INFO "%s: Shutting down Ethernet card at %#x.\n", dev->name, ioaddr); netif_stop_queue(dev); /* * Free and disable the IRQ. */ free_irq(dev->irq, dev); outb(AX_RESET, AX_CMD); /* Reset the chip */ return 0;}/** * el1_get_stats: * @dev: The card to get the statistics for * * In smarter devices this function is needed to pull statistics off the * board itself. The 3c501 has no hardware statistics. We maintain them all * so they are by definition always up to date. * * Returns the statistics for the card from the card private data */ static struct net_device_stats *el1_get_stats(struct net_device *dev){ struct net_local *lp = netdev_priv(dev); return &lp->stats;}/** * set_multicast_list: * @dev: The device to adjust * * Set or clear the multicast filter for this adaptor to use the best-effort * filtering supported. The 3c501 supports only three modes of filtering. * It always receives broadcasts and packets for itself. You can choose to * optionally receive all packets, or all multicast packets on top of this. */static void set_multicast_list(struct net_device *dev){ int ioaddr = dev->base_addr; if(dev->flags&IFF_PROMISC) { outb(RX_PROM, RX_CMD); inb(RX_STATUS); } else if (dev->mc_list || dev->flags&IFF_ALLMULTI) { outb(RX_MULT, RX_CMD); /* Multicast or all multicast is the same */ inb(RX_STATUS); /* Clear status. */ } else { outb(RX_NORM, RX_CMD); inb(RX_STATUS); }}static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info){ strcpy(info->driver, DRV_NAME); strcpy(info->version, DRV_VERSION); sprintf(info->bus_info, "ISA 0x%lx", dev->base_addr);}static u32 netdev_get_msglevel(struct net_device *dev){ return debug;}static void netdev_set_msglevel(struct net_device *dev, u32 level){ debug = level;}static struct ethtool_ops netdev_ethtool_ops = { .get_drvinfo = netdev_get_drvinfo, .get_msglevel = netdev_get_msglevel, .set_msglevel = netdev_set_msglevel,};#ifdef MODULEstatic struct net_device *dev_3c501;MODULE_PARM(io, "i");MODULE_PARM(irq, "i");MODULE_PARM_DESC(io, "EtherLink I/O base address");MODULE_PARM_DESC(irq, "EtherLink IRQ number");/** * init_module: * * When the driver is loaded as a module this function is called. We fake up * a device structure with the base I/O and interrupt set as if it were being * called from Space.c. This minimises the extra code that would otherwise * be required. * * Returns 0 for success or -EIO if a card is not found. Returning an error * here also causes the module to be unloaded */ int init_module(void){ dev_3c501 = el1_probe(-1); if (IS_ERR(dev_3c501)) return PTR_ERR(dev_3c501); return 0;}/** * cleanup_module: * * The module is being unloaded. We unhook our network device from the system * and then free up the resources we took when the card was found. */ void cleanup_module(void){ struct net_device *dev = dev_3c501; unregister_netdev(dev); release_region(dev->base_addr, EL1_IO_EXTENT); free_netdev(dev);}#endif /* MODULE */MODULE_AUTHOR("Donald Becker, Alan Cox");MODULE_DESCRIPTION("Support for the ancient 3Com 3c501 ethernet card");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?