/************************************************************************************
*本文为个人学习记录,如有错误,欢迎指正。
* http://www.cnblogs.com/xiaojiang1025/archive/2017/03/28/6486267.html
* https://blog.csdn.net/zdy0_2004/article/details/79234386
************************************************************************************/
1. 网络设备驱动框架总体简介
Linux网络设备驱动程序体系结构分为四层:网络协议接口层、网络设备接口层、提供实际功能的设备驱动层以及网络设备与媒介层。
(1)网络协议接口层
网络协议接口层向网络层协议提供统一的数据包收发接口,不论上层协议是ARP还是IP,都通过dev_queue_xmit()函数发送数据,并通过netif_rx()函数接收数据。这一层的存在使得上层协议独立于具体的设备。
(2)网络设备接口层
网络设备接口层向协议接口层提供的用于描述具体网络设备属性和操作的结构体net_device,该结构体是设备驱动功能层各函数的容器。
(3)设备驱动功能层
设备驱动功能层的各函数是网络设备接口层net_device数据结构的具体成员,是驱使网络设备硬件完成相应动作的程序,它通过nto_start_xmit()函数启动发送操作,并通过网络设备上的中断触发接收操作。
(4)网络设备与媒介层
网络设备与媒介层完成数据包发送和接收的物理实体,包括网络适配器和具体的传输媒介,网络适配器被设备驱动功能层中的函数在物理上驱动。
驱动工程师的工作:在设计具体的网络设备驱动程序时,需要完成的主要工作是编写设备驱动功能层的相关函数以填充net_device数据结构的内容并将net_device注册入内核。
2. 相关数据结构
(1)struct sk_buff
sk_buff是网络驱动框架中信息的载体, 是网络分层模型中对数据进行层层打包以及层层解包的载体。
427 struct sk_buff { 428 /* These two members must be first. */ 429 struct sk_buff *next; //sk_buff是双向链表,所以有前去后继,这是指向后面的sk_buff结构体指针 430 struct sk_buff *prev; //这是指向前一个sk_buff结构体指针 432 ktime_t tstamp; 434 struct sock *sk; 435 struct net_device *dev; //对应的net_device 443 char cb[48] __aligned(8); 445 unsigned long _skb_refdst; 449 unsigned int len, //表示数据区的长度(tail-data)与分片结构体数据区的长度之和 450 data_len;//只表示分片结构体数据区的长度,所以len=(tail - data) + data_len 451 __u16 mac_len, //mac报头的长度 452 hdr_len; 473 __be16 protocol;//包的协议类型,标识是IP包还是ARP包还是其他数据包 534 __u16 inner_transport_header; 535 __u16 inner_network_header; 536 __u16 inner_mac_header; 537 __u16 transport_header; //指向传输包头 538 __u16 network_header; //指向传输层包头 539 __u16 mac_header; //指向链路层包头 540 /* These elements must be at the end, see alloc_skb() for details. */ 541 sk_buff_data_t tail; //指向当前数据包的尾地址, 随着各个网络层的加工而变化 542 sk_buff_data_t end; //数据缓冲区的结束地址 543 unsigned char *head, //数据缓冲区的开始地址 544 *data; //data指向当前数据包的首地址, 随着各个网路层的加工而变化 545 unsigned int truesize; 546 atomic_t users; 547 };
(2)struct net_device
Linux内核中使用 net_device 来描述一个网络设备,net_device是设备接口层的核心, 也是编写网络驱动核心的对象。
1160 struct net_device { 1167 char name[IFNAMSIZ];//网络设备的名称, 网络设备被载入后会出现在ifconfig中, 比如默认的eth0就是这个 1179 unsigned long mem_end; //网络设备所使用的共享内存起始地址 1180 unsigned long mem_start; //网络设备所使用的共享内存结束地址 1181 unsigned long base_addr; //表示网络设备的IO基地址 1182 int irq; //设备使用的中断号 1189 unsigned long state; 1190 1191 struct list_head dev_list; 1192 struct list_head napi_list; 1193 struct list_head unreg_list; 1194 struct list_head close_list; 1210 netdev_features_t features; //用户层可以修改的特征 1212 netdev_features_t hw_features; //用户层不能修改的特征 1214 netdev_features_t wanted_features; 1243 const struct net_device_ops *netdev_ops;//网络设备的操作方法集 1244 const struct ethtool_ops *ethtool_ops; //ethtool的方法集 1245 const struct forwarding_accel_ops *fwd_ops; 1248 const struct header_ops *header_ops; //协议头操作集 1250 unsigned int flags; /* interface flags (a la BSD) */ 1251 unsigned int priv_flags; /* Like 'flags' but invisible to userspace. 1252 * See if.h for definitions. */ 1253 unsigned short gflags; 1254 unsigned short padded; /* How much padding added by alloc_netdev() */ 1256 unsigned char operstate; /* RFC2863 operstate */ 1257 unsigned char link_mode; /* mapping policy to operstate */ 1259 unsigned char if_port; /* Selectable AUI, TP,..*/ 1260 unsigned char dma; /* DMA channel */ 1262 unsigned int mtu; /* interface MTU value */ 1263 unsigned short type; /* interface hardware type */ 1264 unsigned short hard_header_len; /* hardware hdr length */ 1270 unsigned short needed_headroom; 1271 unsigned short needed_tailroom; 1274 unsigned char perm_addr[MAX_ADDR_LEN]; /* permanent hw address */ 1275 unsigned char addr_assign_type; /* hw address assignment type */ 1276 unsigned char addr_len; /* hardware address length */ 1289 struct kset *queues_kset; 1386 int watchdog_timeo; /* used by dev_watchdog() */ 1480 };
(3)struct net_device_ops
网络设备的操作方法集。
1002 struct net_device_ops { 1003 int (*ndo_init)(struct net_device *dev); 1004 void (*ndo_uninit)(struct net_device *dev); 1005 int (*ndo_open)(struct net_device *dev);//打开网络接口设备,获得设备需要的I/O地址、IRQ、DMA通道等 1006 int (*ndo_stop)(struct net_device *dev);//停止网络接口设备,与open()函数的作用相反 1007 netdev_tx_t (*ndo_start_xmit) (struct sk_buff *skb,struct net_device *dev);//启动数据包的发送,当系统调用驱动程序的xmit函数时,需要向其传入一个sk_buff结构体指针,以使得驱动程序能获取从上层传递下来的数据包 1013 void (*ndo_change_rx_flags)(struct net_device *dev,int flags); 1015 void (*ndo_set_rx_mode)(struct net_device *dev); 1016 int (*ndo_set_mac_address)(struct net_device *dev,void *addr);//用于设置设备的MAC地址 1018 int (*ndo_validate_addr)(struct net_device *dev); 1019 int (*ndo_do_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd);//用于进行设备特定的I/O控制 1021 int (*ndo_set_config)(struct net_device *dev, struct ifmap *map); //用于配置接口,也可用于改变设备的I/O地址和中断号 1023 int (*ndo_change_mtu)(struct net_device *dev, int new_mtu); 1025 int (*ndo_neigh_setup)(struct net_device *dev,struct neigh_parms *); 1027 void (*ndo_tx_timeout) (struct net_device *dev);//当数据包的发送超时时,ndo_tx_timeout()函数会被调用,该函数需采取重新启动数据包发送过程或重新启动硬件等措施来恢复网络设备到正常状态 1028 1148 };
3. 相关API
(1)分配/释放net_device
//linux/etherdevice.h /** * 分配及初始化net_device对象() * @sizeof_priv - 私有数据大小(单位:字节数) * 返回值:失败:NULL, 成功:net_device对象的首地址 */ struct net_device *alloc_etherdev(int sizeof_priv); //linux/netdevice.h /** * 分配及初始化net_device对象 * @int sizeof_priv - 私有数据大小(单位:字节数) * @const char *name - 物理接口名("名称%d") * @unsigned char name_assign_type - NET_NAME_UNKNOWN * @void (*setup)(struct net_device *) - 初始化函数 * 返回值:失败:NULL成功:net_device对象的首地址 */ struct net_device *alloc_netdev(int sizeof_priv, const char *name,unsigned char name_assign_type,void (*setup)(struct net_device *)); //释放 void free_netdev(struct net_device *dev);
(2)以太网的初始化
在初始化一个以太网设备的时候应该被调用, 它的主要作用就是针对以太网标准对net_device对象进行初始化。
void ether_setup(struct net_device *dev);
(3)注册/注销net_device
//注册 int register_netdev(struct net_device *dev); //注销 void unregister_netdev(struct net_device *dev);
(4)开始/停止发送队列
//开启发送队列 void netif_start_queue(struct net_device *dev) //停止发送队列 void netif_stop_queue(struct net_device *dev)
4. 网络设备的中断处理函数
中断处理函数
是网络设备媒介层相设备驱动功能层发送数据的接口, 网卡接收到数据是通过中断的方式上报的, 所以网络驱动中的中断处理函数就是第一时间队接收到的数据进行处理的地方,这个函数最终一定要调用netif_rx()将收到的数据上报到协议接口层。
5. 网络设备驱动实例
转载一篇DM9000驱动分析的文章,写得非常详细,感谢作者的贡献。 Linux DM9000网卡驱动程序完全分析