i.MX6ULL驱动开发 | 32 - 手动编写一个虚拟网卡设备

一、Linux内核网络驱动处理流程

1. net_device结构体的申请与释放

net_device结构体实例可以通过动态申请:

#define alloc_netdev(sizeof_priv, name, name_assign_type, setup) \
	alloc_netdev_mqs(sizeof_priv, name, name_assign_type, setup, 1, 1)

#define alloc_netdev_mq(sizeof_priv, name, name_assign_type, setup, count) \
	alloc_netdev_mqs(sizeof_priv, name, name_assign_type, setup, count, \
			 count)

这两个宏底层调用的函数都是alloc_netdev_mqs,原型如下:

struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
				    unsigned char name_assign_type,
				    void (*setup)(struct net_device *),
				    unsigned int txqs, unsigned int rxqs);

参数意义如下:

  • sizeof_priv:设备私有成员的大小
  • name:设备名
  • name_assign_type:名称对齐类型
  • setup:函数指针
  • txqs:要分配的发送队列的数量
  • rxqs:要分配的接收队列的数量

释放net_device结构体的API如下:

void free_netdev(struct net_device *dev);

2. 网络设备驱动的注册与注销

网络驱动注册与注销使用如下两个API,定义在文件include/linux/netdevice.h中:

int register_netdev(struct net_device *dev);
void unregister_netdev(struct net_device *dev);

一般的,在驱动模块初始化的时候注册网卡设备到内核,在驱动模块卸载的时候,从内核注销网卡设备

3. 网络设备的初始化

网络设备初始化主要完成以下工作:

  • 进行硬件准备工作,检查网络设备是否存在,如果存在,则检测设备所使用的硬件资源;
  • 进行软件准备工作,分配net_device结构体并对其数据和函数指针成员赋值;
  • 获得设备的私有信息指针并初始化各成员的值;

4. 网络设备的打开与释放

网络设备的打开函数需要完成以下工作:

  • 使能设备所使用的硬件资源,申请I/O区域、中断、DMA通道等
  • 调用Linux内核提供的API,激活设备发送队列

网络设备的释放函数需要完成以下工作:

  • 调用Linux内核提供的API,停止设备传输包
  • 释放设备所使用的I/O区域、中断、DMA通道等

Linux内核提供的两个API如下:

/**
 *	netif_start_queue - allow transmit
 *	@dev: network device
 *
 *	Allow upper layers to call the device hard_start_xmit routine.
 */
static inline void netif_start_queue(struct net_device *dev)
{
    
    
	netif_tx_start_queue(netdev_get_tx_queue(dev, 0));
}

/**
 *	netif_stop_queue - stop transmitted packets
 *	@dev: network device
 *
 *	Stop upper layers calling the device hard_start_xmit routine.
 *	Used for flow control when transmit resources are unavailable.
 */
static inline void netif_stop_queue(struct net_device *dev)
{
    
    
	netif_tx_stop_queue(netdev_get_tx_queue(dev, 0));
}

5. 数据发送流程

Linux网络子系统在发送数据包时,会调用驱动程序提供的ndo_start_xmit函数,用于启动数据包的发送,所以底层只需要实现该函数指针即可。

发送流程如下:

  • 阻止上层继续向网络设备驱动传递数据包
  • 硬件发送包
  • 记录发送字节和发送时间戳
  • 唤醒上层继续传递数据包

Linux内核提供的唤醒上层传递数据包的API如下:

/**
 *	netif_wake_queue - restart transmit
 *	@dev: network device
 *
 *	Allow upper layers to call the device hard_start_xmit routine.
 *	Used for flow control when transmit resources are available.
 */
static inline void netif_wake_queue(struct net_device *dev)
{
    
    
	netif_tx_wake_queue(netdev_get_tx_queue(dev, 0));
}

6. 数据接收流程

网络设备接收数据的主要方法时由中断引发设备的中断处理函数,中断处理函数中判断中断类型,如果为接收中断,则读取接收到的数据,分配sk_buffer数据结构和数据缓冲区,将接收到的数据复制到数据缓冲区,并调用netif_rx()函数将sk_buffer传递给上层协议

二、实例——编写一个虚拟网卡设备

参考文章:26.Linux-网卡驱动介绍以及制作虚拟网卡驱动(详解)

该博文写的很不错,博主在此基础上,更新了驱动适配到4.14内核。

1. 目的

编写一个虚拟网卡设备,可以添加到内核中,并写一个虚拟的发包收包设备,使得该网卡可以ping通任何ip。

2. 代码实现

https://github.com/Mculover666/linux_driver_study/

3. 效果

3.1. 加载虚拟网卡驱动模块

加载模块之前,查看当前系统中存在的网卡:

加载虚拟网卡驱动模块:

加载之后再查看系统网卡,可以看到多了virt_eth0:

再使用ifconfig查看:

3.2.ping测试

设置网卡ip:

查看路由表:

ping网段内任意ip测试:

可以看到发包数量和收包数量被统计:

猜你喜欢

转载自blog.csdn.net/Mculover666/article/details/126003032