Linux网络设备驱动架构

 驱动架构自上而下分为4层:

  • 协议接口层

  • 设备接口层

  • 设备驱动功能层

  • 网络设备与媒介层

    

 

协议接口层

    协议接口层主要功能是给上层协议提供接收和发送的接口。当内核协议栈需要发送数据时,会通过调用 dev_queue_xmit 函数来发送数据。

 

         

                                                dev_queue_xmit执行流程

 

    同样内核协议栈接收数据也是通过协议接口层的 netif_rx 函数来进行的。传递的数据被描述为套接字缓冲区,用struct sk_buff结构描述,在openharmony中,该结构体定义位于kernel/linux/linux-5.10/include/linux/skbuff.h中,用于在网络子系统中的各层之间传输数据,该结构在整个网络收发过程中贯穿始终。

         

    sk_buff 是 Linux 网络驱动中一个非常 重要的结构体,网络数据就是以 sk_buff 保存的,各个协议层在 sk_buff 中添加自己的协议头,最终由底层驱动将 sk_buff 中的数据发送出去。网络数据的接收过程恰好相反,网络底层驱动将 接收到的原始数据打包成 sk_buff,然后发送给上层协议,上层会取掉相应的头部,然后将最终的数据发送给用户。

 

设备接口层

    网络设备接口层用于抽象各种不同的网络设备,Linux 内核使用 net_device 结构体表示一个具体的网络设备,net_device 是整个网络驱动的灵魂。网络驱动的核心就是初始化 net_device 结构体中的各个成员变量,然后将初始化完成以 后的 net_device 注册到 Linux 内核中。在openharmony中,该结构体定义位于kernel/linux/linux-5.10/include/linux/netdevice.h中。net_device 是一个庞大的结构体。

        

下面介绍一些关键的成员变量,如下:

1、name 是网络设备的名字。

2、mem_end 是共享内存结束地址。

3、mem_start 是共享内存起始地址。

4、base_addr 是网络设备 I/O 地址。

5、irq 是网络设备的中断号。

6、dev_list 是全局网络设备列表。

7、napi_list 是 napi 网络设备的列表入口。

8、unreg_list 是注销(unregister)的网络设备列表入口。

9、close_list 是关闭的网络设备列表入口。

10、netdev_ops 是网络设备的操作集函数,包含了一系列的网络设备操作回调函数,稍后会讲解 netdev_ops 结构体。

 

设备驱动功能层

    net_device 有个非常重要的成员变量:struct net_device_ops。,这就是网络设备的操作集,来描述对网卡的各种操作。以海思芯片为例,net_device_ops操作集定义在kernel/linux/linux-5.10/drivers/net/ethernet/hisilicon/hisi_femac.c中。net_device_ops 结构体里面都是一些以“ndo_”开头的函数,这些函数就需要网络驱动编写人员去实现,不需要全部都实现,根据实际驱动情况实现其中一部分即可。

        

1、ndo_open 函数,打开网络设备的时候此函数会执行,网络驱动程序需要实现此函数,非常重要!会在此函数中做如下工作:

初始化接收缓冲区。

  • 如果使用 NAPI 的话要使能 NAPI 模块,通过 napi_enable 函数来使能。
  • 硬件相关初始化。
  • 开启 PHY。
  • 调用 netif_tx_start_all_queues 来使能传输队列,也可能调用 netif_start_queue 函数。

 

2、ndo_stop 函数,关闭网络设备的时候此函数会执行,网络驱动程序也需要实现

此函数。会在此函数中做如下工作:

  • 停止 PHY。
  • 停止 NAPI 功能。
  • 停止发送功能。
  • 关闭 MAC。
  • 断开 PHY 连接。
  • 释放数据缓冲区。

 

3、ndo_start_xmit 函数,当需要发送数据的时候此函数就会执行,此函数有一个参数为 sk_buff 结构体指针,sk_buff 结构体在 Linux 的网络驱动中非常重要,sk_buff 保存了上层传递给网络驱动层的数据。也就是说,要发送出去的数据都存在了 sk_buff 中。如果发送成功的话此函数返回 NETDEV_TX_OK,如果发送失败了就返回 NETDEV_TX_BUSY,如果发送失败了我们就需要停止队列。

 

4、ndo_set_rx_mode 函数,此函数用于改变地址过滤列表,根据 net_device 的 flags

成员变量来设置 SoC 的网络外设寄存器。比如 flags 可能为 IFF_PROMISC、IFF_ALLMULTI 或

IFF_MULTICAST,分别表示混杂模式、单播模式或多播模式。

 

5、ndo_set_mac_address 函数,此函数用于修改网卡的 MAC 地址,设置 net_device

的 dev_addr 成员变量,并且将 MAC 地址写入到网络外设的硬件寄存器中。

 

6、ndo_do_ioctl 函数,用户程序调用 ioctl 的时候此函数就会执行,比如 PHY 芯

片相关的命令操作,一般会直接调用 phy_mii_ioctl 函数。

 

初始化

    首先从platform 开始。

static struct platform_driver hisi_femac_driver = {
    .driver = {
        .name = "hisi-femac",
        .of_match_table = hisi_femac_match,
    },
    .probe = hisi_femac_drv_probe,
    .remove = hisi_femac_drv_remove,
#ifdef CONFIG_PM
    .suspend = hisi_femac_drv_suspend,
    .resume = hisi_femac_drv_resume,
#endif
};

module_platform_driver(hisi_femac_driver);

    在这个函数中,定义了一个静态的平台驱动结构体 hisi_femac_driver,用于与海思的以太网访问控制器设备进行交互。通过 probe 和 remove 函数完成设备的初始化和移除操作,最后,使用 module_platform_driver 宏来注册这个平台驱动。

    驱动和设备匹配上后,会调用驱动的 probe 函数hisi_femac_drv_probe。

        

   函数较长,仅截取部分。该函数为 struct net_device 结构体申请内存,并进行了初始化。

 

        

   最终会调用ether_setup函数对dev成员进行初始化。初始化完ndev后,设置了netdev_ops 和 mac 地址,最后调用register_netdev函数注册了网络设备。

 

   接着分析net_device_ops,前面已经介绍过net_device_ops。当用户执行命令ifconfig eth0 up后会调用网卡驱动的 open 函数,即hisi_femac_net_open函数。

        

 

发送过程分析

  应用程序调用send函数去发送数据,内核协议栈会将数据构造成struct sk_buff后放入等待队列,调用start_xmit通知网卡发送数据。即调用hisi_femac_net_xmit函数。

        

   函数较长,仅截取部分。

 

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐