📄 rtl8139.c
字号:
tp->tx_full = 1; } dev->trans_start = jiffies; if (rtl8129_debug > 4) printk(KERN_DEBUG"%s: Queued Tx packet at %p size %ld to slot %d.\n", dev->name, skb->data, 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 device *dev = (struct device *)dev_instance; struct rtl8129_private *tp; int ioaddr, boguscnt = max_interrupt_work; int status; if (dev == NULL) { printk (KERN_ERR"rtl8139_interrupt(): IRQ %d for unknown device.\n", irq); return; } ioaddr = dev->base_addr; tp = (struct rtl8129_private *)dev->priv; if (test_and_set_bit(0, (void*)&tp->in_interrupt)) { printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name); return; } dev->interrupt = 1; do { status = inw(ioaddr + IntrStatus); /* Acknowledge all of the current interrupt sources ASAP. */ 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; for (dirty_tx = tp->dirty_tx; dirty_tx < tp->cur_tx; dirty_tx++) { int entry = dirty_tx % NUM_TX_DESC; int txstatus = inl(ioaddr + TxStat0 + entry*4); if ( ! (txstatus & TxHostOwns)) 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. */#ifndef final_version if (rtl8129_debug > 1) printk(KERN_NOTICE"%s: Transmit error, Tx status %8.8x.\n", dev->name, txstatus);#endif 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 {#ifdef ETHER_STATS /* No count for tp->stats.tx_deferred */#endif if (txstatus & TxUnderrun) { /* Todo: increase the Tx FIFO threshold. */ 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_kfree_skb(tp->tx_skbuff[entry], FREE_WRITE); tp->tx_skbuff[entry] = 0; }#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, tp->tx_full); dirty_tx += NUM_TX_DESC; }#endif if (tp->tx_full && dev->tbusy && dirty_tx > tp->cur_tx - NUM_TX_DESC) { /* The ring is no longer full, clear tbusy. */ tp->tx_full = 0; dev->tbusy = 0; mark_bh(NET_BH); } tp->dirty_tx = dirty_tx; } /* Check uncommon events with one test. */ if (status & (PCIErr|PCSTimeout |RxUnderrun|RxOverflow|RxFIFOOver |TxErr|RxErr)) { /* Update the error count. */ tp->stats.rx_missed_errors += inl(ioaddr + RxMissed); outl(0, ioaddr + RxMissed); 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) % RX_BUF_LEN; outw(tp->cur_rx - 16, ioaddr + RxBufPtr); } /* Error sources cleared above. */ } 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 (rtl8129_debug > 3) printk(KERN_DEBUG"%s: exiting interrupt, intr_status=%#4.4x.\n", dev->name, inl(ioaddr + IntrStatus)); dev->interrupt = 0; clear_bit(0, (void*)&tp->in_interrupt); return;}/* The data sheet doesn't describe the Rx ring at all, so I'm guessing at the field alignments and semantics. */static intrtl8129_rx(struct device *dev){ struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; int ioaddr = dev->base_addr; unsigned char *rx_ring = tp->rx_ring; u16 cur_rx = tp->cur_rx; if (rtl8129_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)); while ((inb(ioaddr + ChipCmd) & 1) == 0) { int ring_offset = cur_rx % RX_BUF_LEN; u32 rx_status = *(u32*)(rx_ring + ring_offset); int rx_size = rx_status >> 16; if (rtl8129_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 & RxTooLong) { if (rtl8129_debug > 0) printk(KERN_NOTICE"%s: Oversized Ethernet frame, status %4.4x!\n", dev->name, rx_status); tp->stats.rx_length_errors++; } else if (rx_status & (RxBadSymbol|RxRunt|RxTooLong|RxCRCErr|RxBadAlign)) { if (rtl8129_debug > 1) printk(KERN_DEBUG"%s: Ethernet frame had errors," " status %4.4x.\n", dev->name, rx_status); tp->stats.rx_errors++; if (rx_status & (RxBadSymbol|RxBadAlign)) tp->stats.rx_frame_errors++; if (rx_status & (RxRunt|RxTooLong)) tp->stats.rx_length_errors++; if (rx_status & RxCRCErr) tp->stats.rx_crc_errors++; /* Reset the receiver, based on RealTek recommendation. (Bug?) */ tp->cur_rx = 0; outb(CmdTxEnb, ioaddr + ChipCmd); outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); outl((RX_FIFO_THRESH << 13) | (RX_BUF_LEN_IDX << 11) | (RX_DMA_BURST<<8), ioaddr + RxConfig); } else { /* Malloc up new buffer, compatible with net-2e. */ /* Omit the four octet CRC from the length. */ struct sk_buff *skb; skb = dev_alloc_skb(rx_size + 2); if (skb == NULL) { printk(KERN_WARNING"%s: Memory squeeze, deferring packet.\n", dev->name); /* We should check that some rx space is free. If not, free one and mark stats->rx_dropped++. */ tp->stats.rx_dropped++; break; } skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP fields. */ if (ring_offset+rx_size+4 > RX_BUF_LEN) { int semi_count = RX_BUF_LEN - ring_offset - 4; memcpy(skb_put(skb, semi_count), &rx_ring[ring_offset + 4], semi_count); memcpy(skb_put(skb, rx_size-semi_count), rx_ring, rx_size-semi_count); if (rtl8129_debug > 4) { int i; printk(KERN_DEBUG"%s: Frame wrap @%d", dev->name, semi_count); for (i = 0; i < 16; i++) printk(" %2.2x", rx_ring[i]); printk(".\n"); memset(rx_ring, 0xcc, 16); } } else memcpy(skb_put(skb, rx_size), &rx_ring[ring_offset + 4], rx_size); skb->protocol = eth_type_trans(skb, dev); netif_rx(skb);#if LINUX_VERSION_CODE > 0x20119 tp->stats.rx_bytes += rx_size;#endif tp->stats.rx_packets++; } cur_rx += rx_size + 4; cur_rx = (cur_rx + 3) & ~3; outw(cur_rx - 16, ioaddr + RxBufPtr); } if (rtl8129_debug > 4) printk(KERN_DEBUG"%s: Done 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)); tp->cur_rx = cur_rx; return 0;}static intrtl8129_close(struct device *dev){ int ioaddr = dev->base_addr; struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; int i; dev->start = 0; dev->tbusy = 1; if (rtl8129_debug > 1) printk(KERN_DEBUG"%s: Shutting down ethercard, status was 0x%4.4x.\n", dev->name, inw(ioaddr + IntrStatus)); /* Disable interrupts by clearing the interrupt mask. */ outw(0x0000, ioaddr + IntrMask); /* Stop the chip's Tx and Rx DMA processes. */ outb(0x00, ioaddr + ChipCmd); /* Update the error counts. */ tp->stats.rx_missed_errors += inl(ioaddr + RxMissed); outl(0, ioaddr + RxMissed); del_timer(&tp->timer); free_irq(dev->irq, dev); for (i = 0; i < NUM_TX_DESC; i++) { if (tp->tx_skbuff[i]) dev_kfree_skb(tp->tx_skbuff[i], FREE_WRITE); tp->tx_skbuff[i] = 0; } kfree(tp->rx_ring); kfree(tp->tx_bufs); /* Green! Put the chip in low-power mode. */ outb(0xC0, ioaddr + Cfg9346); outb(0x03, ioaddr + Config1); outb('H', ioaddr + HltClk); /* 'R' would leave the clock running. */ MOD_DEC_USE_COUNT; return 0;}static struct enet_statistics *rtl8129_get_stats(struct device *dev){ struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; int ioaddr = dev->base_addr; if (dev->start) { tp->stats.rx_missed_errors += inl(ioaddr + RxMissed); outl(0, ioaddr + RxMissed); } return &tp->stats;}/* Set or clear the multicast filter for this adaptor. Note that we only use exclusion around actually queueing the new frame, not around filling tp->setup_frame. This is non-deterministic when re-entered but still correct. *//* The little-endian AUTODIN II ethernet CRC calculation. N.B. Do not use for bulk data, use a table-based routine instead. This is common code and should be moved to net/core/crc.c */static unsigned const ethernet_polynomial_le = 0xedb88320U;static inline unsigned ether_crc_le(int length, unsigned char *data){ unsigned int crc = 0xffffffff; /* Initial value. */ while(--length >= 0) { unsigned char current_octet = *data++; int bit; for (bit = 8; --bit >= 0; current_octet >>= 1) { if ((crc ^ current_octet) & 1) { crc >>= 1; crc ^= ethernet_polynomial_le; } else crc >>= 1; } } return crc;}static void set_rx_mode(struct device *dev){ int ioaddr = dev->base_addr; struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; unsigned char mc_filter[8]; /* Multicast hash filter */ int i; if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ /* Unconditionally log net taps. */ printk(KERN_NOTICE"%s: Promiscuous mode enabled.\n", dev->name); memset(mc_filter, 0xff, sizeof(mc_filter)); outb(0x0F, ioaddr + RxConfig); } else if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) { /* Too many to filter perfectly -- accept all multicasts. */ memset(mc_filter, 0xff, sizeof(mc_filter)); outb(0x0E, ioaddr + RxConfig); } else if (dev->mc_count == 0) { outb(0x0A, ioaddr + RxConfig); return; } else { struct dev_mc_list *mclist; memset(mc_filter, 0, sizeof(mc_filter)); for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; i++, mclist = mclist->next) set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f, mc_filter); } /* ToDo: perhaps we need to stop the Tx and Rx process here? */ if (memcmp(mc_filter, tp->mc_filter, sizeof(mc_filter))) { for (i = 0; i < 2; i++) outl(((u32 *)mc_filter)[i], ioaddr + MAR0 + i*4); memcpy(tp->mc_filter, mc_filter, sizeof(mc_filter)); } if (rtl8129_debug > 3) printk(KERN_DEBUG"%s: set_rx_mode(%4.4x) done -- Rx config %8.8x.\n", dev->name, dev->flags, inl(ioaddr + RxConfig)); return;}#ifdef MODULE/* An additional parameter that may be passed in... */static int debug = -1;intinit_module(void){ int cards_found; if (debug >= 0) rtl8129_debug = debug; root_rtl8129_dev = NULL; cards_found = rtl8139_probe(0); return cards_found ? 0 : -ENODEV;}voidcleanup_module(void){ struct device *next_dev; /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ while (root_rtl8129_dev) { next_dev = ((struct rtl8129_private *)root_rtl8129_dev->priv)->next_module; unregister_netdev(root_rtl8129_dev); release_region(root_rtl8129_dev->base_addr, RTL8129_TOTAL_SIZE); kfree(root_rtl8129_dev); root_rtl8129_dev = next_dev; }}#endif /* MODULE *//* * Local variables: * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c rtl8139.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c rtl8139.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -