?? rtl8139.c
字號:
tp->tx_full = 0; tp->cur_rx = 0; tp->dirty_tx = tp->cur_tx = 0; tp->txPtr = tp->txPtr=0; memset(tp->txDescArray, 0x0, NUM_CP_TX_DESC*sizeof(struct CPlusTxDesc)); memset(tp->rxDescArray, 0x0, NUM_CP_RX_DESC*sizeof(struct CPlusRxDesc)); for (i=0 ; i<NUM_CP_TX_DESC ; i++) tp->tx_skbuff[i]=0; for (i=0; i <NUM_CP_RX_DESC; i++) { if(i==NUM_CP_RX_DESC-1) tp->rxDescArray[i].status = 0xC0000000+RX_BUF_SIZE; else tp->rxDescArray[i].status = 0x80000000+RX_BUF_SIZE; tp->rxBufferRing[i]=kmalloc(RX_BUF_SIZE, GFP_KERNEL); tp->rxDescArray[i].buf_addr=virt_to_bus(tp->rxBufferRing[i]); }}/* 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_skbuffa[i] = 0; tp->tx_buf[i] = &tp->tx_bufs[i*TX_BUF_SIZE]; }}static int rtl8139cp_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; if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { if (jiffies - dev->trans_start >= TX_TIMEOUT) rtl8129_tx_timeout(dev); return 1; } entry=tp->cur_tx%NUM_CP_TX_DESC; tp->tx_skbuff[entry] = skb; tp->txDescArray[entry].buf_addr=virt_to_bus(skb->data); if(entry!=NUM_CP_TX_DESC-1) tp->txDescArray[entry].status= 0xB0000000 |(skb->len > ETH_ZLEN ? skb->len : ETH_ZLEN); else tp->txDescArray[entry].status= 0xF0000000 |(skb->len > ETH_ZLEN ? skb->len : ETH_ZLEN); outb(0x40, ioaddr+CPlusTxPoll); if (++tp->cur_tx - tp->dirty_tx < NUM_CP_TX_DESC) /* Typical path */ clear_bit(0, (void*)&dev->tbusy); else { set_bit(0, &tp->tx_full); if (tp->cur_tx - (volatile int)tp->dirty_tx < NUM_CP_TX_DESC) { clear_bit(0, (void*)&dev->tbusy); clear_bit(0, &tp->tx_full); } } 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;}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; /* Block a timer-based transmit from overlapping. This could better be done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { 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_skbuffa[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) /* Typical path */ clear_bit(0, (void*)&dev->tbusy); else { set_bit(0, &tp->tx_full); if (tp->cur_tx - (volatile int)tp->dirty_tx < NUM_TX_DESC) { clear_bit(0, (void*)&dev->tbusy); clear_bit(0, &tp->tx_full); } } 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 rtl8139CP_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;#if defined(__i386__) /* 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; }#else if (dev->interrupt) { printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name); return; } dev->interrupt = 1;#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) && inw(ioaddr+CSCR) & CSCR_LinkChangeBit) { link_changed = inw(ioaddr+CSCR) & CSCR_LinkChangeBit; /* If link OK, disable Link Down Power Saving mode. If link OFF, enable it. This mode is avaiable only for RTL8139C */ if((inb(ioaddr+TxConfig+3) & 0x7C)==0x74) // 0x74 is RTL8139C HW Ver. { if( (inb(ioaddr + MediaStatus) & LINK_Status)==0) outb(inb(ioaddr+Config5) | 0x4, ioaddr+Config5); else outb(inb(ioaddr+Config5) & 0xB, ioaddr+Config5); } } if (status & (RxFIFOOver | RxOverflow) ) outw( RxFIFOOver & RxOverflow, ioaddr + IntrStatus); if (status & TxErr) outl( 0x01, ioaddr + TxConfig); 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 */ rtl8139cp_rx(dev); if (status & (TxErr | TxOK)) { unsigned int dirty_tx = tp->dirty_tx; while (tp->cur_tx - dirty_tx > 0) { int entry = dirty_tx % NUM_CP_TX_DESC; /* Free the original skb. */ dev_free_skb(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); clear_bit(0, (void*)&dev->tbusy); netif_wake_queue(dev); } dirty_tx++; tp->stats.tx_packets++; }#ifndef final_version if (tp->cur_tx - dirty_tx > NUM_CP_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_CP_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 (tp->full_duplex != duplex) { tp->full_duplex = duplex; outb(Cfg9346_Unlock, ioaddr + Cfg9346); outb(tp->full_duplex ? 0x60 : 0x20, ioaddr + Config1); outb(Cfg9346_Lock, 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++; } 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__) clear_bit(0, (void*)&dev->interrupt);#else dev->interrupt = 0;#endif return;}/* 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__) /* 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; }#else if (dev->interrupt) { printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name); return; } dev->interrupt = 1;#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) && inw(ioaddr+CSCR) & CSCR_LinkChangeBit) { link_changed = inw(ioaddr+CSCR) & CSCR_LinkChangeBit; /* If link OK, disable Link Down Power Saving mode. If link OFF, enable it. This mode is avaiable only for RTL8139C */ if((inb(ioaddr+TxConfig+3) & 0x7C)==0x74) // 0x74 is RTL8139C HW Ver. { if((inb(ioaddr+GPPinData) & 0x04)==0) outb(inb(ioaddr+Config5) | 0x4, ioaddr+Config5); else outb(inb(ioaddr+Config5) & 0xB, ioaddr+Config5); } }/* 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 << TX_DMA_BURST_SHIFT, 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(tp->tx_skbuffa[entry]); tp->tx_skbuffa[entry] = 0; if (test_bit(0, &tp->tx_full)) { /* The ring is no longer full, clear tbusy. */ clear_bit(0, &tp->tx_full); clear_bit(0, (void*)&dev->tbusy); netif_wake_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;
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -