Linux 素来以其强大的网络功能著名, 同时, 网络设备也作为三大设备之一, 成为 Linux 驱动学习中必不可少的设备类型, 此外, 由于历史原因, Linux 并没有强制对网络设备贯彻其 "一切皆文件" 的思想, 网络设备不以 / dev 下的设备文件为接口,用户程序通过 socket 作为访问硬件的接口。本文以 Linux3.14.0 内核为例, 讨论 Linux 中的网络驱动模型
Linux 的网络设备并不使用文件作为用户程序访问网络设备的接口,所以 / sys/dev 下和 / dev 下并没有相应的网络设备文件,在 Linux 中,用户程序最终使用套接字来访问网络设备。
上图就是经典的 OSI 7 层模型,Linux 的网卡驱动程序处于 OSI 模型中的数据链路层,他的职责就是将上上层的协议栈传过来的信息通过网卡发送出去,\
Linux 的网络驱动模型采用 4 层结构:
第 2 and 第 3 层是驱动开发主要关心的层次
在分析核心对象与核心方法的时候, 找到在哪向上提供接口, 在哪向下操作硬件.
套接字缓冲区是数据在多层模型中传输的载体, 其被处理的最终结果就是网络数据包, Linux 巧妙的使用了移动 head/tail 指针的方式实现了网络模型中每一层对数据包的加工过程。sk_buff 部分定义如下
- 427 struct sk_buff {
- 428
- /* These two members must be first. */
- 429 struct sk_buff * next;
- 430 struct sk_buff * prev;
- 432 ktime_t tstamp;
- 434 struct sock * sk;
- 435 struct net_device * dev;
- 443 char cb[48] __aligned(8);
- 445 unsigned long _skb_refdst;
- 449 unsigned int len,
- 450 data_len;
- 451 __u16 mac_len,
- 452 hdr_len;
- 473 __be16 protocol;
- 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;
- 545 unsigned int truesize;
- 546 atomic_t users;
- 547
- };
net_device 是设备接口层的核心, 也是编写网络驱动核心的对象
- 1160 struct net_device {
- 1167 char name[IFNAMSIZ];
- 1179 unsigned long mem_end;
- /* shared mem end */
- 1180 unsigned long mem_start;
- /* shared mem start */
- 1181 unsigned long base_addr;
- /* device I/O address */
- 1182 int irq;
- /* device IRQ number */
- 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;
- 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
- };
下面是一些与 net_device 相关的内核 API
- //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);
通过形参的名字就可以看出, 这个函数其实不止分配了一个 net_device 对象的空间, 因为 net_device 中并没有一个存储私有数据的域 (dev->platform_data 除外), 关于 net_device 的私有数据的存储方式, 我们可以通过这个函数的定义中看出, 这也就是要使用内核 API 来分配一个 net_device 结构的原因
- //include/linux/netdevice.h
- 2897 #define alloc_netdev(sizeof_priv, name, setup) \
- 2898 alloc_netdev_mqs(sizeof_priv, name, setup, 1, 1)
- //net/core/dev.c
- 6308 struct net_device * alloc_netdev_mqs(int sizeof_priv, const char * name, 6309 void( * setup)(struct net_device * ), 6310 unsigned int txqs, unsigned int rxqs) 6311 {
- 6330 alloc_size = sizeof(struct net_device);
- 6331
- if (sizeof_priv) {
- 6332
- /* ensure 32-byte alignment of private area */
- 6333 alloc_size = ALIGN(alloc_size, NETDEV_ALIGN);
- 6334 alloc_size += sizeof_priv;
- 6335
- }
- 6336
- /* ensure 32-byte alignment of whole construct */
- 6337 alloc_size += NETDEV_ALIGN - 1;
- 6339 p = kzalloc(alloc_size, GFP_KERNEL | __GFP_NOWARN | __GFP_REPEAT);
- 6340
- if (!p) 6341 p = vzalloc(alloc_size);
- 6342
- if (!p) 6343
- return NULL;
- 6344 6345 dev = PTR_ALIGN(p, NETDEV_ALIGN);
- 6346 dev - >padded = (char * ) dev - (char * ) p;
- 6406
- }
- //以太网的初始化
- void ether_setup(struct net_device *dev);
这个函数也是一个重头, 在初始化一个以太网设备的时候应该被调用, 它的主要作用就是针对以太网标准对 net_device 对象进行初始化.
- //net/ethernet/eth.c
- 359 void ether_setup(struct net_device *dev)
- 360 {
- 361 dev->header_ops = ð_header_ops;
- 362 dev->type = ARPHRD_ETHER;
- 363 dev->hard_header_len = ETH_HLEN;
- 364 dev->mtu = ETH_DATA_LEN;
- 365 dev->addr_len = ETH_ALEN;
- 366 dev->tx_queue_len = 1000; /* Ethernet wants good queues */
- 367 dev->flags = IFF_BROADCAST|IFF_MULTICAST;
- 368 dev->priv_flags |= IFF_TX_SKB_SHARING;
- 369
- 370 memset(dev->broadcast, 0xFF, ETH_ALEN);
- 371
- 372 }
- //注册
- int register_netdev(struct net_device *dev);
- //注销
- void unregister_netdev(struct net_device *dev);
- 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);
- 1006 int (*ndo_stop)(struct net_device *dev);
- 1007 netdev_tx_t (*ndo_start_xmit) (struct sk_buff *skb,
- 1008 struct net_device *dev);
- 1013 void (*ndo_change_rx_flags)(struct net_device *dev,
- 1014 int flags);
- 1015 void (*ndo_set_rx_mode)(struct net_device *dev);
- 1016 int (*ndo_set_mac_address)(struct net_device *dev,
- 1017 void *addr);
- 1018 int (*ndo_validate_addr)(struct net_device *dev);
- 1019 int (*ndo_do_ioctl)(struct net_device *dev,
- 1020 struct ifreq *ifr, int cmd);
- 1021 int (*ndo_set_config)(struct net_device *dev,
- 1022 struct ifmap *map);
- 1023 int (*ndo_change_mtu)(struct net_device *dev,
- 1024 int new_mtu);
- 1025 int (*ndo_neigh_setup)(struct net_device *dev,
- 1026 struct neigh_parms *);
- 1027 void (*ndo_tx_timeout) (struct net_device *dev);
- 1028
- 1148 };
下面是一个可以借鉴的 ndo_init() 的实现
- void xxx_init(struct net_device *dev)
- {
- /* 设备的私有信息结构体 */
- struct xxx_priv *priv;
- /* 检查设备是否存在, 以及设备需要的硬件资源 */
- xxx_hw_init();
- /* 初始化以太网设备的公用成员 */
- ether_setup(dev);
- /* 设置设备的成员函数指针 */
- dev->netdev_ops->ndo_open = xxx_open;
- dev->netdev_ops->ndo_stop = xxx_stop;
- dev->netdev_ops->ndo_set_config = xxx_set_config;
- dev->netdev_ops->ndo_start_xmit = xxx_tx;
- dev->netdev_ops->ndo_do_ioctl = xxx_ioctl;
- dev->netdev_ops->ndo_get_stats = xxx_stats;
- dev->netdev_ops->ndo_change_mtu = xxx_change_mtu;
- dev->netdev_ops->ndo_tx_timeout = xxx_tx_timeout;
- dev->netdev_ops->ndo_watchdog_timeo = xxx_timeout;
- dev->rebuild_header = xxx_rebuild_header;
- dev->hard_header = xxx_header;
- /* 获得私有数据并将其初始化 */
- priv = netdev_priv(dev);
- /* 初始化priv代码 */
- }
- int xxx_open(struct net_device *dev)
- {
- /* 申请资源 */
- ret = request_irq(dev->irq, &xxx_interrupt, 0, dev->name, dev);
- /* 激活设备的发送队列 */
- netif_start_queue(dev);
- }
- init xxx_release(struct net_device *dev)
- {
- /* 释放资源 */
- free_irq(dev->irq,dev);
- /* 关闭设备的发送队列 */
- netif_stop_queue(dev);
- }
- int xxx_tx(struct sk_buff *skb, struct net_device *dev)
- {
- int len;
- char *data, shortpkt[ETH_ZLEN];
- if(xxx_send_available(...)){ //发送队列未满, 可以发送
- /* 获得有效数据指针和长度 */
- data = skb->data;
- len = skb->len;
- if(len < ETH_ZLEN){
- /* 如果帧长小于以太网帧最小长度,补0 */
- memset(shortpkt,0,ETH_ZLEN);
- memcpy(shortpkt,skb->data,skb->len);
- len = ETH_ZLEN;
- data = shortpkt;
- }
- dev->trans_start = jiffies; //记录发送时间戳
- /* 设置硬件寄存器让硬件将数据发出去 */
- xxx_hw_tx(data,len,dev);
- }else{
- netif_stop_queue(dev);
- ...
- }
- }
这个函数会在超时的时候被调用, 通常用来实现重发.
- void xxx_tx_timeout(struct net_device *dev)
- {
- ...
- netif_wake_queue(dev); //重新启动设备发送队列
- }
是网络设备媒介层相设备驱动功能层发送数据的接口, 网卡接收到数据是通过中断的方式上报的, 所以网络驱动中的中断处理函数就是第一时间队接收到的数据进行处理的地方, 这个函数最终一定要调用 netif_rx() 将收到的数据上报到协议接口层. 下面是一个简单的接收数据中断处理函数模板
- static void xxx_rx(struct xxx_device * dev)
- {
- ...
- length = get_rev_len(...);
- /* 分配新的套接字缓冲区 */
- skb = dev_alloc_skb(length +2);
- skb_researve(skb, 2); //对齐
- skb->dev = dev;
- /* 读取硬件上接收到的数据 */
- insw(ioaddr +RX_FRAME_PORT, skb_put(skb, length), length >>1);
- if(length &1){
- skb ->data[length - 1] = inw(ioaddr + RX_FRAME_PORT);
- }
- /* 获取上层协议类型 */
- skb->protocol = eth_type_trans(skb,dev);
- /* 把数据包交给上层 */
- netif_rx(skb);
- /* 记录接收时间戳 */
- dev->last_rx = jiffies;
- ...
- }
- static void xxx_interrupt(int irq, void *dev_id)
- {
- ...
- switch(status & ISQ_EVENT_MASK){
- case ISQ_RECEIVER_EVENT: /* 获取数据包 */
- xxx_rx(dev);
- break;
- /* 其他类型中断 */
- }
- }
这些 API 都在 "include/linux/netdevice.h" 中声明了
- 得到私有数据指针
- void *netdev_priv(const struct net_device *dev);
- 设置MAC
- int eth_mac_addr(struct net_device *dev, void *p);
- 检查MAC地址是否有效
- int eth_validate_addr(struct net_device *dev);
- 修改MTU值
- int eth_change_mtu(struct net_device *dev, int new_mtu);
- //随机生成mac地址
- void eth_hw_addr_random(struct net_device *dev)
- void eth_random_addr(u8 *addr);
- //开启发送队列
- void netif_start_queue(struct net_device *dev)
- //停止发送队列
- void netif_stop_queue(struct net_device *dev)
- //runing
- void netif_carrier_on(struct net_device *dev)
- //not runing
- void netif_carrier_off(struct net_device *dev)
来源: http://www.cnblogs.com/xiaojiang1025/p/6486267.html