Realtek 8125驱动分析第一篇——初始化

Realtek 8125是瑞昱的2.5G网卡,今天开始分析一下该网卡的驱动程序。

目录

1 驱动模块注册

2 PCI相关初始化

2.1 PCI 驱动列表注册:pci_register_driver()

2.2 搜索和加载驱动:pci_driver->probe()

2.2.1 rtl8125_try_msi

2.2.2 rtl8125_init_napi

2.2.3 rtl8125_init_all_schedule_work

3 以太网相关初始化

3.1 net_device_ops

3.2 注册 ethtool


1 驱动模块注册

module_init() 注册一个初始化函数,加载驱动时执行。

module_init(rtl8125_init_module);

static int __init
rtl8125_init_module(void)
{
        int ret = 0;
#ifdef ENABLE_R8125_PROCFS
        rtl8125_proc_module_init();
#endif

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)

        /*初始化PCI相关的内容*/
        ret = pci_register_driver(&rtl8125_pci_driver);
#else
        ret = pci_module_init(&rtl8125_pci_driver);
#endif

        return ret;
}

rtl8125_proc_module_init() 就是在/proc/net下新建一个文件夹,用于存放rtl8125相关的信息。

static void rtl8125_proc_module_init(void)
{
        //create /proc/net/r8125
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
        rtl8125_proc = proc_mkdir(MODULENAME, init_net.proc_net);
#else
        rtl8125_proc = proc_mkdir(MODULENAME, proc_net);
#endif
        if (!rtl8125_proc)
                dprintk("cannot create %s proc entry \n", MODULENAME);
}

初始化的大部分工作在pci_register_driver中进行。

2 PCI相关初始化

2.1 PCI 驱动列表注册:pci_register_driver()

Realtek 8125是PCIe设备,这种设备设备通过 PCI Configuration Space 识别。当设备驱动编译时,MODULE_DEVICE_TABLE 宏会导出一个 global 的 PCI 设备 ID 列表, 驱动据此识别它可以控制哪些设备,这样内核就能对各设备加载正确的驱动。8125驱动的设备表和PCI设备ID如下:

static struct pci_device_id rtl8125_pci_tbl[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8125), },
        { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8162), },
        { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x3000), },
        {0,},
};

MODULE_DEVICE_TABLE(pci, rtl8125_pci_tbl);

pci_register_driver() 会将该驱动的各种回调方法注册到一个 struct pci_driver rtl8125_pci_driver 变量:

static struct pci_driver rtl8125_pci_driver = {
        .name       = MODULENAME,
        .id_table   = rtl8125_pci_tbl,
        .probe      = rtl8125_init_one, //初始化时执行这个函数
        .remove     = __devexit_p(rtl8125_remove_one),
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,11)
        .shutdown   = rtl8125_shutdown,
#endif
#ifdef CONFIG_PM
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
        .suspend    = rtl8125_suspend,
        .resume     = rtl8125_resume,
#else
        .driver.pm	= RTL8125_PM_OPS,
#endif
#endif
};

2.2 搜索和加载驱动:pci_driver->probe()

内核会通过 PCI ID 依次识别各 PCI 设备,然后为设备选择合适的驱动。 每个 PCI 驱动都注册了一个 probe() 方法,为设备寻找驱动就是调用其 probe() 方法。

来看下 r8125 驱动的 probe() 包含哪些过程:

static int __devinit
rtl8125_init_one(struct pci_dev *pdev,
                 const struct pci_device_id *ent)
{
        struct net_device *dev = NULL;
        struct rtl8125_private *tp;
        void __iomem *ioaddr = NULL;
        static int board_idx = -1;

        int rc;

        assert(pdev != NULL);
        assert(ent != NULL);

        board_idx++;

        if (netif_msg_drv(&debug))
                printk(KERN_INFO "%s 2.5Gigabit Ethernet driver %s loaded\n",
                       MODULENAME, RTL8125_VERSION);

        rc = rtl8125_init_board(pdev, &dev, &ioaddr);
        if (rc)
                goto out;

        tp = netdev_priv(dev);
        assert(ioaddr != NULL);

        //设置一些函数
        tp->set_speed = rtl8125_set_speed_xmii;
        tp->get_settings = rtl8125_gset_xmii;
        tp->phy_reset_enable = rtl8125_xmii_reset_enable;
        tp->phy_reset_pending = rtl8125_xmii_reset_pending;
        tp->link_ok = rtl8125_xmii_link_ok;

        rc = rtl8125_try_msi(tp);
        if (rc < 0) {
                dev_err(&pdev->dev, "Can't allocate interrupt\n");
                goto err_out_1;
        }
#ifdef ENABLE_PTP_SUPPORT
        spin_lock_init(&tp->lock);
#endif
        /* 初始化一些软件参数,即rtl8125_private结构的*tp*/
        rtl8125_init_software_variable(dev);

        /* 这里要注意 */
        RTL_NET_DEVICE_OPS(rtl8125_netdev_ops);

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,22)
        /* ethtool相关 */
        SET_ETHTOOL_OPS(dev, &rtl8125_ethtool_ops);
#endif

        dev->watchdog_timeo = RTL8125_TX_TIMEOUT;
        dev->irq = rtl8125_get_irq(pdev);
        dev->base_addr = (unsigned long) ioaddr;

        rtl8125_init_napi(tp);

#ifdef CONFIG_R8125_VLAN
        if (tp->mcfg != CFG_METHOD_DEFAULT) {
                dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
                dev->vlan_rx_kill_vid = rtl8125_vlan_rx_kill_vid;
#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
        }
#endif

        /* There has been a number of reports that using SG/TSO results in
         * tx timeouts. However for a lot of people SG/TSO works fine.
         * Therefore disable both features by default, but allow users to
         * enable them. Use at own risk!
         */
        tp->cp_cmd |= RTL_R16(tp, CPlusCmd);
        if (tp->mcfg != CFG_METHOD_DEFAULT) {
                dev->features |= NETIF_F_IP_CSUM;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)
                tp->cp_cmd |= RxChkSum;
#else
                dev->features |= NETIF_F_RXCSUM;
                dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
                                   NETIF_F_RXCSUM | NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
                dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
                                     NETIF_F_HIGHDMA;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0)
                dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
#endif //LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0)
                dev->hw_features |= NETIF_F_RXALL;
                dev->hw_features |= NETIF_F_RXFCS;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
                dev->hw_features |= NETIF_F_IPV6_CSUM | NETIF_F_TSO6;
                dev->features |=  NETIF_F_IPV6_CSUM;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,19,0)
                netif_set_tso_max_size(dev, LSO_64K);
                netif_set_tso_max_segs(dev, NIC_MAX_PHYS_BUF_COUNT_LSO2);
#else //LINUX_VERSION_CODE >= KERNEL_VERSION(5,19,0)
                netif_set_gso_max_size(dev, LSO_64K);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0)
                dev->gso_max_segs = NIC_MAX_PHYS_BUF_COUNT_LSO2;
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
                dev->gso_min_segs = NIC_MIN_PHYS_BUF_COUNT;
#endif //LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
#endif //LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0)
#endif //LINUX_VERSION_CODE >= KERNEL_VERSION(5,19,0)

#endif //LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
#endif //LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)

#ifdef ENABLE_RSS_SUPPORT
                if (tp->EnableRss) {
                        dev->hw_features |= NETIF_F_RXHASH;
                        dev->features |=  NETIF_F_RXHASH;
                }
#endif
        }

#ifdef ENABLE_DASH_SUPPORT
        if (tp->DASH)
                AllocateDashShareMemory(dev);
#endif

#ifdef ENABLE_LIB_SUPPORT
        ATOMIC_INIT_NOTIFIER_HEAD(&tp->lib_nh);
#endif
        rtl8125_init_all_schedule_work(tp);

        rc = rtl8125_set_real_num_queue(tp);
        if (rc < 0)
                goto err_out;

        rtl8125_exit_oob(dev);

        rtl8125_powerup_pll(dev);

        rtl8125_hw_init(dev);

        rtl8125_hw_reset(dev);

        /* Get production from EEPROM */
        rtl8125_eeprom_type(tp);

        if (tp->eeprom_type == EEPROM_TYPE_93C46 || tp->eeprom_type == EEPROM_TYPE_93C56)
                rtl8125_set_eeprom_sel_low(tp);

        rtl8125_get_mac_address(dev);

        tp->fw_name = rtl_chip_fw_infos[tp->mcfg].fw_name;

        tp->tally_vaddr = dma_alloc_coherent(&pdev->dev, sizeof(*tp->tally_vaddr),
                                             &tp->tally_paddr, GFP_KERNEL);
        if (!tp->tally_vaddr) {
                rc = -ENOMEM;
                goto err_out;
        }

        rtl8125_tally_counter_clear(tp);

        pci_set_drvdata(pdev, dev);

        rc = register_netdev(dev);
        if (rc)
                goto err_out;

        printk(KERN_INFO "%s: This product is covered by one or more of the following patents: US6,570,884, US6,115,776, and US6,327,625.\n", MODULENAME);

        rtl8125_disable_rxdvgate(dev);

        device_set_wakeup_enable(&pdev->dev, tp->wol_enabled);

        netif_carrier_off(dev);

        printk("%s", GPL_CLAIM);

out:
        return rc;

err_out:
        if (tp->tally_vaddr != NULL) {
                dma_free_coherent(&pdev->dev, sizeof(*tp->tally_vaddr), tp->tally_vaddr,
                                  tp->tally_paddr);

                tp->tally_vaddr = NULL;
        }
#ifdef  CONFIG_R8125_NAPI
        rtl8125_del_napi(tp);
#endif
        rtl8125_disable_msi(pdev, tp);

err_out_1:
        rtl8125_release_board(pdev, dev);

        goto out;
}

2.2.1 rtl8125_try_msi

当一个数据帧通过 DMA 写到内核内存 ringbuffer 后,网卡通过硬件中断(IRQ)通知其他系统。 设备有三种方式触发一个中断:

(1)MSI-X

(2)MSI

(3)legacy interrupts

设备驱动的实现也因此而异。驱动必须判断出设备支持哪种中断方式,然后注册相应的中断处理函数,这些函数在中断发生的时候会被执行。

MSI-X 中断是比较推荐的方式,尤其是对于支持多队列的网卡。 因为每个 RX 队列有独立的 MSI-X 中断,因此可以被不同的CPU处理(通过irqbalance 方式,或者修改 /proc/irq/IRQ_NUMBER/smp_affinity)。后面会看到 ,处理中断的 CPU 也是随后处理这个包的 CPU。这样的话,从网卡硬件中断的层面就可以设置让收到的包被不同的 CPU 处理。

如果不支持 MSI-X,那 MSI 相比于传统中断方式仍然有一些优势,驱动仍然会优先考虑它。

8125支援多队列,一般是使用MSI-X的中断方式。

static int rtl8125_try_msi(struct rtl8125_private *tp)
{
        struct pci_dev *pdev = tp->pci_dev;
        unsigned msi = 0;
        int nvecs = 1;

        tp->max_irq_nvecs = 1;
        tp->min_irq_nvecs = 1;
#ifndef DISABLE_MULTI_MSIX_VECTOR
        switch (tp->mcfg) {
        case CFG_METHOD_4:
        case CFG_METHOD_5:
        case CFG_METHOD_7:
                tp->max_irq_nvecs = R8125_MAX_MSIX_VEC_8125B;
                tp->min_irq_nvecs = R8125_MIN_MSIX_VEC_8125B;
                break;
        }
#endif

#if defined(RTL_USE_NEW_INTR_API)
        if ((nvecs = pci_alloc_irq_vectors(pdev, tp->min_irq_nvecs, tp->max_irq_nvecs, PCI_IRQ_MSIX)) > 0)
                msi |= RTL_FEATURE_MSIX;
        else if ((nvecs = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES)) > 0 &&
                 pci_dev_msi_enabled(pdev))
                msi |= RTL_FEATURE_MSI;
#elif LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13)
        if ((nvecs = rtl8125_enable_msix(tp)) > 0)
                msi |= RTL_FEATURE_MSIX;
        else if (!pci_enable_msi(pdev))
                msi |= RTL_FEATURE_MSI;
#endif
        if (!(msi & (RTL_FEATURE_MSI | RTL_FEATURE_MSIX)))
                dev_info(&pdev->dev, "no MSI/MSI-X. Back to INTx.\n");

        if (!(msi & RTL_FEATURE_MSIX) || nvecs < 1)
                nvecs = 1;

        tp->irq_nvecs = nvecs;

        tp->features |= msi;

        return nvecs;
}

2.2.2 rtl8125_init_napi

NAPI是综合中断方式与轮询方式的技术。数据量低时采用中断,数据量高时采用轮询。平时是中断方式,当有数据到达时,会触发中断处理函数执行,中断处理函数关闭中断开始处理。如果此时有数据到达,则没必要再触发中断了,因为中断处理函数中会轮询处理数据,直到没有新数据时才打开中断。这里分别init了tx和rx的poll函数。

static void rtl8125_init_napi(struct rtl8125_private *tp)
{
        int i;

        for (i=0; i<tp->irq_nvecs; i++) {
                struct r8125_napi *r8125napi = &tp->r8125napi[i];
#ifdef CONFIG_R8125_NAPI
                int (*poll)(struct napi_struct *, int);

                if (tp->features & RTL_FEATURE_MSIX &&
                    tp->HwCurrIsrVer == 2) {
                        if (i < R8125_MAX_RX_QUEUES_VEC_V3)
                                poll = rtl8125_poll_msix_rx;
                        else if (i == 16 || i == 18)
                                poll = rtl8125_poll_msix_tx;
                        else
                                poll = rtl8125_poll_msix_other;
                } else {
                        poll = rtl8125_poll;
                }

                RTL_NAPI_CONFIG(tp->dev, r8125napi, poll, R8125_NAPI_WEIGHT);
#endif

                r8125napi->priv = tp;
                r8125napi->index = i;
        }
}

2.2.3 rtl8125_init_all_schedule_work

这里是在初始化工作队列,它是将操作(或回调)延期异步执行的一种机制。工作队列可以把工作推后,交由一个内核线程去执行,并且工作队列是执行在线程上下文中,因此工作执行过程中可以被重新调度、抢占、睡眠。

static void rtl8125_init_all_schedule_work(struct rtl8125_private *tp)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
        INIT_WORK(&tp->reset_task, rtl8125_reset_task, dev);
        INIT_WORK(&tp->esd_task, rtl8125_esd_task, dev);
        INIT_WORK(&tp->linkchg_task, rtl8125_linkchg_task, dev);
#else
        INIT_DELAYED_WORK(&tp->reset_task, rtl8125_reset_task);
        INIT_DELAYED_WORK(&tp->esd_task, rtl8125_esd_task);
        INIT_DELAYED_WORK(&tp->linkchg_task, rtl8125_linkchg_task);
#endif
}

3 以太网相关初始化

3.1 net_device_ops

struct net_device_ops 可以通过链接里的内容细看,This structure defines the management hooks for network devices。

static const struct net_device_ops rtl8125_netdev_ops = {
        .ndo_open           = rtl8125_open,
        .ndo_stop           = rtl8125_close,
        .ndo_get_stats      = rtl8125_get_stats,
        .ndo_start_xmit     = rtl8125_start_xmit,
        .ndo_tx_timeout     = rtl8125_tx_timeout,
        .ndo_change_mtu     = rtl8125_change_mtu,
        .ndo_set_mac_address    = rtl8125_set_mac_address,
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0)
        .ndo_do_ioctl       = rtl8125_do_ioctl,
#else
        .ndo_siocdevprivate = rtl8125_siocdevprivate,
        .ndo_eth_ioctl      = rtl8125_do_ioctl,
#endif //LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0)
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,1,0)
        .ndo_set_multicast_list = rtl8125_set_rx_mode,
#else
        .ndo_set_rx_mode    = rtl8125_set_rx_mode,
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)
#ifdef CONFIG_R8125_VLAN
        .ndo_vlan_rx_register   = rtl8125_vlan_rx_register,
#endif
#else
        .ndo_fix_features   = rtl8125_fix_features,
        .ndo_set_features   = rtl8125_set_features,
#endif
#ifdef CONFIG_NET_POLL_CONTROLLER
        .ndo_poll_controller    = rtl8125_netpoll,
#endif
};

这里注意:

/* 
 * int (*ndo_open)(struct net_device *dev);
 *     This function is called when a network device transitions to the up
 *     state.
 */

 所以,函数rtl8125_open() 会被调用。

int rtl8125_open(struct net_device *dev)
{
        struct rtl8125_private *tp = netdev_priv(dev);
        int retval;

        retval = -ENOMEM;

#ifdef ENABLE_R8125_PROCFS
        /* rtl8125可以通过cat /proc/net/r8125/ethx/查看一些信息 */
        rtl8125_proc_init(dev);
#endif
        /* set rx buffer size */
        rtl8125_set_rxbufsize(tp, dev);
        /*
         * Rx and Tx descriptors needs 256 bytes alignment.
         * pci_alloc_consistent provides more.
         */
        /* alloc trx desc */
        if (rtl8125_alloc_tx_desc(tp) < 0 || rtl8125_alloc_rx_desc(tp) < 0)
                goto err_free_all_allocated_mem;

        /* 初始化ring,比如ring的一些变量,初始化ring buffer等等*/
        retval = rtl8125_init_ring(dev);
        if (retval < 0)
                goto err_free_all_allocated_mem;

        retval = rtl8125_alloc_patch_mem(tp);
        if (retval < 0)
                goto err_free_all_allocated_mem;

        /* 指定中断处理函数等 */
        retval = rtl8125_alloc_irq(tp);
        if (retval < 0)
                goto err_free_all_allocated_mem;

        if (netif_msg_probe(tp)) {
                printk(KERN_INFO "%s: 0x%lx, "
                       "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, "
                       "IRQ %d\n",
                       dev->name,
                       dev->base_addr,
                       dev->dev_addr[0], dev->dev_addr[1],
                       dev->dev_addr[2], dev->dev_addr[3],
                       dev->dev_addr[4], dev->dev_addr[5], dev->irq);
        }

#ifdef ENABLE_USE_FIRMWARE_FILE
        rtl8125_request_firmware(tp);
#endif
        /* enables bus-mastering for device dev */
        pci_set_master(tp->pci_dev);

#ifdef  CONFIG_R8125_NAPI
        rtl8125_enable_napi(tp);
#endif

        rtl8125_exit_oob(dev);

        rtl8125_up(dev);

#ifdef ENABLE_PTP_SUPPORT
        if (tp->EnablePtp)
                rtl8125_ptp_init(tp);
#endif
        clear_bit(R8125_FLAG_DOWN, tp->task_flags);

        if (tp->resume_not_chg_speed)
                rtl8125_check_link_status(dev);
        else
                rtl8125_set_speed(dev, tp->autoneg, tp->speed, tp->duplex, tp->advertising);

        if (tp->esd_flag == 0) {
                //rtl8125_request_esd_timer(dev);

                rtl8125_schedule_esd_work(tp);
        }

        //rtl8125_request_link_timer(dev);

        rtl8125_enable_hw_linkchg_interrupt(tp);

out:

        return retval;

err_free_all_allocated_mem:
        rtl8125_free_alloc_resources(tp);

        goto out;
}

3.2 注册 ethtool

static const struct ethtool_ops rtl8125_ethtool_ops = {
        .get_drvinfo        = rtl8125_get_drvinfo,
        .get_regs_len       = rtl8125_get_regs_len,
        .get_link       = ethtool_op_get_link,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
        .get_ringparam      = rtl8125_get_ringparam,
        .set_ringparam      = rtl8125_set_ringparam,
#endif //LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,6,0)
        .get_settings       = rtl8125_get_settings,
        .set_settings       = rtl8125_set_settings,
#else
        .get_link_ksettings       = rtl8125_get_settings,
        .set_link_ksettings       = rtl8125_set_settings,
#endif //LINUX_VERSION_CODE < KERNEL_VERSION(4,6,0)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
        .get_pauseparam     = rtl8125_get_pauseparam,
        .set_pauseparam     = rtl8125_set_pauseparam,
#endif //LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
        .get_msglevel       = rtl8125_get_msglevel,
        .set_msglevel       = rtl8125_set_msglevel,
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)
        .get_rx_csum        = rtl8125_get_rx_csum,
        .set_rx_csum        = rtl8125_set_rx_csum,
        .get_tx_csum        = rtl8125_get_tx_csum,
        .set_tx_csum        = rtl8125_set_tx_csum,
        .get_sg         = ethtool_op_get_sg,
        .set_sg         = ethtool_op_set_sg,
#ifdef NETIF_F_TSO
        .get_tso        = ethtool_op_get_tso,
        .set_tso        = ethtool_op_set_tso,
#endif //NETIF_F_TSO
#endif //LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)
        .get_regs       = rtl8125_get_regs,
        .get_wol        = rtl8125_get_wol,
        .set_wol        = rtl8125_set_wol,
        .get_strings        = rtl8125_get_strings,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
        .get_stats_count    = rtl8125_get_stats_count,
#else
        .get_sset_count     = rtl8125_get_sset_count,
#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
        .get_ethtool_stats  = rtl8125_get_ethtool_stats,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
#ifdef ETHTOOL_GPERMADDR
        .get_perm_addr      = ethtool_op_get_perm_addr,
#endif //ETHTOOL_GPERMADDR
#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
        .get_eeprom     = rtl_get_eeprom,
        .get_eeprom_len     = rtl_get_eeprom_len,
#ifdef ENABLE_RSS_SUPPORT
        .get_rxnfc		= rtl8125_get_rxnfc,
        .set_rxnfc		= rtl8125_set_rxnfc,
        .get_rxfh_indir_size	= rtl8125_rss_indir_size,
        .get_rxfh_key_size	= rtl8125_get_rxfh_key_size,
        .get_rxfh		= rtl8125_get_rxfh,
        .set_rxfh		= rtl8125_set_rxfh,
#endif //ENABLE_RSS_SUPPORT
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
#ifdef ENABLE_PTP_SUPPORT
        .get_ts_info        = rtl8125_get_ts_info,
#else
        .get_ts_info        = ethtool_op_get_ts_info,
#endif //ENABLE_PTP_SUPPORT
#endif //LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)
        .get_eee = rtl_ethtool_get_eee,
        .set_eee = rtl_ethtool_set_eee,
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0) */
        .nway_reset = rtl_nway_reset,

};

第二篇的链接:Realtek 8125驱动分析第二篇——触发硬件中断

参考文献如下,十分推荐的一篇文章。

Linux 网络栈接收数据(RX):原理及内核实现(2022)icon-default.png?t=N7T8http://arthurchiao.art/blog/linux-net-stack-implementation-rx-zh/

如果觉得这篇文章有用的话,可以点赞、评论或者收藏,万分感谢,goodbye~

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值