5.4.2 Linux网络设备驱动功能的实现
实现Linux网络设备驱动功能主要有两种形式:
(1)通过内核来进行加载,当内核启动的时候,就开始加载网络设备驱动程序,内核启动完成之后,网络驱动功能也随即实现了。
(2)再就是通过模块加载的形式。比较两者,第二种形式更加灵活。
通过模块加载的过程如下:
(1)insmod把网络设备驱动程序插入到内核之中。
(2)insmod将调用init_module()函数。
(4)register_netdev()函数注册该网络设备。如果成功,网络设备初始化,将net_device数据结构插入到dev_base链表的末尾。
(5)模块卸载rmmod。
参见图5-5。
图5-5 网络设备驱动程序加载过程
net_device-〉init所指的函数用来完成如下工作:
(1)检查设备是否存在。
(2)资源分配。
(3)构造net_device,用检测到的数据值对net_device变量初始化。
(4)向Linux内核注册该设备并申请内存空间。
网络设备被激活时调用open(即设备状态由down--〉up)。所以实际上很多在initialize中的工作可以放到这里来做。比如资源的申请,硬件的激活。如果dev-〉open返回非0(error),则硬件的状态还是down。
open另一个作用是如果驱动程序作为一个模块被装入,则要防止模块卸载时设备处于打开状态。在open方法里要调用MOD_INC_USE_COUNT宏。
close释放资源,设备状态由up转为down时被调用的。作为模块装入的驱动程序,close里应该调用MOD_DEC_USE_COUNT,减少设备被引用的次数,以使驱动程序可以被卸载。close方法必须返回成功(0==success)。
数据包的发送过程如下:
(1)网络设备驱动加载时,网络设备的初始化函数(net_device-〉init)。
(2)打开网络设备 (net_device-〉open)。
(3)通过net_device-〉hard_header建立硬件包头函数指针。
(4)最后通过协议接口层函数dev_queue_xmit(详见/linux/net/core/dev.c)来调用net_device-〉hard_start_xmit,完成数据包的发送。
(5)如果发送成功,hard_start_xmit方法里释放sk_buff,返回0(发送成功)。
如果暂时无法处理,比如硬件忙,则返回1。这时如果dev-〉tbusy置为非0,则系统认为硬件忙,要等到dev-〉tbusy置0以后才会再次发送。tbusy的置0任务一般由中断完成。硬件在发送结束后产生中断,这时可以把tbusy置0,然后用mark_bh()调用通知系统可以再次发送。
驱动程序不存在一个接收方法。有数据收到应该是驱动程序来通知系统的。数据包的接收过程如下:
(1)设备收到数据后都会产生一个中断。
(2)中断处理程序中驱动程序申请一块sk_buff(skb),从硬件读出数据放置到申请好的缓冲区里。
(3)填充sk_buff中的相关信息。skb-〉dev = dev,判断收到帧的协议类型,填入skb-〉protocol(多协议的支持)。把指针skb-〉mac.raw指向硬件数据然后丢弃硬件帧头(skb_pull)。还要设置skb-〉pkt_type,标明第二层(链路层)数据类型,可以是以下类型:
PACKET_BROADCAST:链路层广播
PACKET_MULTICAST:链路层组播
PACKET_SELF:发给自己的帧
PACKET_OTHERHOST:发给别人的帧(监听模式时会有这种帧)
(4)最后调用netif_rx()把数据传送给协议层。
如果使用模块(module)方式加载驱动程序,需要在模块初始化时把设备注册到系统设备表里去。不再使用时,把设备从系统中卸除。定义在drivers/net/net_init.h里的两个函数完成这个工作。
int register_netdev(struct net_device *dev);
void unregister_netdev(struct net_device *dev);
dev:待注册的设备结构指针。注:net_device最重要的是name指针和init方法。name指针空(NULL)或者内容为空或者name[0]为空格(space),则系统把设备作为以太网设备处理。
register_netdev()返回0表示成功,非0不成功。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。