📄 rtl8139.c
字号:
np->twistie = 2; /* Change to state 2. */ next_tick = HZ/10; } else { /* Just put in some reasonable defaults for when beat returns. */ outw(CSCR_LinkDownCmd, ioaddr + CSCR); outl(0x20,ioaddr + FIFOTMS); /* Turn on cable test mode. */ outl(PARA78_default ,ioaddr + PARA78); outl(PARA7c_default ,ioaddr + PARA7c); np->twistie = 0; /* Bail from future actions. */ } } break; case 2: { /* Read how long it took to hear the echo. */ int linkcase = inw(ioaddr + CSCR) & CSCR_LinkStatusBits; if (linkcase == 0x7000) np->twist_row = 3; else if (linkcase == 0x3000) np->twist_row = 2; else if (linkcase == 0x1000) np->twist_row = 1; else np->twist_row = 0; np->twist_col = 0; np->twistie = 3; /* Change to state 2. */ next_tick = HZ/10; } break; case 3: { /* Put out four tuning parameters, one per 100msec. */ if (np->twist_col == 0) outw(0, ioaddr + FIFOTMS); outl(param[(int)np->twist_row][(int)np->twist_col], ioaddr + PARA7c); next_tick = HZ/10; if (++np->twist_col >= 4) { /* For short cables we are done. For long cables (row == 3) check for mistune. */ np->twistie = (np->twist_row == 3) ? 4 : 0; } } break; case 4: { /* Special case for long cables: check for mistune. */ if ((inw(ioaddr + CSCR) & CSCR_LinkStatusBits) == 0x7000) { np->twistie = 0; break; } else { outl(0xfb38de03, ioaddr + PARA7c); np->twistie = 5; next_tick = HZ/10; } } break; case 5: { /* Retune for shorter cable (column 2). */ outl(0x20,ioaddr + FIFOTMS); outl(PARA78_default, ioaddr + PARA78); outl(PARA7c_default, ioaddr + PARA7c); outl(0x00,ioaddr + FIFOTMS); np->twist_row = 2; np->twist_col = 0; np->twistie = 3; next_tick = HZ/10; } break; }#endif if (debug > 2) { if (np->drv_flags & 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)); } np->timer.expires = jiffies + next_tick; add_timer(&np->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 (debug > 0) printk(KERN_ERR "%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(KERN_DEBUG "%s: Tx queue start entry %d dirty entry %d%s.\n", dev->name, tp->cur_tx, tp->dirty_tx, tp->tx_full ? ", full" : ""); 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"); /* Stop a shared interrupt from scavenging while we are. */ tp->dirty_tx = tp->cur_tx = 0; /* Dump the unsent Tx packets. */ for (i = 0; i < NUM_TX_DESC; i++) { if (tp->tx_skbuff[i]) { dev_free_skb(tp->tx_skbuff[i]); tp->tx_skbuff[i] = 0; tp->stats.tx_dropped++; } } rtl_hw_start(dev); netif_unpause_tx_queue(dev); tp->tx_full = 0; 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->dirty_tx = tp->cur_tx = 0; for (i = 0; i < NUM_TX_DESC; i++) { tp->tx_skbuff[i] = 0; tp->tx_buf[i] = &tp->tx_bufs[i*TX_BUF_SIZE]; }}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;#ifdef CONFIG_LEDMAN ledman_cmd(LEDMAN_CMD_SET, (dev->name[3] == '0') ? LEDMAN_LAN1_TX : LEDMAN_LAN2_TX);#endif if (netif_pause_tx_queue(dev) != 0) { /* This watchdog code is redundant with the media monitor timer. */ if (jiffies - dev->trans_start > TX_TIMEOUT) rtl8129_tx_timeout(dev); return 1; } /* Calculate the next Tx descriptor entry. */ entry = tp->cur_tx % NUM_TX_DESC; tp->tx_skbuff[entry] = skb; if ((long)skb->data & 3) { /* Must use alignment buffer. */ memcpy(tp->tx_buf[entry], skb->data, skb->len); outl(virt_to_bus(tp->tx_buf[entry]), ioaddr + TxAddr0 + entry*4); } else outl(virt_to_bus(skb->data), 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); /* There is a race condition here -- we might read dirty_tx, take an interrupt that clears the Tx queue, and only then set tx_full. So we do this in two phases. */ if (++tp->cur_tx - tp->dirty_tx >= NUM_TX_DESC) { set_bit(0, &tp->tx_full); if (tp->cur_tx - (volatile unsigned int)tp->dirty_tx < NUM_TX_DESC) { clear_bit(0, &tp->tx_full); netif_unpause_tx_queue(dev); } else netif_stop_tx_queue(dev); } else netif_unpause_tx_queue(dev); dev->trans_start = jiffies; if (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; long ioaddr = dev->base_addr; int link_changed = 0; /* Grrr, avoid bogus "uninitialized" warning */#if defined(__i386__) && LINUX_VERSION_CODE < 0x20123 /* A lock to prevent simultaneous entry bug on Intel SMP machines. */ if (test_and_set_bit(0, (void*)&dev->interrupt)) { printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n", dev->name); dev->interrupt = 0; /* Avoid halting machine. */ return; }#endif do { int 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) link_changed = inw(ioaddr+CSCR) & CSCR_LinkChangeBit; outw(status, ioaddr + IntrStatus); if (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 (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, 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++; } /* Free the original skb. */ dev_free_skb_irq(tp->tx_skbuff[entry]); tp->tx_skbuff[entry] = 0; if (test_bit(0, &tp->tx_full)) { /* The ring is no longer full, clear tbusy. */ clear_bit(0, &tp->tx_full); netif_resume_tx_queue(dev); } dirty_tx++; }#ifndef final_version if (tp->cur_tx - dirty_tx > NUM_TX_DESC) { printk(KERN_ERR"%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n", dev->name, dirty_tx, tp->cur_tx, (int)tp->tx_full); dirty_tx += NUM_TX_DESC; }#endif tp->dirty_tx = dirty_tx; } /* Check uncommon events with one test. */ if (status & (PCIErr|PCSTimeout |RxUnderrun|RxOverflow|RxFIFOOver |TxErr|RxErr)) { if (debug > 2) printk(KERN_NOTICE"%s: Abnormal interrupt, status %8.8x.\n", dev->name, status); if (status == 0xffff) break; /* Update the error count. */ tp->stats.rx_missed_errors += inl(ioaddr + RxMissed); outl(0, ioaddr + RxMissed); if ((status & RxUnderrun) && link_changed && (tp->drv_flags & HAS_LNK_CHNG)) { /* Really link-change on new chips. */ int lpar = inw(ioaddr + NWayLPAR); int duplex = (lpar&0x0100) || (lpar & 0x01C0) == 0x0040 || tp->duplex_lock; if (debug > 1) printk(KERN_DEBUG "%s: Link changed, link partner " "%4.4x new duplex %d.\n", dev->name, lpar, duplex); if (tp->full_duplex != duplex) { tp->full_duplex = duplex; outb(0xC0, ioaddr + Cfg9346); outb(tp->full_duplex ? 0x60 : 0x20, ioaddr + Config1); outb(0x00, ioaddr + Cfg9346); } status &= ~RxUnderrun; } if (status & (RxUnderrun | RxOverflow | RxErr | RxFIFOOver)) tp->stats.rx_errors++; if (status & (PCSTimeout)) tp->stats.rx_length_errors++; if (status & (RxUnderrun|RxFIFOOver)) tp->stats.rx_fifo_errors++; if (status & RxOverflow) { tp->stats.rx_over_errors++; tp->cur_rx = inw(ioaddr + RxBufAddr) % tp->rx_buf_len; outw(tp->cur_rx - 16, ioaddr + RxBufPtr); } if (status & PCIErr) { u32 pci_cmd_status; pci_read_config_dword(tp->pci_dev, PCI_COMMAND, &pci_cmd_status); printk(KERN_ERR "%s: PCI Bus error %4.4x.\n", dev->name, pci_cmd_status); } } if (--boguscnt < 0) { printk(KERN_WARNING"%s: Too much work at interrupt, " "IntrStatus=0x%4.4x.\n", dev->name, status); /* Clear all interrupt sources. */ outw(0xffff, ioaddr + IntrStatus); break; } } while (1); if (debug > 3) printk(KERN_DEBUG"%s: exiting interrupt, intr_status=%#4.4x.\n", dev->name, inw(ioaddr + IntrStatus));#if defined(__i386__) && LINUX_VERSION_CODE < 0x20123 clear_bit(0, (void*)&dev->interrupt);#endif return;}/* The data sheet doesn't describe the Rx ring at all, so I'm guessing at the field alignments and semantics. */static int rtl8129_rx(struct net_device *dev){ struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; long ioaddr = dev->base_addr; unsigned char *rx_ring = tp->rx_ring; u16 cur_rx = tp->cur_rx; if (debug > 4) printk(KERN_DEBUG"%s: In rtl8129_rx(), current %4.4x BufAddr %4.4x," " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx, inw(ioaddr + RxBufAddr), inw(ioaddr + RxBufPtr), inb(ioaddr + ChipCmd));#ifdef CONFIG_LEDMAN ledman_cmd(LEDMAN_CMD_SET, (dev->name[3] == '0') ? LEDMAN_LAN1_RX : LEDMAN_LAN2_RX);#endif while ((inb(ioaddr + ChipCmd) & RxBufEmpty) == 0) { int ring_offset = cur_rx % tp->rx_buf_len; u32 rx_status = le32_to_cpu(*(u32*)(rx_ring + ring_offset)); int rx_size = rx_status >> 16; /* Includes the CRC. */ if (debug > 4) { int i; printk(KERN_DEBUG"%s: rtl8129_rx() status %4.4x, size %4.4x," " cur %4.4x.\n", dev->name, rx_status, rx_size, cur_rx); printk(KERN_DEBUG"%s: Frame contents ", dev->name); for (i = 0; i < 70; i++) printk(" %2.2x", rx_ring[ring_offset + i]); printk(".\n"); } if (rx_status & (RxBadSymbol|RxRunt|RxTooLong|RxCRCErr|RxBadAlign)) { if (debug > 1) printk(KERN_DEBUG"%s: Ethernet frame had errors," " status %8.8x.\n", dev->name, rx_status); if (rx_status == 0xffffffff) { if (debug > 0) printk(KERN_NOTICE"%s: Invalid receive status at ring " "offset %4.4x\n", dev->name, ring_offset); rx_status = 0; } if (rx_status & RxTooLong) { if (debug > 0) printk(KERN_NOTICE"%s: Oversized Ethernet frame, status %4.4x!\n", dev->name, rx_status); /* A.C.: The chip hangs here. */ }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -