Linux 网卡驱动相关——04

rtl8139 网卡驱动分析:(注:这里只分析了主要函数,代码版本是:

8139too.c71420 bytes2010-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;
 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值