rtl8139 网卡驱动分析:(注:这里只分析了主要函数,代码版本是:
8139too.c | 71420 bytes | 2010-04-01 22:56:18 |
)
/* * 提供了一系列驱动程序可调用的接口,主要用于发现和初始化设备 */ static struct pci_driver rtl8139_pci_driver = { .name = DRV_NAME, .id_table = rtl8139_pci_tbl, .probe = rtl8139_init_one, .remove = __devexit_p(rtl8139_remove_one), #ifdef CONFIG_PM .suspend = rtl8139_suspend, .resume = rtl8139_resume, #endif /* CONFIG_PM */ }; static int __devinit rtl8139_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { struct net_device *dev = NULL; struct rtl8139_private *tp; int i, addr_len, option; void __iomem *ioaddr; static int board_idx = -1; assert (pdev != NULL); assert (ent != NULL); board_idx++; /* when we're built into the kernel, the driver version message * is only printed if at least one 8139 board has been found */ #ifndef MODULE { static int printed_version; if (!printed_version++) pr_info(RTL8139_DRIVER_NAME "\n"); } #endif if (pdev->vendor == PCI_VENDOR_ID_REALTEK && pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pdev->revision >= 0x20) { dev_info(&pdev->dev, "This (id %04x:%04x rev %02x) is an enhanced 8139C+ chip, use 8139cp\n", pdev->vendor, pdev->device, pdev->revision); return -ENODEV; } if (pdev->vendor == PCI_VENDOR_ID_REALTEK && pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pdev->subsystem_vendor == PCI_VENDOR_ID_ATHEROS && pdev->subsystem_device == PCI_DEVICE_ID_REALTEK_8139) { pr_info("8139too: OQO Model 2 detected. Forcing PIO\n"); use_io = 1; } dev = rtl8139_init_board (pdev);//返回了一个struct net_device 对象的指针 if (IS_ERR(dev)) return PTR_ERR(dev); assert (dev != NULL); tp = netdev_priv(dev);//获取私有结构的指针 tp->dev = dev;//关联rtl8139_init_board 返回的 net_device 对象指针,便于操作 ioaddr = tp->mmio_addr;//获取虚拟io地址,便于操作 assert (ioaddr != NULL); /*读取MAC地址*/ addr_len = read_eeprom (ioaddr, 0, 8) == 0x8129 ? 8 : 6; for (i = 0; i < 3; i++)//macaddr 6 byte, each time 2byte ,so three times is ok ((__le16 *) (dev->dev_addr))[i] = cpu_to_le16(read_eeprom (ioaddr, i + 7, addr_len)); memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);//保存MAC地址 /* The Rtl8139-specific entries in the device structure. */ dev->netdev_ops = &rtl8139_netdev_ops; //关联net_device的操作 dev->ethtool_ops = &rtl8139_ethtool_ops; dev->watchdog_timeo = TX_TIMEOUT; netif_napi_add(dev, &tp->napi, rtl8139_poll, 64); /*注册轮询函数,64为一次处理的帧数,如果一个处理的帧数小于这个数字,则使用中断的方式,否则使用轮询的方式*/ /* note: the hardware is not capable of sg/csum/highdma, however * through the use of skb_copy_and_csum_dev we enable these * features */ dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA; dev->irq = pdev->irq;//关联irq,方便在request_irq 函数中的操作 /* tp zeroed and aligned in alloc_etherdev */ tp = netdev_priv(dev); /* note: tp->chipset set in rtl8139_init_board */ tp->drv_flags = board_info[ent->driver_data].hw_flags; tp->mmio_addr = ioaddr; tp->msg_enable = (debug < 0 ? RTL8139_DEF_MSG_ENABLE : ((1 << debug) - 1)); spin_lock_init (&tp->lock); spin_lock_init (&tp->rx_lock); INIT_DELAYED_WORK(&tp->thread, rtl8139_thread); tp->mii.dev = dev; tp->mii.mdio_read = mdio_read; tp->mii.mdio_write = mdio_write; tp->mii.phy_id_mask = 0x3f; tp->mii.reg_num_mask = 0x1f; /* dev is fully set up and ready to use now */ pr_debug("about to register device named %s (%p)...\n", dev->name, dev); i = register_netdev (dev);//注册网络设备 if (i) goto err_out; pci_set_drvdata (pdev, dev); /*把网络设备指针地址放入PCI设备中的设备指针中,便于remove的时候使用 * pci_get_drvdata 获取该网络设备 */ pr_info("%s: %s at 0x%lx, %pM, IRQ %d\n", dev->name, board_info[ent->driver_data].name, dev->base_addr, dev->dev_addr, dev->irq); pr_debug("%s: Identified 8139 chip type '%s'\n", dev->name, rtl_chip_info[tp->chipset].name); /* Find the connected MII xcvrs. Doing this in open() would allow detecting external xcvrs later, but takes too much time. */ #ifdef CONFIG_8139TOO_8129 if (tp->drv_flags & HAS_MII_XCVR) { int phy, phy_idx = 0; for (phy = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) { int mii_status = mdio_read(dev, phy, 1); if (mii_status != 0xffff && mii_status != 0x0000) { u16 advertising = mdio_read(dev, phy, 4); tp->phys[phy_idx++] = phy; pr_info("%s: MII transceiver %d status 0x%4.4x advertising %4.4x.\n", dev->name, phy, mii_status, advertising); } } if (phy_idx == 0) { pr_info("%s: No MII transceivers found! Assuming SYM transceiver.\n", dev->name); tp->phys[0] = 32; } } else #endif tp->phys[0] = 32; tp->mii.phy_id = tp->phys[0]; /* The lower four bits are the media type. */ option = (board_idx >= MAX_UNITS) ? 0 : media[board_idx]; if (option > 0) { tp->mii.full_duplex = (option & 0x210) ? 1 : 0; tp->default_port = option & 0xFF; if (tp->default_port) tp->mii.force_media = 1; } if (board_idx < MAX_UNITS && full_duplex[board_idx] > 0) tp->mii.full_duplex = full_duplex[board_idx]; if (tp->mii.full_duplex) { pr_info("%s: Media type forced to Full Duplex.\n", dev->name); /* Changing the MII-advertised media because might prevent re-connection. */ tp->mii.force_media = 1; } if (tp->default_port) { pr_info(" Forcing %dMbps %s-duplex operation.\n", (option & 0x20 ? 100 : 10), (option & 0x10 ? "full" : "half")); mdio_write(dev, tp->phys[0], 0, ((option & 0x20) ? 0x2000 : 0) | /* 100Mbps? */ ((option & 0x10) ? 0x0100 : 0)); /* Full duplex? */ } /* Put the chip into low-power mode. */ if (rtl_chip_info[tp->chipset].flags & HasHltClk) RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */ return 0; err_out: __rtl8139_cleanup_dev (dev); pci_disable_device (pdev); return i; } static __devinit struct net_device * rtl8139_init_board (struct pci_dev *pdev) { void __iomem *ioaddr; struct net_device *dev; struct rtl8139_private *tp; u8 tmp8; int rc, disable_dev_on_err = 0; unsigned int i; unsigned long pio_start, pio_end, pio_flags, pio_len; unsigned long mmio_start, mmio_end, mmio_flags, mmio_len; u32 version; assert (pdev != NULL); /* dev and priv zeroed in alloc_etherdev */ dev = alloc_etherdev (sizeof (*tp)); /*注意到这里分配空间传入的参数是 struct rtl8139rtl 的大小,其实也就是在创建 struct net_device 的同时给 指向 private 的指针分配了空间*/ if (dev == NULL) { dev_err(&pdev->dev, "Unable to alloc new net device\n"); return ERR_PTR(-ENOMEM); } SET_NETDEV_DEV(dev, &pdev->dev); /*#define SET_NETDEV_DEV(net, pdev) ((net)->dev.parent = (pdev))*/ /*netdev_priv - access network device private data*/ tp = netdev_priv(dev); tp->pci_dev = pdev; /* enable device (incl. PCI PM wakeup and hotplug setup) 相当于windows驱动中发送IRP_MN_START_DEVICE 类型IRP 包给PDO处理 */ rc = pci_enable_device (pdev); if (rc) goto err_out; /*读取PCI配置空间信息*/ pio_start = pci_resource_start (pdev, 0); pio_end = pci_resource_end (pdev, 0); pio_flags = pci_resource_flags (pdev, 0); pio_len = pci_resource_len (pdev, 0); mmio_start = pci_resource_start (pdev, 1); mmio_end = pci_resource_end (pdev, 1); mmio_flags = pci_resource_flags (pdev, 1); mmio_len = pci_resource_len (pdev, 1); /* set this immediately, we need to know before * we talk to the chip directly */ pr_debug("PIO region size == 0x%02lX\n", pio_len); pr_debug("MMIO region size == 0x%02lX\n", mmio_len); retry: if (use_io) { /* make sure PCI base addr 0 is PIO */ if (!(pio_flags & IORESOURCE_IO)) { dev_err(&pdev->dev, "region #0 not a PIO resource, aborting\n"); rc = -ENODEV; goto err_out; } /* check for weird/broken PCI region reporting */ if (pio_len < RTL_MIN_IO_SIZE) { dev_err(&pdev->dev, "Invalid PCI I/O region size(s), aborting\n"); rc = -ENODEV; goto err_out; } } else { /* make sure PCI base addr 1 is MMIO */ if (!(mmio_flags & IORESOURCE_MEM)) { dev_err(&pdev->dev, "region #1 not an MMIO resource, aborting\n"); rc = -ENODEV; goto err_out; } if (mmio_len < RTL_MIN_IO_SIZE) { dev_err(&pdev->dev, "Invalid PCI mem region size(s), aborting\n"); rc = -ENODEV; goto err_out; } } /*登记该空间已经分配给了特定对象,不能再次分配给其它设备*/ rc = pci_request_regions (pdev, DRV_NAME); if (rc) goto err_out; disable_dev_on_err = 1; /* enable PCI bus-mastering */ pci_set_master (pdev); /*跟ioremap类似的功能,将板上硬件物理地址映射为虚拟地址*/ if (use_io) { ioaddr = pci_iomap(pdev, 0, 0); if (!ioaddr) { dev_err(&pdev->dev, "cannot map PIO, aborting\n"); rc = -EIO; goto err_out; } dev->base_addr = pio_start; tp->regs_len = pio_len; } else { /* ioremap MMIO region */ ioaddr = pci_iomap(pdev, 1, 0); if (ioaddr == NULL) { dev_err(&pdev->dev, "cannot remap MMIO, trying PIO\n"); pci_release_regions(pdev); use_io = 1; goto retry; } dev->base_addr = (long) ioaddr; tp->regs_len = mmio_len; } tp->mmio_addr = ioaddr;//在rtl8139_private 结构中保存该虚拟地址,用于io操作 /* Bring old chips out of low-power mode. */ RTL_W8 (HltClk, 'R'); /* check for missing/broken hardware */ if (RTL_R32 (TxConfig) == 0xFFFFFFFF) { dev_err(&pdev->dev, "Chip not responding, ignoring board\n"); rc = -EIO; goto err_out; } /* identify chip attached to board */ version = RTL_R32 (TxConfig) & HW_REVID_MASK; for (i = 0; i < ARRAY_SIZE (rtl_chip_info); i++) if (version == rtl_chip_info[i].version) { tp->chipset = i; goto match; } /* if unknown chip, assume array element #0, original RTL-8139 in this case */ dev_dbg(&pdev->dev, "unknown chip version, assuming RTL-8139\n"); dev_dbg(&pdev->dev, "TxConfig = 0x%lx\n", RTL_R32 (TxConfig)); tp->chipset = 0; match: pr_debug("chipset id (%d) == index %d, '%s'\n", version, i, rtl_chip_info[i].name); if (tp->chipset >= CH_8139B) { u8 new_tmp8 = tmp8 = RTL_R8 (Config1); pr_debug("PCI PM wakeup\n"); if ((rtl_chip_info[tp->chipset].flags & HasLWake) && (tmp8 & LWAKE)) new_tmp8 &= ~LWAKE; new_tmp8 |= Cfg1_PM_Enable; if (new_tmp8 != tmp8) { RTL_W8 (Cfg9346, Cfg9346_Unlock); RTL_W8 (Config1, tmp8); RTL_W8 (Cfg9346, Cfg9346_Lock); } if (rtl_chip_info[tp->chipset].flags & HasLWake) { tmp8 = RTL_R8 (Config4); if (tmp8 & LWPTN) { RTL_W8 (Cfg9346, Cfg9346_Unlock); RTL_W8 (Config4, tmp8 & ~LWPTN); RTL_W8 (Cfg9346, Cfg9346_Lock); } } } else { pr_debug("Old chip wakeup\n"); tmp8 = RTL_R8 (Config1); tmp8 &= ~(SLEEP | PWRDN); RTL_W8 (Config1, tmp8); } rtl8139_chip_reset (ioaddr); return dev; err_out: __rtl8139_cleanup_dev (dev); if (disable_dev_on_err) pci_disable_device (pdev); return ERR_PTR(rc); } /* *8139网卡的有⼀个接收缓冲寄存器,用于存放接收缓存的首地址,网 *卡⼀边把网线上的发出的数据放到内部FIFO,⼀边从FIFO中把数据通过 *DMA传送到由接收寄存器指定的内存地址中,接收到的数据依次排放,当 *长度超过默认的缓冲区长度时,会回过头来放到开始的地方,所以接收 *缓冲区被称为环形缓冲区。发送方面:8139有四个发送地址寄存器,CPU *将要发送的数据在内存中的地址写⼊这四个寄存器中的任何⼀个,网卡 *就会通过DMA操作把数据发送出去。当发送或者接送完成后,网卡会发出 *中断,中断处理程序通过读取网卡的中断状态寄存器来识别出是发送完 *成发出的中断,接收到数据包的中断,还是错误中断。 *当运行ifconfig ethx up的时候,rtl8139_open得到调用。该函数的 *任务就是分配,初始化接收,发送缓冲区,分配中断号等。 * */ static int rtl8139_open (struct net_device *dev) { struct rtl8139_private *tp = netdev_priv(dev); int retval; void __iomem *ioaddr = tp->mmio_addr; retval = request_irq (dev->irq, rtl8139_interrupt, IRQF_SHARED, dev->name, dev); if (retval) return retval; //dma_alloc_coherent() -- 获取物理页,并将该物理页的总线地址保存于dma_handle,返回该物理页的虚拟地址 tp->tx_bufs = dma_alloc_coherent(&tp->pci_dev->dev, TX_BUF_TOT_LEN, &tp->tx_bufs_dma, GFP_KERNEL); tp->rx_ring = dma_alloc_coherent(&tp->pci_dev->dev, RX_BUF_TOT_LEN, &tp->rx_ring_dma, GFP_KERNEL); if (tp->tx_bufs == NULL || tp->rx_ring == NULL) { free_irq(dev->irq, dev); if (tp->tx_bufs) dma_free_coherent(&tp->pci_dev->dev, TX_BUF_TOT_LEN, tp->tx_bufs, tp->tx_bufs_dma); if (tp->rx_ring) dma_free_coherent(&tp->pci_dev->dev, RX_BUF_TOT_LEN, tp->rx_ring, tp->rx_ring_dma); return -ENOMEM; } /** * napi_enable - enable NAPI scheduling * @n: napi context * * Resume NAPI from being scheduled on this context. * Must be paired with napi_disable. */ napi_enable(&tp->napi); tp->mii.full_duplex = tp->mii.force_media; tp->tx_flag = (TX_FIFO_THRESH << 11) & 0x003f0000; rtl8139_init_ring (dev);//将发送帧所存放的地址写下来 rtl8139_hw_start (dev);//初始化接收和发送寄存器对于的dma地址 netif_start_queue (dev);//该函数用于告诉上层网络驱动层驱动空间有缓冲区可用,开始发送数据包到驱动层。 if (netif_msg_ifup(tp)) pr_debug("%s: rtl8139_open() ioaddr %#llx IRQ %d" " GP Pins %2.2x %s-duplex.\n", dev->name, (unsigned long long)pci_resource_start (tp->pci_dev, 1), dev->irq, RTL_R8 (MediaStatus), tp->mii.full_duplex ? "full" : "half"); rtl8139_start_thread(tp); return 0; } /* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */ static irqreturn_t rtl8139_interrupt (int irq, void *dev_instance) { struct net_device *dev = (struct net_device *) dev_instance; struct rtl8139_private *tp = netdev_priv(dev); void __iomem *ioaddr = tp->mmio_addr; u16 status, ackstat; int link_changed = 0; /* avoid bogus "uninit" warning */ int handled = 0; spin_lock (&tp->lock); status = RTL_R16 (IntrStatus);//从网卡的中断状态寄存器中读出状态值进行分析 /* shared irq? */ if (unlikely((status & rtl8139_intr_mask) == 0)) goto out; handled = 1; /* h/w no longer present (hotplug?) or major error, bail */ if (unlikely(status == 0xFFFF)) goto out; /* close possible race's with dev_close */ if (unlikely(!netif_running(dev))) { RTL_W16 (IntrMask, 0); goto out; } /* Acknowledge all of the current interrupt sources ASAP, but an first get an additional status bit from CSCR. */ if (unlikely(status & RxUnderrun)) link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit; ackstat = status & ~(RxAckBits | TxErr); if (ackstat) RTL_W16 (IntrStatus, ackstat); /* Receive packets are processed by poll routine. If not running start it now. */ /*如果状态寄存器的接收bit位置为1,则进入接收处理函数。根据NAPI机制。这里先向终端屏蔽寄存器写入rtl8139_norx_intr_mask,关闭中断,然后通过把接收的poll 函数加入中断队列,将来软中断调度的时候回调用rtl8139_poll,进行轮询*/ if (status & RxAckBits){ if (napi_schedule_prep(&tp->napi)) { RTL_W16_F (IntrMask, rtl8139_norx_intr_mask); __napi_schedule(&tp->napi); //转为轮询机制,而非中断机制 } } /* Check uncommon events with one test. */ if (unlikely(status & (PCIErr | PCSTimeout | RxUnderrun | RxErr))) rtl8139_weird_interrupt (dev, tp, ioaddr, status, link_changed); /* 如果状态寄存器的发送位置 1 ,则进入发送中断的处理函数 */ if (status & (TxOK | TxErr)) { rtl8139_tx_interrupt (dev, tp, ioaddr); if (status & TxErr) RTL_W16 (IntrStatus, TxErr); } out: spin_unlock (&tp->lock); pr_debug("%s: exiting interrupt, intr_status=%#4.4x.\n", dev->name, RTL_R16 (IntrStatus)); return IRQ_RETVAL(handled); } static int rtl8139_poll(struct napi_struct *napi, int budget) { struct rtl8139_private *tp = container_of(napi, struct rtl8139_private, napi); struct net_device *dev = tp->dev; void __iomem *ioaddr = tp->mmio_addr; int work_done; spin_lock(&tp->rx_lock); work_done = 0; if (likely(RTL_R16(IntrStatus) & RxAckBits))//接收中断 work_done += rtl8139_rx(dev, tp, budget); /*rtl8139_rx 将接收到的数据拷贝出来封装到 sk_buff 里面传递给上层协议驱动*/ if (work_done < budget) {//如果一次处理的帧个数小于budget(64)则退出轮询模式 unsigned long flags; /* * Order is important since data can get interrupted * again when we think we are done. */ spin_lock_irqsave(&tp->lock, flags); RTL_W16_F(IntrMask, rtl8139_intr_mask); __napi_complete(napi); //退出轮询模式 spin_unlock_irqrestore(&tp->lock, flags); } spin_unlock(&tp->rx_lock); return work_done; } static int rtl8139_rx(struct net_device *dev, struct rtl8139_private *tp, int budget) { void __iomem *ioaddr = tp->mmio_addr; int received = 0; unsigned char *rx_ring = tp->rx_ring; unsigned int cur_rx = tp->cur_rx; unsigned int rx_size = 0; pr_debug("%s: In rtl8139_rx(), current %4.4x BufAddr %4.4x," " free to %4.4x, Cmd %2.2x.\n", dev->name, (u16)cur_rx, RTL_R16 (RxBufAddr), RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd)); //这个函数是一个大循环,循环条件是只要接收缓存不为空就还可以继续读取数据,循环不会停止,读空了之后就跳出 while (netif_running(dev) && received < budget && (RTL_R8 (ChipCmd) & RxBufEmpty) == 0) { u32 ring_offset = cur_rx % RX_BUF_LEN; // u32 rx_status; unsigned int pkt_size; struct sk_buff *skb; rmb(); /* read size+status of next frame from DMA ring buffer */ rx_status = le32_to_cpu (*(__le32 *) (rx_ring + ring_offset)); rx_size = rx_status >> 16; //计算出要接收的包的长度 pkt_size = rx_size - 4; if (netif_msg_rx_status(tp)) pr_debug("%s: rtl8139_rx() status %4.4x, size %4.4x," " cur %4.4x.\n", dev->name, rx_status, rx_size, cur_rx); #if RTL8139_DEBUG > 2 { int i; pr_debug("%s: Frame contents ", dev->name); for (i = 0; i < 70; i++) pr_cont(" %2.2x", rx_ring[ring_offset + i]); pr_cont(".\n"); } #endif /* Packet copy from FIFO still in progress. * Theoretically, this should never happen * since EarlyRx is disabled. */ if (unlikely(rx_size == 0xfff0)) { if (!tp->fifo_copy_timeout) tp->fifo_copy_timeout = jiffies + 2; else if (time_after(jiffies, tp->fifo_copy_timeout)) { pr_debug("%s: hung FIFO. Reset.", dev->name); rx_size = 0; goto no_early_rx; } if (netif_msg_intr(tp)) { pr_debug("%s: fifo copy in progress.", dev->name); } tp->xstats.early_rx++; break; } no_early_rx: tp->fifo_copy_timeout = 0; /* If Rx err or invalid rx_size/rx_status received * (which happens if we get lost in the ring), * Rx process gets reset, so we abort any further * Rx processing. */ if (unlikely((rx_size > (MAX_ETH_FRAME_SIZE+4)) || (rx_size < 8) || (!(rx_status & RxStatusOK)))) { rtl8139_rx_err (rx_status, dev, tp, ioaddr); received = -1; goto out; } /* Malloc up new buffer, compatible with net-2e. */ /* Omit the four octet CRC from the length. */ skb = netdev_alloc_skb(dev, pkt_size + NET_IP_ALIGN); //如果分配成功就把数据从接收缓存中拷贝到这个包中 if (likely(skb)) { skb_reserve (skb, NET_IP_ALIGN); /* 16 byte align the IP fields. */ #if RX_BUF_IDX == 3 wrap_copy(skb, rx_ring, ring_offset+4, pkt_size); #else //现在我们已经熟知,&rx_ring[ring_offset + 4]就是接收缓存,也是源地址, //而skb->data就是包的数据地址,下面这个函数就是将接收到的数据拷贝到skb->data 所指向的位置 skb_copy_to_linear_data (skb, &rx_ring[ring_offset + 4], pkt_size); #endif skb_put (skb, pkt_size); //当接收到的数据应经复制到skb中head指向的位置之后,需要改变skb->tail 指针 skb->protocol = eth_type_trans (skb, dev); dev->stats.rx_bytes += pkt_size; dev->stats.rx_packets++; //在netif_receive_skb (skb)函数执行完后,这个包的数据就脱离了网卡驱动范畴,而进入了Linux网络协议栈里面 netif_receive_skb (skb); } else { if (net_ratelimit()) pr_warning("%s: Memory squeeze, dropping packet.\n", dev->name); dev->stats.rx_dropped++; } received++; cur_rx = (cur_rx + rx_size + 4 + 3) & ~3; RTL_W16 (RxBufPtr, (u16) (cur_rx - 16)); rtl8139_isr_ack(tp); } if (unlikely(!received || rx_size == 0xfff0)) rtl8139_isr_ack(tp); pr_debug("%s: Done rtl8139_rx(), current %4.4x BufAddr %4.4x," " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx, RTL_R16 (RxBufAddr), RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd)); tp->cur_rx = cur_rx; /* * The receive buffer should be mostly empty. * Tell NAPI to reenable the Rx irq. */ if (tp->fifo_copy_timeout) received = budget; out: return received; } static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev) { struct rtl8139_private *tp = netdev_priv(dev); void __iomem *ioaddr = tp->mmio_addr; unsigned int entry; unsigned int len = skb->len; unsigned long flags; /* Calculate the next Tx descriptor entry. */ entry = tp->cur_tx % NUM_TX_DESC; /* Note: the chip doesn't have auto-pad! */ if (likely(len < TX_BUF_SIZE)) { if (len < ETH_ZLEN) memset(tp->tx_buf[entry], 0, ETH_ZLEN); skb_copy_and_csum_dev(skb, tp->tx_buf[entry]);//把待发送的数据拷贝到发送缓冲区,也就是skb->data 指向的空间 dev_kfree_skb(skb); } else { dev_kfree_skb(skb); dev->stats.tx_dropped++; return 0; } spin_lock_irqsave(&tp->lock, flags); /* * Writing to TxStatus triggers a DMA transfer of the data * copied to tp->tx_buf[entry] above. Use a memory barrier * to make sure that the device sees the updated data. */ wmb(); /* 我们要让网卡知道这个包的长度,才能保证数据不多不少精确的从缓存中截取出来搬运到网卡中去, 这是靠写发送状态寄存器(TSD)来完成的。 */ RTL_W32_F (TxStatus0 + (entry * sizeof (u32)), tp->tx_flag | max(len, (unsigned int)ETH_ZLEN)); dev->trans_start = jiffies; tp->cur_tx++; /*判断发送缓存是否已经满了,如果满了在发就覆盖数据了,要停发。*/ if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx) netif_stop_queue (dev); spin_unlock_irqrestore(&tp->lock, flags); if (netif_msg_tx_queued(tp)) pr_debug("%s: Queued Tx packet size %u to slot %d.\n", dev->name, len, entry); return 0; }
来源:https://www.cnblogs.com/zhuyp1015/archive/2012/08/04/2623387.html