设备描述
每个网络接口都由一个 net_device(位于 <linux/netdevice.h>头文件的的第840行) 结构描述,该结构可用于如下内核函数动态分配:
- *struct net_device *alloc_netdev (int sizeof_priv,const char *mask,void (*setup)(struct net_device ))
参数:
sizeof_priv:私有数据区大小;
mask:设备名;
setup:初始化函数,用来设置 net_device 结构剩余的部分 - *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 中断号
- init (init)(struct net_device dev)
初始化函数。该函数在register_netdev 时被调用**来完成对 net_device 结构的初始化
其中的基本方法
- **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)
数据发送函数
可选操作
- **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_buffer
linux 内核中的每个网络数据包都由一个套接字缓冲区结构 struct sk_buff 描述,即一个 sk_buff 结构就是一个包,指向 sk_buff 的指针通常被称做skb。
*struct 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操作函数
- 分配一个sk_buff 结构,供协议栈代码使用
struct sk_buff *alloc_skb (unsigned int len,int priority - 分配一个sk_buff 结构,供驱动代码使用
struct sk_buff *dev_alloc_skb(unsigned int len) - unsigned char *skb_push(struct sk_buff *skb,int len)
向后移动skb的tail指针,并返回tail移动之前的值 - unsigned char *skb_put(struct sk_buff *skb,int len)
向前移动skb的head指针,并返回head移动之后的值 - kfree_skb (struct sk_buff *skb)
释放一个 sk_buff 结构,供协议栈使用 - dev_kfree_skb(struct sk_buff *skb)
释放一个 sk_buff 结构,供驱动代码使用
设备打开
open 请求任何它需要的系统资源并且启动接口:
- 注册中断,DMA等
- 设置寄存器,启动设备
- 启动发送队列
举例:
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 中驱动多采用中断方式。
接收流程
- 分配 skb
skb = dev_alloc_skb(pkt->datalen+2) - 从硬件中读取数据到skb
- 调用netif_rx将数据交给协议栈
netif_rc(skb)
中断处理
网络接口通常支持3种类型的中断:新报文到达中断、报文发送完成中断和出错中断。中断处理程序可通过查看网卡中的中断状态寄存器,来分辨出中断类型。