如何编写网卡的驱动程序

如何编写网卡的驱动程序,第1张

Linux *** 作系统网络驱动程序编写

一.Linux系统设备驱动程序概述

1.1 Linux设备驱动程序分类

1.2 编写驱动程序的一些基本概念

二.Linux系统网络设备驱动程序

2.1 网络驱动程序的结构

2.2 网络驱动程序的基本方法

2.3 网络驱动程序中用到的数据结构

2.4 常用的系统支持

三.编写Linux网络驱动程序中可能遇到的问题

3.1 中断共享

3.2 硬件发送忙时的处理

3.3 流量控制(flow control)

3.4 调试

四.进一步的阅读

五.杂项

一.Linux系统设备驱动程序概述

1.1 Linux设备驱动程序分类

Linux设备驱动程序在Linux的内核源代码中占有很大的比例,源代码的长度日

益增加,主要是驱动程序的增加。在Linux内核的不断升级过程中,驱动程序的结构

还是相对稳定。在2.0.xx到2.2.xx的变动里,驱动程序的编写做了一些改变,但是

从2.0.xx的驱动到2.2.xx的移植只需做少量的工作。

Linux系统的设备分为字符设备(char device),块设备(block device)和网络

设备(network device)三种。字符设备是指存取时没有缓存的设备。块设备的读写

都有缓存来支持,并且块设备必须能够随机存取(random access),字符设备则没有

这个要求。典型的字符设备包括鼠标,键盘,串行口等。块设备主要包括硬盘软盘

设备,CD-ROM等。一个文件系统要安装进入 *** 作系统必须在块设备上。

网络设备在Linux里做专门的处理。Linux的网络系统主要是基于BSD unix的socket

机制。在系统和驱动程序之间定义有专门的数据结构(sk_buff)进行数据的传递。系

统里支持对发送数据和接收数据的缓存,提供流量控制机制,提供对多协议的支持。

1.2 编写驱动程序的一些基本概念

无论是什么 *** 作系统的驱动程序,都有一些通用的概念。 *** 作系统提供给驱动

程序的支持也大致相同。下面简单介绍一下网络设备驱动程序的一些基本要求。

1.2.1 发送和接收

这是一个网络设备最基本的功能。一块网卡所做的无非就是收发工作。所以驱

动程序里要告诉系统你的发送函数在哪里,系统在有数据要发送时就会调用你的发

送程序。还有驱动程序由于是直接 *** 纵硬件的,所以网络硬件有数据收到最先能得

到这个数据的也就是驱动程序,它负责把这些原始数据进行必要的处理然后送给系

统。这里, *** 作系统必须要提供两个机制,一个是找到驱动程序的发送函数,一个

是驱动程序把收到的数据送给系统。

1.2.2 中断

中断在现代计算机结构中有重要的地位。 *** 作系统必须提供驱动程序响应中断

的能力。一般是把一个中断处理程序注册到系统中去。 *** 作系统在硬件中断发生后

调用驱动程序的处理程序。Linux支持中断的共享,即多个设备共享一个中断。

1.2.3 时钟

在实现驱动程序时,很多地方会用到时钟。如某些协议里的超时处理,没有中

断机制的硬件的轮询等。 *** 作系统应为驱动程序提供定时机制。一般是在预定的时

间过了以后回调注册的时钟函数。在网络驱动程序中,如果硬件没有中断功能,定

时器可以提供轮询(poll)方式对硬件进行存取。或者是实现某些协议时需要的超时

重传等。

二.Linux系统网络设备驱动程序

2.1 网络驱动程序的结构

所有的Linux网络驱动程序遵循通用的接口。设计时采用的是面向对象的方法。

一个设备就是一个对象(device 结构),它内部有自己的数据和方法。每一个设备的

方法被调用时的第一个参数都是这个设备对象本身。这样这个方法就可以存取自身

的数据(类似面向对象程序设计时的this引用)。

一个网络设备最基本的方法有初始化、发送和接收。

------------------- ---------------------

|deliver packets | |receive packets queue|

|(dev_queue_xmit()) | |them(netif_rx()) |

------------------- ---------------------

| | /

/ | |

-------------------------------------------------------

| methods and variables(initialize,open,close,hard_xmit,|

| interrupt handler,config,resources,status...) |

-------------------------------------------------------

| | /

/ | |

----------------- ----------------------

|send to hardware | |receivce from hardware|

----------------- ----------------------

| | /

/ | |

-----------------------------------------------------

| hardware media |

-----------------------------------------------------

初始化程序完成硬件的初始化、device中变量的初始化和系统资源的申请。发送

程序是在驱动程序的上层协议层有数据要发送时自动调用的。一般驱动程序中不对发

送数据进行缓存,而是直接使用硬件的发送功能把数据发送出去。接收数据一般是通

过硬件中断来通知的。在中断处理程序里,把硬件帧信息填入一个skbuff结构中,然

------------------ Linux *** 作系统网络驱动程序编写 -------------------

------------ Contact the author by mailto:bordi@bordi.dhs.org ------

后调用netif_rx()传递给上层处理。

2.2 网络驱动程序的基本方法

网络设备做为一个对象,提供一些方法供系统访问。正是这些有统一接口的方法,

掩蔽了硬件的具体细节,让系统对各种网络设备的访问都采用统一的形式,做到硬件

无关性。

下面解释最基本的方法。

2.2.1 初始化(initialize)

驱动程序必须有一个初始化方法。在把驱动程序载入系统的时候会调用这个初

始化程序。它做以下几方面的工作。检测设备。在初始化程序里你可以根据硬件的

特征检查硬件是否存在,然后决定是否启动这个驱动程序。配置和初始化硬件。在

初始化程序里你可以完成对硬件资源的配置,比如即插即用的硬件就可以在这个时

候进行配置(Linux内核对PnP功能没有很好的支持,可以在驱动程序里完成这个功

能)。配置或协商好硬件占用的资源以后,就可以向系统申请这些资源。有些资源是

可以和别的设备共享的,如中断。有些是不能共享的,如IO、DMA。接下来你要初始

化device结构中的变量。最后,你可以让硬件正式开始工作。

2.2.2 打开(open)

open这个方法在网络设备驱动程序里是网络设备被激活的时候被调用(即设备状

态由down-->up)。所以实际上很多在initialize中的工作可以放到这里来做。比如资

源的申请,硬件的激活。如果dev->open返回非0(error),则硬件的状态还是down。

open方法另一个作用是如果驱动程序做为一个模块被装入,则要防止模块卸载时

设备处于打开状态。在open方法里要调用MOD_INC_USE_COUNT宏。

2.2.3 关闭(stop)

close方法做和open相反的工作。可以释放某些资源以减少系统负担。close是在

设备状态由up转为down时被调用的。另外如果是做为模块装入的驱动程序,close里

应该调用MOD_DEC_USE_COUNT,减少设备被引用的次数,以使驱动程序可以被卸载。

另外close方法必须返回成功(0==success)。

2.2.4 发送(hard_start_xmit)

所有的网络设备驱动程序都必须有这个发送方法。在系统调用驱动程序的xmit

时,发送的数据放在一个sk_buff结构中。一般的驱动程序把数据传给硬件发出去。

也有一些特殊的设备比如loopback把数据组成一个接收数据再回送给系统,或者

dummy设备直接丢弃数据。

如果发送成功,hard_start_xmit方法里释放sk_buff,返回0(发送成功)。如果

设备暂时无法处理,比如硬件忙,则返回1。这时如果dev->tbusy置为非0,则系统

认为硬件忙,要等到dev->tbusy置0以后才会再次发送。tbusy的置0任务一般由中断

完成。硬件在发送结束后产生中断,这时可以把tbusy置0,然后用mark_bh()调用通

知系统可以再次发送。在发送不成功的情况下,也可以不置dev->tbusy为非0,这样

系统会不断尝试重发。如果hard_start_xmit发送不成功,则不要释放sk_buff。

传送下来的sk_buff中的数据已经包含硬件需要的帧头。所以在发送方法里不需

要再填充硬件帧头,数据可以直接提交给硬件发送。sk_buff是被锁住的(locked),

确保其他程序不会存取它。

2.2.5 接收(reception)

驱动程序并不存在一个接收方法。有数据收到应该是驱动程序来通知系统的。

一般设备收到数据后都会产生一个中断,在中断处理程序中驱动程序申请一块

sk_buff(skb),从硬件读出数据放置到申请好的缓冲区里。接下来填充sk_buff中

的一些信息。skb->dev = dev,判断收到帧的协议类型,填入skb->protocol(多协

议的支持)。把指针skb->mac.raw指向硬件数据然后丢弃硬件帧头(skb_pull)。还要

设置skb->pkt_type,标明第二层(链路层)数据类型。可以是以下类型:

PACKET_BROADCAST : 链路层广播

PACKET_MULTICAST : 链路层组播

PACKET_SELF : 发给自己的帧

PACKET_OTHERHOST : 发给别人的帧(监听模式时会有这种帧)

最后调用netif_rx()把数据传送给协议层。netif_rx()里数据放入处理队列然后返

回,真正的处理是在中断返回以后,这样可以减少中断时间。调用netif_rx()以后,

驱动程序就不能再存取数据缓冲区skb。

2.2.6 硬件帧头(hard_header)

硬件一般都会在上层数据发送之前加上自己的硬件帧头,比如以太网(Ethernet)

就有14字节的帧头。这个帧头是加在上层ip、ipx等数据包的前面的。驱动程序提供

一个hard_header方法,协议层(ip、ipx、arp等)在发送数据之前会调用这段程序。

硬件帧头的长度必须填在dev->hard_header_len,这样协议层回在数据之前保留好

硬件帧头的空间。这样hard_header程序只要调用skb_push然后正确填入硬件帧头就

可以了。

在协议层调用hard_header时,传送的参数包括(2.0.xx):数据的sk_buff,

device指针,protocol,目的地址(daddr),源地址(saddr),数据长度(len)。数据

长度不要使用sk_buff中的参数,因为调用hard_header时数据可能还没完全组织好。

saddr是NULL的话是使用缺省地址(default)。daddr是NULL表明协议层不知道硬件目

的地址。如果hard_header完全填好了硬件帧头,则返回添加的字节数。如果硬件帧

头中的信息还不完全(比如daddr为NULL,但是帧头中需要目的硬件地址。典型的情

况是以太网需要地址解析(arp)),则返回负字节数。hard_header返回负数的情况

下,协议层会做进一步的build header的工作。目前Linux系统里就是做arp

(如果hard_header返回正,dev->arp=1,表明不需要做arp,返回负,dev->arp=0,

做arp)。

对hard_header的调用在每个协议层的处理程序里。如ip_output。

2.2.7 地址解析(xarp)

有些网络有硬件地址(比如Ethernet),并且在发送硬件帧时需要知道目的硬件

地址。这样就需要上层协议地址(ip、ipx)和硬件地址的对应。这个对应是通过地址

解析完成的。需要做arp的的设备在发送之前会调用驱动程序的rebuild_header方

法。调用的主要参数包括指向硬件帧头的指针,协议层地址。如果驱动程序能够解

析硬件地址,就返回1,如果不能,返回0。

对rebuild_header的调用在net/core/dev.c的do_dev_queue_xmit()里。

2.2.8 参数设置和统计数据

在驱动程序里还提供一些方法供系统对设备的参数进行设置和读取信息。一般

只有超级用户(root)权限才能对设备参数进行设置。设置方法有:

dev->set_mac_address()

当用户调用ioctl类型为SIOCSIFHWADDR时是要设置这个设备的mac地址。一般

对mac地址的设置没有太大意义的。

dev->set_config()

------------------ Linux *** 作系统网络驱动程序编写 -------------------

------------ Contact the author by mailto:bordi@bordi.dhs.org ------

当用户调用ioctl时类型为SIOCSIFMAP时,系统会调用驱动程序的set_config

方法。用户会传递一个ifmap结构包含需要的I/O、中断等参数。

dev->do_ioctl()

如果用户调用ioctl时类型在SIOCDEVPRIVATE和SIOCDEVPRIVATE+15之间,系统

会调用驱动程序的这个方法。一般是设置设备的专用数据。

读取信息也是通过ioctl调用进行。除次之外驱动程序还可以提供一个

dev->get_stats方法,返回一个enet_statistics结构,包含发送接收的统计信息。

ioctl的处理在net/core/dev.c的dev_ioctl()和dev_ifsioc()里。

方法如下:

方法一:更新网卡驱动

1、在桌面找到我的电脑。

2、右键点击设备管理器。

3、找到无线适配器,单击,出现驱动然后点击右键进行更新。

方法二:重新下载网卡驱动

(1)手动官网驱动下载

1、按照电脑品牌搜索官网。

2、在官网的支持专区输入电脑型号搜索驱动。

3、在搜索结果中找到网卡驱动,点击下载即可。

4、下载完成后进行解压和安装即可。

(2)下载第三方驱动软件,如驱动人生或者驱动精灵

1、到以上软件的官网下载正版软件。

2、进行下载,下载完成后进行安装。

3、运行软件,软件自动开始检测,检测完毕后会自动进行匹配的网卡驱动安装。

1

方法一:

1:ethtool -i ethx

如:

linux:/mnt # ethtool -i eth1

driver: e1000e

version: 1.0.2-k2

firmware-version: 1.9-0

bus-info: 0000:0b:00.0

linux:/mnt # ethtool -i eth16

driver: igb

version: 2.1.0-k2

firmware-version: 1.4-1

bus-info: 0000:0a:00.0

linux:/mnt #

2:使用 modinfo igb 查看驱动信息

linux:~ # modinfo igb

filename: /lib/modules/2.6.32.12-0.7-default/kernel/drivers/net/igb/igb.ko

version:5.2.5

license:GPL

description:Intel(R) Gigabit Ethernet Network Driver

author: Intel Corporation, <e1000-devel@lists.sourceforge.net>

srcversion: 0E80ABCD0117D822FE8B271

alias: pci:v00008086d000010D6sv*sd*bc*sc*i*

alias: pci:v00008086d000010A9sv*sd*bc*sc*i*

alias: pci:v00008086d000010A7sv*sd*bc*sc*i*

alias: pci:v00008086d000010E8sv*sd*bc*sc*i*

alias: pci:v00008086d00001526sv*sd*bc*sc*i*

alias: pci:v00008086d0000150Dsv*sd*bc*sc*i*

alias: pci:v00008086d000010E7sv*sd*bc*sc*i*

alias: pci:v00008086d000010E6sv*sd*bc*sc*i*

alias: pci:v00008086d00001518sv*sd*bc*sc*i*

alias: pci:v00008086d0000150Asv*sd*bc*sc*i*

alias: pci:v00008086d000010C9sv*sd*bc*sc*i*

alias: pci:v00008086d00000440sv*sd*bc*sc*i*

alias: pci:v00008086d0000043Csv*sd*bc*sc*i*

alias: pci:v00008086d0000043Asv*sd*bc*sc*i*

alias: pci:v00008086d00000438sv*sd*bc*sc*i*

alias: pci:v00008086d00001516sv*sd*bc*sc*i*

alias: pci:v00008086d00001511sv*sd*bc*sc*i*

alias: pci:v00008086d00001510sv*sd*bc*sc*i*

alias: pci:v00008086d00001527sv*sd*bc*sc*i*

alias: pci:v00008086d0000150Fsv*sd*bc*sc*i*

alias: pci:v00008086d0000150Esv*sd*bc*sc*i*

alias: pci:v00008086d00001524sv*sd*bc*sc*i*

alias: pci:v00008086d00001523sv*sd*bc*sc*i*

alias: pci:v00008086d00001522sv*sd*bc*sc*i*

alias: pci:v00008086d00001521sv*sd*bc*sc*i*

alias: pci:v00008086d00001539sv*sd*bc*sc*i*

alias: pci:v00008086d0000157Csv*sd*bc*sc*i*

alias: pci:v00008086d0000157Bsv*sd*bc*sc*i*

alias: pci:v00008086d00001538sv*sd*bc*sc*i*

alias: pci:v00008086d00001537sv*sd*bc*sc*i*

alias: pci:v00008086d00001536sv*sd*bc*sc*i*

alias: pci:v00008086d00001533sv*sd*bc*sc*i*

alias: pci:v00008086d00001F45sv*sd*bc*sc*i*

alias: pci:v00008086d00001F41sv*sd*bc*sc*i*

alias: pci:v00008086d00001F40sv*sd*bc*sc*i*

depends:hwmon,dca

supported: external

vermagic: 2.6.32.12-0.7-default SMP mod_unload modversions

parm: InterruptThrottleRate:Maximum interrupts per second, per vector, (max 100000), default 3=adaptive (array of int)

parm: IntMode:Change Interrupt Mode (0=Legacy, 1=MSI, 2=MSI-X), default 2 (array of int)

parm: Node:set the starting node to allocate memory on, default -1 (array of int)

parm: LLIPort:Low Latency Interrupt TCP Port (0-65535), default 0=off (array of int)

parm: LLIPush:Low Latency Interrupt on TCP Push flag (0,1), default 0=off (array of int)

parm: LLISize:Low Latency Interrupt on Packet Size (0-1500), default 0=off (array of int)

parm: RSS:Number of Receive-Side Scaling Descriptor Queues (0-8), default 1, 0=number of cpus (array of int)

parm: VMDQ:Number of Virtual Machine Device Queues: 0-1 = disable, 2-8 enable, default 0 (array of int)

parm: max_vfs:Number of Virtual Functions: 0 = disable, 1-7 enable, default 0 (array of int)

parm: MDD:Malicious Driver Detection (0/1), default 1 = enabled. Only available when max_vfs is greater than 0 (array of int)

parm: QueuePairs:Enable Tx/Rx queue pairs for interrupt handling (0,1), default 1=on (array of int)

parm: EEE:Enable/disable on parts that support the feature (array of int)

parm: DMAC:Disable or set latency for DMA Coalescing ((0=off, 1000-10000(msec), 250, 500 (usec)) (array of int)

parm: LRO:Large Receive Offload (0,1), default 0=off (array of int)

parm: enable_debug:Set to 1 to enable debug tracing into the syslog (uint)

parm: debug:Debug level (0=none, ..., 16=all) (int)

linux:~ #

2

方法二:

1:dmesg | grep ethx

如:

linux:~ # dmesg | grep eth17

[ 30.351872] igb 0000:0a:00.1: eth17: (PCIe:2.5Gb/s:Width x4) 00:0b:ab:52:fb:b3

[ 30.351952] igb 0000:0a:00.1: eth17: PBA No: ffffff-0ff

[429171.548763] device eth17 entered promiscuous mode

[429173.116530] igb: eth17 NIC Link is Up 1000 Mbps Full Duplex, Flow Control: RX

[429173.118867] ADDRCONF(NETDEV_UP): eth17: link is not ready

[429173.121176] ADDRCONF(NETDEV_CHANGE): eth17: link becomes ready

[429183.236266] eth17: no IPv6 routers present

[1641503.272376] igb 0000:0a:00.1: eth17: (PCIe:2.5Gb/s:Width x4) 00:0b:ab:52:fb:b3

[1641503.272460] igb 0000:0a:00.1: eth17: PBA No: ffffff-0ff

[1641598.356110] device eth17 entered promiscuous mode

[1641598.369229] igb: eth17 NIC Link is Up 1000 Mbps Full Duplex, Flow Control: RX

[1641598.372636] ADDRCONF(NETDEV_UP): eth17: link is not ready

[1641598.374978] ADDRCONF(NETDEV_CHANGE): eth17: link becomes ready

[1641608.564181] eth17: no IPv6 routers present

[1701779.787471] igb 0000:0a:00.1: eth17: (PCIe:2.5GT/s:Width x4)

[1701779.787473] igb 0000:0a:00.1: eth17: MAC: 00:0b:ab:52:fb:b3

[1701779.787555] igb 0000:0a:00.1: eth17: PBA No: FFFFFF-0FF

[1702124.805650] device eth17 entered promiscuous mode

[1702141.839131] ADDRCONF(NETDEV_UP): eth17: link is not ready

[1702144.057474] igb: eth17 NIC Link is Up 1000 Mbps Full Duplex, Flow Control: None

[1702144.059425] ADDRCONF(NETDEV_CHANGE): eth17: link becomes ready

[1702154.705520] eth17: no IPv6 routers present

[1712008.630151] igb 0000:0a:00.1: eth17: (PCIe:2.5GT/s:Width x4)

[1712008.630153] igb 0000:0a:00.1: eth17: MAC: 00:0b:ab:52:fb:b3

[1712008.630235] igb 0000:0a:00.1: eth17: PBA No: FFFFFF-0FF

[1712100.136186] device eth17 entered promiscuous mode

[1712101.873823] ADDRCONF(NETDEV_UP): eth17: link is not ready

[1712104.159209] igb: eth17 NIC Link is Up 1000 Mbps Full Duplex, Flow Control: None

[1712104.161548] ADDRCONF(NETDEV_CHANGE): eth17: link becomes ready

[1712114.854722] eth17: no IPv6 routers present

linux:~ #

2:使用 modinfo igb 查看驱动信息

(结果同方法一中的)


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/8110497.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-13
下一篇 2023-04-13

发表评论

登录后才能评论

评论列表(0条)

保存