说明:如需转载,请说明出处,否则发现一律删除
一,设备描述
linux下的驱动程序都有一套通用的规则,无论是字符设备,块设备还是网络设备都必须有一个结构来描述这个设备。但是网络设备没有主次设备号,它是通过socket套接字来访问的。
每个网络接口设备都由一个net_device结构体来描述,该结构可使用如下内核函数动态分配:
1、struct net_device *alloc_netdev(int sizeof_priv, const char *mask, void(*setup)(struct net_device*))
sizeof_priv : 私有数据区大小; mask: 设备名; setup: 初始化函数
2、struct net_device *alloc_etherdev(int sizeof_priv)
结构net_device的主要成员包括:
char name[IFNAMSIZ] 设备名,如:eth%d
unsigned long state 设备状态
unsigned long base_addr I/O基地址
unsigned int irq 中断号
int (*init) (struct net_device *dev)
初始化函数。该函数在register_netdev时被调用来完成对net_device结构的初始化。
二,设备方法
和字符设备,块设备驱动程序一样,网络设备也要声明能够操作它的函数。有些操作可以保留为NULL,有的可以通过ether_setup来使用默认设置。网络接口的设备方法可分为两组:基本的和可选的,基本方法包括那些使用接口所必须的;可选的方法实现更多高级的功能。
(1)基本方法
int (*open) (struct net_device *dev) 打开接口。ifconfig激活时,接口将被打开。
int (*stop) (struct net_device *dev) 停止接口。
int (*hard_start_xmit) (struct sk_buff *skb, struct net_device *dev) 数据发送函数
(2)可选方法
int (*do_ioctl) (struct net_device *dev, struct ifreq *ifr, int cmd) 处理特定于接口的ioctl命令
int (*set_mac_address) (struct net_device *dev, void *addr) 改变Mac地址的函数,需要硬件支持该功能
三,设备注册
网络接口驱动的注册方式与字符设备驱动程序的不同之处在于它没有主次设备号,并使用如下函数注册:
int register_netdev(struct net_device *dev)
四,网络数据包的描述(sk_buff结构体)
linux内核中的每个网络数据包都由一个套接字缓冲区结构struct sk_buff 来描述,即一个sk_buff结构就是一个包,指向sk_buff的指针通常被称做skb。
该结构包含如下重要成员:
struct device *dev 处理该包的设备
__u32 saddr IP源地址
__u32 daddr IP目的地址
__u32 raddr IP路由器地址
unsigned char *head 分配空间的开始
unsigned char *data 有效数据的开始
unsigned char *tail 有效数据的结束
unsigned char *end 分配空间的结束
unsigned long len 有效数据的长度
五,SKB操作函数
struct sk_buff *alloc_skb(unsigned int len, int priority)
分配一个sk_buff结构,供协议栈代码使用
struct sk_buff *dev_alloc_skb(unsigned int len)
分配一个sk_buff结构,供驱动代码使用
unsigned char *skb_push(struct sk_buff *skb, int len)
向后移动skb的tail指针,并返回tail移动之前的值。函数常用来往sk_buff数据尾部添加数据
unsigned char *skb_put(struct sk_buff *skb, int len)
向前移动skb的head指针,并返回head移动之后的值。函数常用来往sk_buff数据头部 添加数据
kfree_skb(struct sk_buff *skb)
释放一个sk_buff结构, 供协议栈代码使用
dev_kfree_skb(struct sk_buff *skb)
释放一个sk_buff结构,供驱动代码使用
六,设备打开
Open请求任何它需要的系统资源并且启动接口:
(1)注册中断,DMA等
(2)设置寄存器,启动设备
(3)启动发送队列
例:
int net_open(struct net_device *dev)
{
/*申请中断*/
request_irq(dev->irq, &net_interrupt, SA_SHIRQ,
“dm9000”, dev);
/* 设置寄存器,启动设备*/
...... ...... ...... ......
/*启动发送队列*/
netif_start_queue(dev);
}
七,数据发送
当核心需要发送一个数据包时,它调用hard_start_transmit函数,该函数将最终调用到net_device结构中的hard_start_xmit函数指针。
八,数据接收
网络接口驱动可以实现两种方式的报文接收:中断和查询,linux中驱动多采用中断方式,bootloader多采用查询。网络接口通常支持3中类型的中断:新报文到达中断,报文发送完成中断和出错中断。中断处理程序可通过查看网卡中的中断状态寄存器,来分辨出中断类型。
接收数据的流程:
(1) 分配skb
skb = dev_alloc_skb(pkt->datalen + 2)
(2) 从硬件中读取数据到skb
(3)调用netif_rx将数据交给协议栈
netif_rx(skb)