📄 rtl8129.c
字号:
outb(0xC0, ioaddr + Cfg9346); outb(tp->full_duplex ? 0x60 : 0x20, ioaddr + Config1); outb(0x00, ioaddr + Cfg9346); outl(tp->rx_ring_dma, ioaddr + RxBuf); /* Start the chip's Tx and Rx process. */ outl(0, ioaddr + RxMissed); set_rx_mode(dev); outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); /* Enable all known interrupts by setting the interrupt mask. */ outw(PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver | TxErr | TxOK | RxErr | RxOK, ioaddr + IntrMask); if (rtl8129_debug > 1) printk(KERN_DEBUG"%s: rtl8129_open() ioaddr %#lx IRQ %d" " GP Pins %2.2x %s-duplex.\n", dev->name, ioaddr, dev->irq, inb(ioaddr + GPPinData), tp->full_duplex ? "full" : "half"); /* Set the timer to switch to check for link beat and perhaps switch to an alternate media type. */ init_timer(&tp->timer); tp->timer.expires = RUN_AT((24*HZ)/10); /* 2.4 sec. */ tp->timer.data = (unsigned long)dev; tp->timer.function = &rtl8129_timer; add_timer(&tp->timer); return 0;}static void rtl8129_timer(unsigned long data){ struct net_device *dev = (struct net_device *)data; struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; long ioaddr = dev->base_addr; int next_tick = 60*HZ; int mii_reg5 = mdio_read(dev, tp->phys[0], 5); if (! tp->duplex_lock && mii_reg5 != 0xffff) { int duplex = (mii_reg5&0x0100) || (mii_reg5 & 0x01C0) == 0x0040; if (tp->full_duplex != duplex) { tp->full_duplex = duplex; printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d link" " partner ability of %4.4x.\n", dev->name, tp->full_duplex ? "full" : "half", tp->phys[0], mii_reg5); outb(0xC0, ioaddr + Cfg9346); outb(tp->full_duplex ? 0x60 : 0x20, ioaddr + Config1); outb(0x00, ioaddr + Cfg9346); } } /* Check for bogusness. */ if (inw(ioaddr + IntrStatus) & (TxOK | RxOK)) { int status = inw(ioaddr + IntrStatus); if (status & (TxOK | RxOK)) { /* Double check */ printk(KERN_ERR "%s: RTL8129 Interrupt line blocked, status %x.\n", dev->name, status); rtl8129_interrupt(dev->irq, dev, 0); } } if (netif_queue_stopped(dev) && (jiffies - dev->trans_start) >= 2*TX_TIMEOUT) rtl8129_tx_timeout(dev);#if 0 if (tp->twistie) { unsigned int CSCRval = inw(ioaddr + CSCR); /* Read link status. */ if (tp->twistie == 1) { if (CSCRval & CSCR_LinkOKBit) { outw(CSCR_LinkDownOffCmd, ioaddr + CSCR); tp->twistie = 2; next_tick = HZ/10; } else { outw(CSCR_LinkDownCmd, ioaddr + CSCR); outl(FIFOTMS_default,ioaddr + FIFOTMS); outl(PARA78_default ,ioaddr + PARA78); outl(PARA7c_default ,ioaddr + PARA7c); tp->twistie = 0; } } else if (tp->twistie == 2) { int linkcase = (CSCRval & CSCR_LinkStatusBits) >> 12; int row; if (linkcase >= 0x7000) row = 3; else if (linkcase >= 0x3000) row = 2; else if (linkcase >= 0x1000) row = 1; else row = 0; tp->twistie == row + 3; outw(0,ioaddr+FIFOTMS); outl(param[row][0], ioaddr+PARA7c); tp->twist_cnt = 1; } else { outl(param[tp->twistie-3][tp->twist_cnt], ioaddr+PARA7c); if (++tp->twist_cnt < 4) { next_tick = HZ/10; } else if (tp->twistie-3 == 3) { if ((CSCRval & CSCR_LinkStatusBits) != 0x7000) { outl(PARA7c_xxx, ioaddr+PARA7c); next_tick = HZ/10; /* 100ms. */ outl(FIFOTMS_default, ioaddr+FIFOTMS); outl(PARA78_default, ioaddr+PARA78); outl(PARA7c_default, ioaddr+PARA7c); tp->twistie == 3 + 3; outw(0,ioaddr+FIFOTMS); outl(param[3][0], ioaddr+PARA7c); tp->twist_cnt = 1; } } } }#endif if (rtl8129_debug > 2) { if (rtl_cap_tbl[tp->chip_id] & HAS_MII_XCVR) printk(KERN_DEBUG"%s: Media selection tick, GP pins %2.2x.\n", dev->name, inb(ioaddr + GPPinData)); else printk(KERN_DEBUG"%s: Media selection tick, Link partner %4.4x.\n", dev->name, inw(ioaddr + NWayLPAR)); printk(KERN_DEBUG"%s: Other registers are IntMask %4.4x IntStatus %4.4x" " RxStatus %4.4x.\n", dev->name, inw(ioaddr + IntrMask), inw(ioaddr + IntrStatus), inl(ioaddr + RxEarlyStatus)); printk(KERN_DEBUG"%s: Chip config %2.2x %2.2x.\n", dev->name, inb(ioaddr + Config0), inb(ioaddr + Config1)); } tp->timer.expires = RUN_AT(next_tick); add_timer(&tp->timer);}static void rtl8129_tx_timeout(struct net_device *dev){ struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; long ioaddr = dev->base_addr; int mii_reg, i; if (rtl8129_debug > 0) printk(KERN_WARNING "%s: Transmit timeout, status %2.2x %4.4x " "media %2.2x.\n", dev->name, inb(ioaddr + ChipCmd), inw(ioaddr + IntrStatus), inb(ioaddr + GPPinData)); /* Disable interrupts by clearing the interrupt mask. */ outw(0x0000, ioaddr + IntrMask); /* Emit info to figure out what went wrong. */ printk("%s: Tx queue start entry %d dirty entry %d.\n", dev->name, tp->cur_tx, tp->dirty_tx); for (i = 0; i < NUM_TX_DESC; i++) printk(KERN_DEBUG"%s: Tx descriptor %d is %8.8x.%s\n", dev->name, i, inl(ioaddr + TxStatus0 + i*4), i == tp->dirty_tx % NUM_TX_DESC ? " (queue head)" : ""); printk(KERN_DEBUG"%s: MII #%d registers are:", dev->name, tp->phys[0]); for (mii_reg = 0; mii_reg < 8; mii_reg++) printk(" %4.4x", mdio_read(dev, tp->phys[0], mii_reg)); printk(".\n"); /* Soft reset the chip. */ outb(CmdReset, ioaddr + ChipCmd); /* Check that the chip has finished the reset. */ for (i = 1000; i > 0; i--) if ((inb(ioaddr + ChipCmd) & CmdReset) == 0) break; for (i = 0; i < 6; i++) outb(dev->dev_addr[i], ioaddr + MAC0 + i); outb(0x00, ioaddr + Cfg9346); tp->cur_rx = 0; /* Must enable Tx/Rx before setting transfer thresholds! */ outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); outl((RX_FIFO_THRESH << 13) | (RX_BUF_LEN_IDX << 11) | (RX_DMA_BURST<<8), ioaddr + RxConfig); outl((TX_DMA_BURST<<8), ioaddr + TxConfig); set_rx_mode(dev); { /* Save the unsent Tx packets. */ struct sk_buff *saved_skb[NUM_TX_DESC], *skb; int j; for (j = 0; tp->cur_tx - tp->dirty_tx > 0 ; j++, tp->dirty_tx++) { struct ring_info *rp = &tp->tx_info[tp->dirty_tx % NUM_TX_DESC]; saved_skb[j] = rp->skb; if (rp->mapping != 0) { pci_unmap_single(tp->pdev, rp->mapping, rp->skb->len, PCI_DMA_TODEVICE); rp->mapping = 0; } } tp->dirty_tx = tp->cur_tx = 0; for (i = 0; i < j; i++) { skb = tp->tx_info[i].skb = saved_skb[i]; if ((long)skb->data & 3) { /* Must use alignment buffer. */ memcpy(tp->tx_buf[i], skb->data, skb->len); outl(tp->tx_bufs_dma + (tp->tx_buf[i] - tp->tx_bufs), ioaddr + TxAddr0 + i*4); } else { tp->tx_info[i].mapping = pci_map_single(tp->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); outl(tp->tx_info[i].mapping, ioaddr + TxAddr0 + i*4); } /* Note: the chip doesn't have auto-pad! */ outl(tp->tx_flag | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN), ioaddr + TxStatus0 + i*4); } tp->cur_tx = i; while (i < NUM_TX_DESC) { tp->tx_info[i].skb = NULL; tp->tx_info[i].mapping = 0; i++; } if (tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */ netif_wake_queue(dev); tp->tx_full = 0; } else { tp->tx_full = 1; netif_stop_queue(dev); } } dev->trans_start = jiffies; tp->stats.tx_errors++; /* Enable all known interrupts by setting the interrupt mask. */ outw(PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver | TxErr | TxOK | RxErr | RxOK, ioaddr + IntrMask); return;}/* Initialize the Rx and Tx rings, along with various 'dev' bits. */static voidrtl8129_init_ring(struct net_device *dev){ struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; int i; tp->tx_full = 0; tp->cur_rx = 0; tp->dirty_tx = tp->cur_tx = 0; for (i = 0; i < NUM_TX_DESC; i++) { tp->tx_buf[i] = &tp->tx_bufs[i*TX_BUF_SIZE]; tp->tx_info[i].skb = NULL; tp->tx_info[i].mapping = 0; }}static intrtl8129_start_xmit(struct sk_buff *skb, struct net_device *dev){ struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; long ioaddr = dev->base_addr; int entry; netif_stop_queue(dev); /* Calculate the next Tx descriptor entry. */ entry = tp->cur_tx % NUM_TX_DESC; tp->tx_info[entry].skb = skb; if ((long)skb->data & 3) { /* Must use alignment buffer. */ tp->tx_info[entry].mapping = 0; memcpy(tp->tx_buf[entry], skb->data, skb->len); outl(tp->tx_bufs_dma + (tp->tx_buf[entry] - tp->tx_bufs), ioaddr + TxAddr0 + entry*4); } else { tp->tx_info[entry].mapping = pci_map_single(tp->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); outl(tp->tx_info[entry].mapping, ioaddr + TxAddr0 + entry*4); } /* Note: the chip doesn't have auto-pad! */ outl(tp->tx_flag | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN), ioaddr + TxStatus0 + entry*4); if (++tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) { /* Typical path */ netif_start_queue(dev); } else { tp->tx_full = 1; } dev->trans_start = jiffies; if (rtl8129_debug > 4) printk(KERN_DEBUG"%s: Queued Tx packet at %p size %d to slot %d.\n", dev->name, skb->data, (int)skb->len, entry); return 0;}/* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */static void rtl8129_interrupt(int irq, void *dev_instance, struct pt_regs *regs){ struct net_device *dev = (struct net_device *)dev_instance; struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; int boguscnt = max_interrupt_work; int status, link_changed = 0; long ioaddr = dev->base_addr; do { status = inw(ioaddr + IntrStatus); /* Acknowledge all of the current interrupt sources ASAP, but an first get an additional status bit from CSCR. */ if ((status & RxUnderrun) && inw(ioaddr+CSCR) & CSCR_LinkChangeBit) link_changed = 1; outw(status, ioaddr + IntrStatus); if (rtl8129_debug > 4) printk(KERN_DEBUG"%s: interrupt status=%#4.4x new intstat=%#4.4x.\n", dev->name, status, inw(ioaddr + IntrStatus)); if ((status & (PCIErr|PCSTimeout|RxUnderrun|RxOverflow|RxFIFOOver |TxErr|TxOK|RxErr|RxOK)) == 0) break; if (status & (RxOK|RxUnderrun|RxOverflow|RxFIFOOver))/* Rx interrupt */ rtl8129_rx(dev); if (status & (TxOK | TxErr)) { unsigned int dirty_tx = tp->dirty_tx; while (tp->cur_tx - dirty_tx > 0) { int entry = dirty_tx % NUM_TX_DESC; int txstatus = inl(ioaddr + TxStatus0 + entry*4); if ( ! (txstatus & (TxStatOK | TxUnderrun | TxAborted))) break; /* It still hasn't been Txed */ /* Note: TxCarrierLost is always asserted at 100mbps. */ if (txstatus & (TxOutOfWindow | TxAborted)) { /* There was an major error, log it. */ if (rtl8129_debug > 1) printk(KERN_NOTICE"%s: Transmit error, Tx status %8.8x.\n", dev->name, txstatus); tp->stats.tx_errors++; if (txstatus&TxAborted) { tp->stats.tx_aborted_errors++; outl((TX_DMA_BURST<<8)|0x03000001, ioaddr + TxConfig); } if (txstatus&TxCarrierLost) tp->stats.tx_carrier_errors++; if (txstatus&TxOutOfWindow) tp->stats.tx_window_errors++;#ifdef ETHER_STATS if ((txstatus & 0x0f000000) == 0x0f000000) tp->stats.collisions16++;#endif } else { if (txstatus & TxUnderrun) { /* Add 64 to the Tx FIFO threshold. */ if (tp->tx_flag < 0x00300000) tp->tx_flag += 0x00020000; tp->stats.tx_fifo_errors++; } tp->stats.collisions += (txstatus >> 24) & 15;#if LINUX_VERSION_CODE > 0x20119 tp->stats.tx_bytes += txstatus & 0x7ff;#endif tp->stats.tx_packets++; } if (tp->tx_info[entry].mapping != 0) { pci_unmap_single(tp->pdev, tp->tx_info[entry].mapping, tp->tx_info[entry].skb->len, PCI_DMA_TODEVICE); tp->tx_info[entry].mapping = 0; } /* Free the original skb. */ dev_kfree_skb_irq(tp->tx_info[entry].skb); tp->tx_info[entry].skb = NULL; if (tp->tx_full) { /* The ring is no longer full, wake the queue. */ tp->tx_full = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -