STM32MP157 | 虚拟网卡设备驱动

一、网络设备驱动

1. 概念

网络设备是Linux的第三类标准设备,没有对应的设备文件,使用内部设备名访问。网络设备及其驱动属于整个TCP/IP协议层的一部分,实现遵循TCP/IP协议栈的要求。
网络设备异步接收外部的数据包,主动请求将硬件收到的数据包交给内核。

2. 网络设备驱动框架

在这里插入图片描述

3.套接字缓冲区

sk_buff中重要的字段:

1.各层协议头
	传输层,网络层,链路层各个协议头
2.数据缓冲区指针head,end,data,tail
	指向缓冲区的不同位置
3.长度信息
	有效数据长度

使用sk_buff处理数据包在协议层之间的传递,只需要移动其中的指针,效率比内存拷贝高。
在这里插入图片描述

4. sk_buff的操作接口

(1)分配

struct sk_buff*dev_alloc_skb(unsigned int len);

//分配成功之后,因为还没有存放具体的网络数据包,所以sk_buff的data、tail指针都指向存储空间的起始地址head,而len的大小则为0。

(2)释放
void dev_kfree_skb(struct sk_buff *skb);
//用于释放被alloc_skb()函数分配的套接字缓冲区和数据缓冲区

(3)指针移动

//Linux套接字缓冲区中的数据缓冲区指针移动操作包括put(放置)、push(推)、pull(拉)、reserve(保留)等。

//put操作
//将tail指针下移,增加sk_buff的len值,并返回skb->tail原先的值。
unsigned char/skb_put(struct sk_buff *skb, unsigned int len);

//push操作
//将data指针上移,因此也要增加sk_buff的len值。
unsigned char*skb_push(struct sk_buff *skb, unsigned int len);

//pull操作
//将data指针下移,并减小skb的len值。
unsigned char* skb _pull(struct sk_buff *skb, unsigned int len);

//reserve操作
//同时将skb-二data与skb->tail增加len字节数
void skb_reserve(struct sk_buff *skb, unsigned int len);
 
 //获取skb->data与skb->head之间空闲空间的大小 
 static inline unsigned int skb_headroom(const struct sk_buff*skb)
 //获取skb->tail与skb->end之间空闲空间的大小static inline int skb_tailroom(const struct sk_buff *skb)

5.net_device结构

net_device在内核中代表一个网络设备,网络设备驱动的实现实际上就是分配填充net device结构并注册到内核的过程,实现上层协议和网络硬件设备之间的数据传递。

net_device是一个巨大的结构体,主要包含网络设备的属性描述和操作接口。实现网络设备驱动只需要了解其中相关的部分即可。

(1)成员组成

	1.全局成员
    2.硬件相关成员
    3.接口相关成员
    4.设备方法成员
    5.公用成员

1)全局成员

1 char name[ IFNAMSIZ]; //网络设备名
2 int (*init)(struct net_device *dev) ;//net_device初始化函数

2)硬件成员

1 unsigned long mem_end;          /*设备使用共享内存结束地址*/
2 unsigned long mem_start;        /*设备使用共享内存起始地址*/
3 unsigned long base_addr;        /*设备Io基地址*/
4 unsigned int irq;               /*设备使用的中断号*/
5 unsigned char if_port;          /*多端口设备的端口号*/
6 unsigned char dma;              /*DMA通道*/

3)接口相关成员

1 unsigned int mtu;               /*MTU值*/
2 unsigned short type;            /*网络硬件接口类型*/
3 unsigned short hard_header_len; /*网络硬件设备头长度*/
4 unsigned char *dev_addr ;       /*MAc地址*/
5 unsigned int flags;             /*网络接口标志*/

4)设备方法成员

const struct net_device_ops *netdev_ops;//操作函数集合

int (*ndo_open)(struct net_device *dev);//打开接口
int (*ndo_stop) ( struct net_device *dev);//停止接口
//初始化数据包的传输,将完整的数据包放入sk_buff中
netdev_tx_t (*ndo_start_xmit) (struct sk_buff *skb, struct net_device *dev)
//数据包发送超时调用的函数,通常是重新发送
void (*ndo_tx_timeout) ( struct net_device *dev);

(2) net_device的注册和注销

int register_netdev(struct net_device *dev);//注册2
void unregister _netdev(struct net_device *dev);//注销

(3)网络设备打开和关闭需要进行的操作

int xxx_open( struct net_device *dev)
{
	//申请设备资源 ----- IO内存中断DMA.. .
    //激活设备发送队列
	void netif_start_queue( struct net_device *dev ) ;
}
int xxx_release( struct net_device *dev)
{
    //释放设备资源 ----- Io内存中断 DMA. . .
    //停止设备传输数据包
	void netif_stop_queue( struct net_device *dev ) ;
}

(4)网络设备发送和超时处理进行的操作

int xxx_tx ( struct sk_buff *skb,struct det_device *dev)
{
	//从sk_buff中获取有效数据
	// ...
	//记录发送时间
	dev->trans_siart = jiffies;
	//通过设置网卡硬件的寄存器发送数据包
    //xxx_hw_tx( data,len, dev);
}
void xxx_tx_timeout( struct det_device *dev)
{
    // ...
    //重新发送
	void netif_wake_queue( struct net_device *dev) ;
}

(5)网络设备数据接收进行的操作

//网卡接收中断函数
void xxx_interrupt(int irq,void *dev_id , struct pt_regs *regs )
{
    //接收中断
    //分配sk_buf结构
    //...
    //从网卡硬件结束数据
    // ...
    //获取上层协议类型
    skb- >protocol = eth_type_trans(skb, dev);
    //将数据包交给上层协议层
    netif_rx( skb);
    //记录时间戳
    dev- >last_rx =jiffies;
}

二、编写虚拟网卡驱动

1.先写个模块

#include <linux/init.h>
#include <linux/module.h>

static int __init virnet_init(void)
{
    
    return 0;
}
static void __exit virnet_exit(void)
{

}
module_init(virnet_init);
module_exit(virnet_exit);
MODULE_LICENSE("GPL");

2.写个Makefile编译一下

ifeq ($(arch),arm)
KERNELDIR :=/home/linux/linux-5.10.61
CROSS_COMPILE ?=arm-linux-gnueabihf-
else 
KERNELDIR :=/lib/modules/$(shell uname -r)/build
CROSS_COMPILE ?=
endif 

modname ?=
PWD :=$(shell pwd)

CC :=$(CROSS_COMPILE)gcc

all:
	make -C $(KERNELDIR) M=$(PWD) modules
	# $(CC) test.c -o test
clean:
	make -C $(KERNELDIR) M=$(PWD) clean
	# rm test

install:
	cp *.ko ~/nfs/rootfs/
	# cp test ~/nfs/rootfs/

help:
	echo "make arch = arm or x86 modname= dirvers file name"

obj-m:=$(modname).o

3.再搭个网卡设备驱动框架

#include <linux/init.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>

//网络设备结构体指针
struct net_device *virnet_card = NULL;
int virnet_card_init(struct net_device *dev)
{
    //以太网卡的初始化
    ether_setup(dev);
    //设置网卡名
    strcpy(dev->name,"virnetdevice");
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
int virnet_card_open(struct net_device *dev)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

netdev_tx_t virnet_card_xmit(struct sk_buff *skb,struct net_device *dev)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
//操作函数集合
struct net_device_ops vir_netdev_ops = {
    .ndo_init = virnet_card_init,
    .ndo_open = virnet_card_open,
    .ndo_start_xmit = virnet_card_xmit,
};

//加载函数
static int __init virnet_init(void)
{
    //申请net_device空间
    virnet_card = alloc_etherdev(0);
    //设置net_devicede 操作函数集合
    virnet_card->netdev_ops = &vir_netdev_ops;
    //注册net_device
    register_netdev(virnet_card);
    return 0;
}
//卸载函数
static void __exit virnet_exit(void)
{
    //注销 net_deviec
    unregister_netdev(virnet_card);

    //释放net_device空间
    free_netdev(virnet_card);
}
module_init(virnet_init);
module_exit(virnet_exit);
MODULE_LICENSE("GPL");

3. 编译一下

下载到开发板上

在这里插入图片描述

可以看到有新的网卡设备产生

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值