分类: LINUX
2015-09-09 20:43:01
************************************ 16.1 linux网络设备驱动的结构*******************************
【结构概述】
图:linux网络设备驱动程序的体系结构
1、网络协议接口层:向网络层协议提供提供统一的数据包收发接口。不论上层协议为ARP还是IP,都通过dev_queue_xmit()函数发送数据,并通过netif_rx()函数接受数据。这一层的存在使得上层协议独立于具体的设备。
2、网络设备接口层:向协议接口层提供统一的用于描述具体网络设备属性和操作的结构体net_device,该结构体是设备驱动功能层中各函数的容器。
3、设备驱动功能层:各函数是网络设备接口层net_device数据结构的具体成员,是驱使网络设备硬件完成相应动作的程序,他通过hard_start_xmit()函数启动发送操作,并通过网络设备上的中断触发接受操作。
4、网络设备与媒介层:完成数据包发送和接受的物理实体,包括网络适配器和具体的传输媒介,网络适配器被驱动功能层中的函数物理上驱动。对于Linux系统而言,网络设备和媒介都可以是虚拟的。
驱动程序员的主要工作:编写设备驱动功能层的相关函数以填充net_device数据结构的内容并将net_device注册;
【16.1.1】网络协议接口层
【1】当上层ARP或IP发包时,将调用网络协议接口层的dev_queue_xmit()函数发送数据包。
dev_queue_xmit(struct sk_buff * skb);
【2】当上层接收数据包,调用netif_rx()函数;
int netif_rx(struct sk_buff *skb);
【3】sk_buff结构体
1、含义:套接字缓冲区
2、作用:用于linux网络子系统中的各层之间传递数据;
3、原型:
struct sk_buff {
struct device *dev;正在处理该包的设备
__u32 saddr ; //IP元地址
__u32 daddr; //IP目的地址
__u32 raddr; //IP路由器地址
…...
unsigned int len , data_len ;
_u16 mac_len , hdr_len ;
……
sk_buff_data_t transport_header;
sk_buff_data_t network_header ;
sk_buff_data_t mac_header;
……
sk_buff_data_t tail ;
sk_buff_data_t end ;
unsigned char * head , * data;
};
4、重要成员:
Head:整个缓冲区的头指针
data:有效数据的头指针
tail: 有效数据的尾指针
end: 指向分配的内存块的结尾
transport_header :传输层包头位置
network_header:网络层包头位置
mac_header:MAC层的包头位置
【4】套接字缓冲区( sk_buff )的操作
1、分配:申请一个套接字缓冲区和一个数据缓冲区;
1) struct sk_buff *alloc_skb(unsigned int len , gfp_t priority)
参数
len:数据缓冲区的空间大小,通常以L1_CACHE_BYTES字节对齐;
priority:内存分配的优先级;
2) struct sk_buff *dev_alloc_skb(unsigned int len);
以GFP_ATOMIC优先级进行skb的分配;
2、释放
linux内核内部使用
1) void kfree_skb(struct sk_buff *skb);
网络设备驱动程序中使用
2)void dev_kfree_skb(struct sk_buff * skb);
用于非中断上下文
3)void dev_kfree_skb_irq(struct sk_buff *skb);
用于中断上下文
4)void dev_kfree_skb_any(struct sk_buff * skb)
在中断和非中断上下文中皆可使用;
3、变更
1)在缓冲区尾部增加数据
unsigned char * skb_put(struct sk_buff * skb , unsigned int len);
会导致skb->tail后移len, 而skb->len增加len的大小;
2)在缓冲区开头增加数据
unsigned char * skb_push(struct sk_buff * skb,unsiged int len );
会导致skb->data前移len,而skb->len增加len的大小;
与之相反功能的是:skb_pull——在缓冲区开头移除数据
3)空缓冲区,调整缓冲区的头部
static inline void skb_reserve(struct sk_buff * skb, int len);
将skb_data 和 skb_tail 同时后移len;
【16.1.2 网络设备接口层 】
【1】net_device:包含网络设备的属性描述和操作接口;
【2】结构体net_device的关键成员
1、全局变量
1) char name(IFNAMESIZ); name:网络设备的名称;
2)int (* init)(struct net_device *dev);
init:初始化函数指针
如果指针被设置,则网络设备驱动注册时将调用此函数初始化net_device结构体,可以不实现此函数并将其值设置为NULL;
2、硬件信息
unsigned long mem_end; //设备使用的共享内存的结束地址
unsigned long mem_start; // 设备使用的共享内存的起始地址
unsigned long base_addr; //网络设备I/O基地址
unsigned char irq; //设备使用的中断号
unsigned char if_port; //指定多端口设备使用哪一个端口
unsigned char dma; //指定分配给设备的DMA通道
3、接口信息
unsigned short hard_header_len; //网络设备的硬件头长度,以太网初始化函数中赋值为ETH_HLEN,即14;
unsigned short type; //接口的硬件类型
unsigned mtu; //最大传输单位
unsigned char dev_addr[MAX_ADDR_LEN]; //存放设备的硬件地址
unsigned char broadcast[MAX_ADDR_LEN]; //存放设备的广播地址
//以太网:这两个地址都是6个字节 —— 广播地址为6个0XFF,MAC地址需要从硬件读出;
unsigned short flags; //网络接口标志
4、设备操作函数
int (*open)(struct net_device * dev); //打开网络接口设备
int(*stop)(struct net_device *dev); //停止网络接口设备
int (*hard_start_xmit)(struct sk_buff * skb , struct net_device * dev); //启动数据包的发送
void (*tx_timeout )(struct net_device *dev);
//数据包发送超时时,调用此函数,重新发包或者重启硬件等;
//完成硬件帧头填充,返回填充的字节数。
int (*hard_header)(struct sk_buff *skb, //套接字缓冲区指针
struct net_device *dev, //设备指针
unsigned short type, //协议类型
void *daddr, //目的地址
void *saddr, //源地址
unsigned len); //数据长度
struct net_device_stats*(*get_stats)(struct net_device *dev);
//获得网络设备的状态信息,包括网络设备详细的流量统计信息;
int (*do_ioctl)(struct net_device *dev , struct ifreq *ifr, int cmd); //进行设备特定的I/O控制
int (*set_config)(struct net_device *dev , struct ifmap *map );
……
……
5、辅助成员
unsigned long trans_start; //最后的数据包开始发送时的时间戳;
unsigned long last_rx; //最后一次接收到数据包的时间戳
……
……
【16.1.3 设备驱动功能层】
【1】编写net_device结构体内部成员的同形函数;
xxx_open(),xxx_stop(),xxx_get_stats()等等
【2】中断触发的网络数据包的接收;
xxx_interrupt() //完成中断类型判断的基本工作
xxx_rx() //完成数据包的生成和递交上层的复杂工作;
【3】定义私有数据和操作
封装一个私有结构体xxx_private,并将该结构体指针赋值给net_device的priv成员。
包括特殊的设备属性和操作、自旋锁与信号量、定时器及统计信息;
【16.1.4 网络设备与媒介层】
对应于实际的硬件设备,定义一组宏和一组访问设备内部寄存器的函数。
#define DATA_REG 0x0004
#define CMD_REG 0x0008
//寄存器读写函数
static u16 xxx_readword(u32 base_addr , int portno){
…… //读取寄存器值并返回
}
static void xxx_writeword(u32 base_addr , int portno , u16 value){
…… //向寄存器写入数值
}
********************************** 16.2 linux网络设备驱动的注册与注销 *******************************
【1】网络设备驱动的注册
int register_netdev(struct net_device *dev);
【2】网络设备驱动的注销
void unregister_netdev(struct net_device *dev);
【3】net_device结构体的生成和赋值
1、使用宏定义填充
#define alloc_netdev(sizeof_priv , name ,setup) alloc_netdev_mq(sizeof_priv, name ,setup , 1)
struct net_device *alloc_netdev_mq(int sizeof_priv , const char *name , void (*setup)(struct net_device *),
unsigned int queue_count);
参数解析
sizeof_priv :设备私有成员的大小
name:设备名
setup:struct net_device的setup函数指针,预置net_device成员的值;
queue_count:要分配的子队列的数量
2、针对以太网,使用宏定义填充
#define alloc_etherdev(sizeof_priv) alloc_etherdev_mq(sizeof_priv, 1)
Struct net_device *alloc_etherdev_mq(int sizeof_priv, unsigned int queue_count)
{
return alloc_netdev_mq(sizeof_priv, "eth%d" , ether_setup , queue_count);
}
**********************************16.3 网络设备的初始化 **************************************
【1】需要完成如下工作
1、检查网络设备是否存在,存在,检测设备所使用的硬件资源
2、分配net_device结构体并对其内部数据和函数指针赋值
3、获得设备的私有信息指针并初始化其各成员值
初始化函数模板 P371
**********************************16.4 网络设备的打开与释放 **********************************
【1】打开函数
xxx_open(struct net_device *dev)
在open函数内部完成如下工作
1、使能设备使用的硬件资源,申请I/O区域、中断和DMA通道等
2、调用linux内核提供的netif_start_queue(struct net_device *dev),激活设备发送队列
【2】关闭函数
xxx_release(struct net_device *dev)
在release函数内部完成如下工作
1、调用Linux内核提供的netif_stop_queue(struct net_device *dev), 停止设备传输包
2、释放设备所使用的I/O区域、中断和DMA资源
***********************************16.5 数据发送流程 ****************************************
int xxx_tx(struct sk_buff *skb , struct net_device *dev)
1、网络设备驱动程序从上层协议传递过来的sk_buff参数获得数据包的有效数据和长度,将有效数据放入临时缓冲区
2、对于以太网,如果有效数据的长度小于以太网冲突检测所要求数据帧的最小长度,则给临时缓冲区的末尾填充0;
3、设置硬件的寄存器,驱使网络设备惊醒数据发送操作
【特殊事件处理】
1、发送队列满或者其他原因导致执行netif_stop_queue() ;TX结束的中断处理中,应该调用netif_wake_queue()唤醒被阻塞的上层,启动继续发送;
2、当数据传输超时,xxx_tx_timeout()将被调用,处理完成之后,也要调用netif_wake_queue唤醒;
************************************ 16.6 数据接收流程 **************************************
*********************************** 16.7 网络连接状态 *****************************
【1】改变设备的连接状态
void netif_carrier_on(struct net_device *dev);
void netif_carrier_off(struct net_device *dev);
【2】检测链路上的载波信号是否存在
int netif_carrier_ok(struct net_device *dev);
****************************** 16.8 参数设置和统计数据 *****************************
【1】当用户调用ioctl()函数,并指定SIOCSIFHWADDR命令时,需要设置MAC地址
static int set_mac_address(struct net_device *dev , void *addr){
If(netif_running(dev)) //判断设备是否正在运行
return -EBUSY; //设备忙
xxx_set_mac(dev,addr); //设置以太网的MAC地址
return 0;
}
【2】当用户调用ioctl()函数时,并指定命令为SIOCSIFMAP,系统会调用驱动程序的set_config()函数;
int xxx_config(struct net_device *dev , struct ifmap * map){
if(netif_runing(dev)) //不能设置一个正在运行状态的设备
return -EBUSY;
/* 假设不允许改变I/O地址 */
if(map->base_addr != dev->base_addr){
printk(KERN_WARNING "xxx:Can't change I/O adress \n");
return -EOPNOTSUPP;
}
/* 假设允许改变IRQ*/
if(map->irq != dev->irq)
dev->irq = malp->irq;
Return 0;
}
【3】驱动程序还应提供get_stats()函数用以反馈设备的状态和统计信息
struct net_device_stats *xxx_stats(struct net_device *dev){
struct xxx_priv *priv = netdev_priv(dev);
return &priv->stats;
}
【】