zoukankan      html  css  js  c++  java
  • Linux 网卡驱动相关——04

    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;
     }
  • 相关阅读:
    js 生成指定范围之内的随机数
    vue项目在ie浏览器打开做提示
    vue 瀑布流组件
    docker 移动文件到其他目录
    学习hyperf遇到的问题
    Linux 部署elasticsearch
    Git 常用命令
    Linux svn定时更新
    eclipse 导入web项目后,线程假死
    向量基本概念
  • 原文地址:https://www.cnblogs.com/zhuyp1015/p/2623387.html
Copyright © 2011-2022 走看看