一、网络设备驱动
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. 编译一下
下载到开发板上
可以看到有新的网卡设备产生