网络驱动移植之解析Linux网络驱动的基本框架
来源:互联网 发布:美工教学视频基础知识 编辑:程序博客网 时间:2024/04/30 14:37
内核源码: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 32alloc_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);
2、注册网络设备
通过register_netdev函数把已完成部分初始化的net_device结构体变量(即某个网络设备实例)注册到Linux内核中,大致过程如下图:
下面将结合linux-2.6.38.8中的代码详细分析网络设备的注册过程。
(1)、获得rtnl信号量
rtnl_lock();
(2)、分配网络设备名(即%d对应的数字)
if (strchr(dev->name, '%')) {err = dev_alloc_name(dev, dev->name);if (err < 0)goto out;}
(3)、调用实际注册函数
err = register_netdevice(dev);
3.1、初始化dev->addr_list_lock自旋锁并根据dev->type设置其类别
spin_lock_init(&dev->addr_list_lock);netdev_set_addr_lockdep_class(dev);
3.2、调用init函数
if (dev->netdev_ops->ndo_init) {ret = dev->netdev_ops->ndo_init(dev);if (ret) {if (ret > 0)ret = -EIO;goto out;}}
3.3、检测网络设备名是否有效
ret = dev_get_valid_name(dev, dev->name, 0);if (ret)goto err_uninit;
3.4、为网络设备分配唯一的索引号
dev->ifindex = dev_new_index(net);if (dev->iflink == -1)dev->iflink = dev->ifindex;
3.5、设置网络设备特性(dev->features)
if ((dev->features & NETIF_F_HW_CSUM) && (dev->features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) {printk(KERN_NOTICE "%s: mixed HW and IP checksum settings.\n", dev->name);dev->features &= ~(NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM);}if ((dev->features & NETIF_F_NO_CSUM) && (dev->features & (NETIF_F_HW_CSUM|NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) {printk(KERN_NOTICE "%s: mixed no checksumming and other settings.\n", dev->name);dev->features &= ~(NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM|NETIF_F_HW_CSUM);}dev->features = netdev_fix_features(dev->features, dev->name);if (dev->features & NETIF_F_SG)dev->features |= NETIF_F_GSO;dev->vlan_features |= (NETIF_F_GRO | NETIF_F_HIGHDMA);
3.6、通过通知链告知内核其他子系统某种事件的发生(如注册网络设备)
ret = call_netdevice_notifiers(NETDEV_POST_INIT, dev);ret = notifier_to_errno(ret);if (ret)goto err_uninit;ret = call_netdevice_notifiers(NETDEV_REGISTER, dev);ret = notifier_to_errno(ret);if (ret) {rollback_registered(dev);dev->reg_state = NETREG_UNREGISTERED;}
3.7、创建网络设备在sysfs文件系统中的入口
ret = netdev_register_kobject(dev);if (ret)goto err_uninit;
3.8、设置网络设备为已注册状态
dev->reg_state = NETREG_REGISTERED;
3.9、设置网络设备状态为可用
set_bit(__LINK_STATE_PRESENT, &dev->state);
3.10、初始化网络设备的队列规则
dev_init_scheduler(dev);
3.11、增加网络设备的引用计数
dev_hold(dev);
3.12、加入到设备链表(如dev->dev_list、dev->name_hlist、dev->index_hlist)
list_netdevice(dev);
3.13、发送netlink(RFC 3549协议)信息
if (!dev->rtnl_link_ops || dev->rtnl_link_state == RTNL_LINK_INITIALIZED)rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U);
- 网络驱动移植之解析Linux网络驱动的基本框架
- 网络驱动移植之解析Linux网络驱动的基本框架
- 网络驱动移植之解析Linux网络驱动的基本框架
- Linux网络驱动框架
- Linux网络协议栈之驱动框架
- 网络驱动移植之简述CS8900A网络芯片的基本原理
- 网络驱动移植之简述CS8900A网络芯片的基本原理
- Zynq-Linux移植学习笔记之八-linux网络驱动
- Zynq-Linux移植学习笔记之七-网络驱动
- Zynq-Linux移植学习笔记之七-网络驱动
- 【Linux基础】网络接口驱动移植
- uboot移植之网络驱动移植--移植理论知识--7.29
- uboot移植之网络驱动移植--移植操作--7.29
- 网络驱动移植之网络子系统设备处理层的初始化
- Linux驱动框架解析
- Linux 网络驱动详解
- Linux 网络驱动详解
- linux网络驱动体系结构
- debug
- AIX-HACMP群集系统安装详细步骤
- 做人不要太成熟
- Chrome 离线安装包下载地址
- @awk判断输出----一些不常见的用法@
- 网络驱动移植之解析Linux网络驱动的基本框架
- Freemarker list 对象取前几条数据
- 加强自我管理
- Javascript的时间方法的内部机制
- 代码妹 从这里开始
- android获取本地音乐的专辑的图片
- SQL Error:library routine called out of sequence.
- 网络驱动移植之简述CS8900A网络芯片的基本原理
- Android手机铃声代码实现