网络驱动移植之解析Linux网络驱动的基本框架
网络驱动移植之解析Linux网络驱动的基本框架
内核源码:linux-2.6.38.8.tar.bz2概括而言,编写Linux网络驱动其实只要完成两件事即可,一是分配并初始化网络设备,二是注册网络设备。
1、分配并初始化网络设备
动态分配网络设备(从C语言角度来看,其实就是定义了一个struct net_device结构体变量,并对这个结构体变量的某些成员进行了初始化而已)及其私有数据的大致过程如下图(以以太网设备为例):
下面将结合linux-2.6.38.8中的代码详细分析网络设备的分配和初始化过程。
- /* linux-2.6.38.8/include/linux/etherdevice.h */
- #define alloc_etherdev(sizeof_priv) alloc_etherdev_mq(sizeof_priv, 1)
- #define alloc_etherdev_mq(sizeof_priv, count) alloc_etherdev_mqs(sizeof_priv, count, count)
- /* linux-2.6.38.8/net/ethernet/eth.c */
- struct net_device *alloc_etherdev_mqs(int sizeof_priv, unsigned int txqs,
- unsigned int rxqs)
- {
- return alloc_netdev_mqs(sizeof_priv, "eth%d", ether_setup, txqs, rxqs);
- }
- void ether_setup(struct net_device *dev)
- {
- dev->header_ops = ð_header_ops;
- dev->type = ARPHRD_ETHER;
- dev->hard_header_len = ETH_HLEN;
- dev->mtu = ETH_DATA_LEN;
- dev->addr_len = ETH_ALEN;
- dev->tx_queue_len = 1000; /* Ethernet wants good queues */
- dev->flags = IFF_BROADCAST|IFF_MULTICAST;
- memset(dev->broadcast, 0xFF, ETH_ALEN);
- }
以前各类网络设备的分配函数(如以太网设备的alloc_etherdev)都只是alloc_netdev函数的封装而已,但对于linux-2.6.38.8而言已经不是这样了。
- /* linux-2.6.38.8/include/linux/netdevice.h */
- #define alloc_netdev(sizeof_priv, name, setup) \
- alloc_netdev_mqs(sizeof_priv, name, setup, 1, 1)
alloc_netdev_mqs函数的五个参数分别为私有数据大小、设备名称、默认初始化函数、发送队列数目和接收队列数目。
以太网设备的名称设为eth%d,默认初始化函数设为ether_setup,发送和接收队列数目都设为1。
函数alloc_netdev_mqs定义在linux-2.6.38.8/net/core/dev.c文件中,大概会完成以下各种操作:
(1)、为struct net_device和私有数据分配内存空间
- alloc_size = sizeof(struct net_device);
- if (sizeof_priv) {
- alloc_size = ALIGN(alloc_size, NETDEV_ALIGN); //#define NETDEV_ALIGN 32
- alloc_size += sizeof_priv;
- }
- alloc_size += NETDEV_ALIGN - 1;
- p = kzalloc(alloc_size, GFP_KERNEL);
- if (!p) {
- printk(KERN_ERR "alloc_netdev: Unable to allocate device.\n");
- return NULL;
- }
- dev = PTR_ALIGN(p, NETDEV_ALIGN);
- dev->padded = (char *)dev - (char *)p;
对齐操作相关宏:
- /* linux-2.6.38.8/include/linux/kernel.h */
- #define ALIGN(x, a) __ALIGN_KERNEL((x), (a))
- #define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)
- #define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask))
- #define PTR_ALIGN(p, a) ((typeof(p))ALIGN((unsigned long)(p), (a)))
(2)、动态分配per-CPU变量
- dev->pcpu_refcnt = alloc_percpu(int);
- if (!dev->pcpu_refcnt)
- goto free_p;
(3)、初始化硬件地址链表dev->dev_addrs,并把首元素赋给dev->dev_addr
- if (dev_addr_init(dev))
- goto free_pcpu;
(4)、初始化组播和单播地址链表
- dev_mc_init(dev);
- dev_uc_init(dev);
(5)、设置网络命名空间
- dev_net_set(dev, &init_net);
(6)、设置GSO最大值
- dev->gso_max_size = GSO_MAX_SIZE;
(7)、初始化各种链表
- INIT_LIST_HEAD(&dev->ethtool_ntuple_list.list);
- dev->ethtool_ntuple_list.count = 0;
- INIT_LIST_HEAD(&dev->napi_list);
- INIT_LIST_HEAD(&dev->unreg_list);
- INIT_LIST_HEAD(&dev->link_watch_list);
(8)、设置priv_flags值
- dev->priv_flags = IFF_XMIT_DST_RELEASE;
(9)、执行默认初始化函数(以太网设备默认为ether_setup)
- setup(dev);
(10)、初始化数据包发送队列
- dev->num_tx_queues = txqs;
- dev->real_num_tx_queues = txqs;
- if (netif_alloc_netdev_queues(dev))
- goto free_all;
(11)、初始化数据包接收队列
- dev->num_rx_queues = rxqs;
- dev->real_num_rx_queues = rxqs;
- if (netif_alloc_rx_queues(dev))
- goto free_all;
(12)、设置网络设备名称
- strcpy(dev->name, name);
|
评论暂时关闭