📄 rtl8139.c
字号:
}#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++; tp->cur_rx = inw(ioaddr + RxBufAddr) % 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__) clear_bit(0, (void*)&dev->interrupt);#else dev->interrupt = 0;#endif return;}static int rtl8139cp_rx(struct net_device *dev){ struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; u16 cur_rx = tp->cur_rx; struct sk_buff *skb; int pkt_size=0 ; while ( (tp->rxDescArray[cur_rx].status & 0x80000000)== 0) { pkt_size=(int)(tp->rxDescArray[cur_rx].status & 0x00001FFF)-4; skb = dev_alloc_skb(pkt_size + 2); if (skb == NULL) { printk(KERN_WARNING"%s: Memory squeeze, deferring packet.\n", dev->name); tp->stats.rx_dropped++; break; } skb->dev = dev; skb_reserve(skb, 2); eth_copy_and_sum(skb, tp->rxBufferRing[cur_rx], pkt_size, 0); skb_put(skb, pkt_size); skb->protocol = eth_type_trans(skb, dev); netif_rx(skb);#if LINUX_VERSION_CODE > 0x20119 tp->stats.rx_bytes += pkt_size;#endif tp->stats.rx_packets++; tp->rxDescArray[cur_rx].status |= 0x80000000 + RX_BUF_SIZE; cur_rx = (cur_rx +1) % NUM_CP_RX_DESC; } tp->cur_rx = cur_rx; return 0;}/* 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)); while ((inb(ioaddr + ChipCmd) & RxBufEmpty) == 0) { int ring_offset = cur_rx % 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 & 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. */ } 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); /* A.C.: Reset the multicast list. */ set_rx_mode(dev); outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); } else { /* Malloc up new buffer, compatible with net-2e. */ /* Omit the four octet CRC from the length. */ struct sk_buff *skb; int pkt_size = rx_size - 4; skb = dev_alloc_skb(pkt_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; /* This could presumably use two calls to copy_and_sum()? */ memcpy(skb_put(skb, semi_count), &rx_ring[ring_offset + 4], semi_count); memcpy(skb_put(skb, pkt_size-semi_count), rx_ring, pkt_size-semi_count); if (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 { eth_copy_and_sum(skb, &rx_ring[ring_offset + 4], pkt_size, 0); skb_put(skb, pkt_size); } skb->protocol = eth_type_trans(skb, dev); netif_rx(skb);#if LINUX_VERSION_CODE > 0x20119 tp->stats.rx_bytes += pkt_size;#endif tp->stats.rx_packets++; } cur_rx = (cur_rx + rx_size + 4 + 3) & ~3; outw(cur_rx - 16, ioaddr + RxBufPtr); } if (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 net_device *dev){ long ioaddr = dev->base_addr; struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; int i; dev->start = 0; dev->tbusy = 1; if (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);//add for 8139C+ if ( CPFlag ==1 ){ kfree(tp->txDescArrays); kfree(tp->rxDescArrays); tp->txDescArrays = NULL; tp->rxDescArrays = NULL; tp->txDescArray = NULL; tp->rxDescArray = NULL; } else { kfree(tp->rx_ring); }//add for 8139C+. if( CPFlag == 1 ){ for (i = 0; i < NUM_CP_TX_DESC; i++) { if (tp->tx_skbuff[i]) { dev_free_skb(tp->tx_skbuff[i]); tp->tx_skbuff[i] = 0; } } } else{ for (i = 0; i < NUM_TX_DESC; i++) { if (tp->tx_skbuffa[i]) { dev_free_skb(tp->tx_skbuffa[i]); tp->tx_skbuffa[i] = 0; } } }// kfree(tp->rx_ring); /* Green! Put the chip in low-power mode. */ outb(Cfg9346_Unlock, ioaddr + Cfg9346); outb(0x03, ioaddr + Config1); outb('H', ioaddr + HltClk); /* 'R' would leave the clock running. */ MOD_DEC_USE_COUNT; return 0;}static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd){ struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; u16 *data = (u16 *)&rq->ifr_data; switch(cmd) { case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ data[0] = tp->phys[0] & 0x3f; /* Fall Through */ case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ data[3] = mdio_read(dev, data[0], data[1] & 0x1f); return 0; case SIOCDEVPRIVATE+2: /* Write the specified MII register */ if (!capable(CAP_NET_ADMIN)) return -EPERM; mdio_write(dev, data[0], data[1] & 0x1f, data[2]); return 0; default: return -EOPNOTSUPP; }}static struct net_device_stats *rtl8129_get_stats(struct net_device *dev){ struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; long 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. This routine is not state sensitive and need not be SMP locked. */static unsigned const ethernet_polynomial = 0x04c11db7U;static inline u32 ether_crc(int length, unsigned char *data){ int crc = -1; while (--length >= 0) { unsigned char current_octet = *data++; int bit; for (bit = 0; bit < 8; bit++, current_octet >>= 1) crc = (crc << 1) ^ ((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0); } return crc;}/* Bits in RxConfig. */enum rx_mode_bits { AcceptErr=0x20, AcceptRunt=0x10, AcceptBroadcast=0x08, AcceptMulticast=0x04, AcceptMyPhys=0x02, AcceptAllPhys=0x01,};static void set_rx_mode(struct net_device *dev){ struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; long ioaddr = dev->base_addr; u32 mc_filter[2]; /* Multicast hash filter */ int i, rx_mode; if (debug > 3) printk(KERN_DEBUG"%s: set_rx_mode(%4.4x) done -- Rx config %8.8x.\n", dev->name, dev->flags, inl(ioaddr + RxConfig)); /* Note: do not reorder, GCC is clever about common statements. */ if (dev->flags & IFF_PROMISC) { /* Unconditionally log net taps. */ printk(KERN_NOTICE"%s: Promiscuous mode enabled.\n", dev->name); rx_mode = AcceptBroadcast|AcceptMulticast|AcceptMyPhys|AcceptAllPhys; mc_filter[1] = mc_filter[0] = 0xffffffff; } else if ((dev->mc_count > multicast_filter_limit) || (dev->flags & IFF_ALLMULTI)) { /* Too many to filter perfectly -- accept all multicasts. */ rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; mc_filter[1] = mc_filter[0] = 0xffffffff; } else { struct dev_mc_list *mclist; rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; mc_filter[1] = mc_filter[0] = 0; for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; i++, mclist = mclist->next) set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26, mc_filter); } /* We can safely update without stopping the chip. */ outl(tp->rx_config | rx_mode, ioaddr + RxConfig); outl(mc_filter[0], ioaddr + MAR0 + 0); outl(mc_filter[1], ioaddr + MAR0 + 4); return;}static int rtl_pwr_event(void *dev_instance, int event){ struct net_device *dev = dev_instance; struct rtl8129_private *np = (struct rtl8129_private *)dev->priv; long ioaddr = dev->base_addr; if (debug > 1) printk("%s: Handling power event %d.\n", dev->name, event); switch(event) { case DRV_ATTACH: MOD_INC_USE_COUNT; break; case DRV_SUSPEND: /* Disable interrupts, stop Tx and Rx. */ outw(0x0000, ioaddr + IntrMask); outb(0x00, ioaddr + ChipCmd); /* Update the error counts. */ np->stats.rx_missed_errors += inl(ioaddr + RxMissed); outl(0, ioaddr + RxMissed); break; case DRV
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -