对于那些想了解更多有关网卡如何工作、或如何使用现有驱动程序,以及试图为目前不支持的网卡编写自己的驱动程序的人来说,这些信息应该会有用。如果你没有这种想法,那么最好跳过这一节。
1 可编程I/O、共享内存与DMA
如果已经可以发送接收背靠背数据包,就无法把更多的数据放到网络上。每一个现代的以太网卡都可以接收背靠背数据包。Linux的DP8390驱动程序(wd80x3、SMC-Ultra、3c503、ne2000,等等)基本上都可以发送背靠背数据包(依赖于当前的中断延迟),3c509和AT1500的硬件在自动发送背靠背数据包上没有一点问题。
ISA总线可以达到5.3MB/sec (42Mb/sec),对10Mbps以太网而言已经足够了。对于100Mbps网卡,显然需要更快的总线来充分利用网络带宽。
可编程I/O(如NE2000、3c509)
优点:没有使用任何受限制的系统资源,只用了若干I/O寄存器,而且没有16M的限制。
缺点:一般传输速率较慢,CPU需要等待,几乎不可能访问交叉的数据包。
共享内存(如WD80x3、SMC-Ultra、3c503)
优点:简单,比可编程I/O速度快,允许随机访问数据包。在可能的情况下,Linux驱动程序在从网卡复制出接收的IP数据包时计算其校验和,从而比相应的PIO网卡进一步减少了对CPU的占用。
缺点:使用高端内存空间(对DOS用户来说是个大问题,在Linux下没有问题),依然要占用CPU。
从属(普通)的直接内存存取(Linux下没有这种情况!)
优点:在实际数据传递过程中不占用CPU。
缺点:检查边界条件、分配相邻的缓存和DMA寄存器编程使该方法成为最慢的技术。它还占用了一个珍贵的DMA通道,并要求对齐的低端内存缓存。
总线控制的直接内存存取(如LANCE、DEC 21040)
优点:在数据传输过程中不占用CPU,可以把缓存串起来,CPU时间很少或不花费在ISA总线上。大多数总线控制的Linux驱动程序现在使用一种“copybreak”方案,较大的数据包直接从网卡放进内核的网络缓存,小的数据包被CPU复制到cache里进行下一步的处理。
缺点:(只适用于ISA总线的网卡)网卡要求低端内存缓存和一个DMA通道。任何总线控制器在与其它强占总线的总线控制器,如某些古老的SCSI适配器,一起工作时都会出问题。有几个设计低劣的主板芯片组在与总线控制器一起使用时也有麻烦。不使用任何类型的DMA设备的一个原因是使用了设计为代替386的486处理器插件:这些处理器在每个DMA周期都必须刷新cache。(这其中包括Cx486DLC、Ti486DLC、Cx486SLC、Ti486SLC,等等。)
2 编写驱动程序
在Linux下使用以太网卡所必需的只不过是相应的驱动程序。因此,关键是制造商要向公众公开编程的技术资料,而无需你(或其他什么人)签署什么协议。关于获取资料的可能性(也许你不编写代码,那么就是其他人编写你确实需要的驱动程序的可能性),一个较好的指南是Crynwr (nee Clarkson)的包驱动程序的可用性。Russ Nelson在干这些事,对开发Linux驱动程序很有帮助。网上冲浪者可以试着看一下Russ的软件。
Russ Nelson's Packet Drivers
有了资料,就可以为网卡编写驱动程序并在Linux下使用(至少从理论上来说是这样)。记住,有些为XT一类机器设计的老式硬件在Linux这样的多任务环境下工作得不是很好。如果网络流量较大,使用这些网卡会带来大麻烦。
大多数网卡都带有如NDIS和ODI一类的MS-DOS接口的驱动程序,但对Linux没有用。许多人建议直接链接它们或自动翻译一下,但这几乎是不可能的。MS-DOS驱动程序需要在16比特模式,并依赖于“软件中断”,这二者与Linux内核不兼容。这种不兼容实际上是Linux的一个特性,有些Linux驱动程序比其相应的MS-DOS驱动程序要好得多。比如“8390”系列驱动程序使用乒乓传送缓存,该方法刚刚被引进MS-DOS。
(乒乓传送缓存意味着为传送数据包使用至少两个最大大小的包缓存。在网卡发送其中的一个时,载入另一个。在第一个包被发出去后,立刻发送第二个包,依次类推。这样,大多数网卡就可以连续向线路上发送背靠背数据包。)
好啦。你可以决定为Foobar Ethernet网卡编写驱动程序了,因为你有编程资料,而且还没人写这个驱动程序。(......这是两个主要的需求)你可以从Linux内核源码树中提供的网络驱动程序框架开始。在所有近期的内核里都能找到这个文件/usr/src/linux/drivers/net/skeleton.c。也可以看看如下URL的Kernel Hackers Guide:KHG
3 内核的驱动程序接口
下面对编写一个新驱动程序所必需的函数进行了若干说明。和上面提到的驱动程序框架一起阅读可以更清楚一些。
探测
在启动时调用以检查网卡存在与否。如果可以通过读取内存等非强制手段进行检查最好。也可以从I/O端口读取。在探测开始向I/O端口写不好,因为这样可能会损害另一个设备。通常在这里还进行一些设备初始化(分配I/O空间、IRQ、填充dev->???域等等)。必须了解网卡可以配置到哪些I/O端口/内存、如何启用共享内存(如果用了的话)以及如何选择/启用中断产生,等等。
中断处理程序
在网卡发出一个中断时内核调用的程序。他需要确定网卡发出中断的原因并进行相应的操作。一般的中断条件是接收到数据、发送完成、报告出错状况。需要了解相关的中断状态位以进行相应的操作。
传送函数
与dev->hard_start_xmit()链接,在内核想通过设备传送数据时调用它。该函数把数据放入网卡并触发传送。需要了解如何把数据打包并传给网卡(共享内存拷贝、PIO传送、DMA?),以及放入网卡正确的位置。然后需要了解如何通知网卡把数据发送到线路上,(可能)在发送完成后发出一个中断。在硬件无法接收更多数据包时需要设置dev->tbusy标志。在网卡有空间可用时,一般这发生在传送完成中断过程中,清除dev->tbusy标志并用mark_bh(INET_BH)通知上一层。
接收函数
在网卡报告有数据时由内核中断处理程序调用。它把数据从网卡上移出,放入一个sk_buff并通过执行netif_rx(sk_buff)告诉内核数据所在位置。需要了解如何在接收数据时启用中断生成,如何检查相关的接收状态位,以及如何从网卡获取数据(通过共享内存拷贝、PIO、DMA,等等)。
打开函数
与dev->open链接,在有人使用ifconfig eth0 up时网络层调用它—— 把设备连到线路上并启用来接收/发送数据。任何在探测过程中(启用IRQ生成等)没有完成的特别的初始化操作都在这里进行。
关闭函数(可选)
在有人使用ifconfig eth0 down时使网卡进入一个清醒的状态。如果硬件许可的话它会释放中断和DMA通道,并完全关闭以节约能源(象收发器一样)。
其它函数
象一个重新设置函数,如果事情变得很糟,驱动程序可以试图重新设置网卡作为最后防线。一般在发送超时或类似情况下如此进行。也是一个读取网卡统计寄存器的函数,如果是这样配备的话。
4 3Com的技术信息
如果对3Com网卡驱动程序的工作感兴趣,可以从3Com公司获取技术资料。Cameron友好地告诉了我们该如何做:
在我们的“技术参考文献”里给出了3Com的以太网适配器驱动程序程序员需要了解的资料。这些手册描述了板上的程序员接口,但没有提及诊断、安装程序等终端用户所看到的东西。
网络适配器分部的市场部有技术参考资料分发。为了使这个计划更有效,我们把它集中到一个称作“CardFacts”的自动电话系统里。你可以打电话来,然后它把资料传真给你。要索取技术参考资料,打电话到408-727-7021。索取开发人员的订单,资料号是9070。在打电话前准备好你的传真号码。填完订单后把它传真到408-764-5004。手册会由联邦速递的次日服务送到。
有人认为我们的手册不该免费,他们也在寻找此系统过于昂贵或占用的时间和努力太多的证据。到目前为止,3Com的顾客确实很好,向我们提出的要求也很合理。我们需要你们的继续合作并把这样的服务维持下去。
5 基于AMD PCnet/LANCE的网卡的注意事项
AMD的LANCE(以太网的局域网控制器)是最早提供的,已经被“PCnet-ISA”芯片所取代,否则又名为79C960。注意,名称“LANCE”有毛病,有些人会用老名称称呼新芯片。AMD的网络产品分部的Dave Roberts友好地提供了下面有关该芯片的信息:
“从功能上来看,它等同与NE1500。它的寄存器组与使用附加1500/2100结构的老式LANCE一样。PCnet-ISA可以使用较早的1500/2100驱动程序。NE1500和NE2100的结构基本上是相同的。开始Novell把它称为2100,但后来想区分同轴电缆与10BASE-T网卡。属于10BASE-T的就只采用1500范围的编号。这是仅有的区别。
许多公司提供基于PCnet-ISA的产品,包括HP、Racal-Datacom、Allied Telesis、Boca Research、Kingston Technology等等。除了有些制造商增加了“无跳线”特性允许软件配置网卡外,这些网卡基本上都是一样的。大多数制造商没有增加这一特性。AMD提供了一个使用PCnet-ISA的网卡的标准设计软件包,许多制造商不加改变地直接使用我们的设计。这也就是说,如果想编写大多数基于PCnet-ISA的网卡的驱动程序,只需要从AMD获取数据资料。打电话给我们的资料分发中心(800)222-9323,索取PCnet-ISA的数据资料Am79C960。这是免费的。
要迅速了解一块网卡是否“标准”网卡只需要看一下它。如果是标准的,网卡上只有一块大的芯片、一块晶振、一块小的IEEE地址PROM、可能还有一个启动ROM的插座和一个连接器(依照提供的媒介选项可能是1、2或3)。注意,如果是同轴电缆网卡,卡上就应该有一些收发器缓存,它们靠近连接器,远离PCnet-ISA。”
一个可能的网卡黑客需要注意,不同的LANCE产品采用不同的“重起”方法。有些恢复到上次离开网络环路的地方,另一些从环路的开头开始,就象刚被初始化一样。
6 广播与混杂模式
Donald所做的另一件事是实现广播与混杂模式的钩子。所有发布的(即不是ALPHA的)ISA驱动程序现在都支持混杂模式。
Donald写道:“我准备从讨论混杂模式开始,它从概念上来说很容易实现。对大多数硬件,你只需要设置一个寄存器位,然后就可以接收到线路上的每一个数据包。对,差不多就这么简单;对有些硬件,你必须先关闭板卡(可能会丢失若干数据包),重新配置它,然后重新启用以太网卡。对吧,就这么简单,下面要讨论的就不是这么明显了:广播模式。它可以用两种方式实现:
使用混杂模式和一个如Berkeley包过滤器(BPF)的数据包过滤器。BPF是一个模式匹配指令语言,可以编写一个程序挑出感兴趣的地址。它的优点在于它很普遍和可编程。其缺点是没有一个一般性的方法可以让内核避免打开混杂模式和通过每一个注册的包过滤器运行每一个线路上的数据包。参见Berkeley包过滤器以了解更多信息。
使用绝大多数以太网芯片内建的广播包过滤器。
我想应该列出几个以太网卡/芯片提供的广播包过滤器:
芯片/网卡 混杂模式 广播包过滤器
Seeq8001/3c501 Yes Binary filter (1)
3Com/3c509 Yes Binary filter (1)
8390 Yes Autodin II six bit hash (2) (3)
LANCE Yes Autodin II six bit hash (2) (3)
i82586 Yes Hidden Autodin II six bit hash (2) (4)
这些网卡声称有一个过滤器,但只是简单地对“accept all multicast packets”或“accept no multicast packets”回答yes/no。
AUTODIN II是标准的以太网CRC校验多项式。在这种方式下,广播地址被哈希运算后在哈希表里进行查找。如果启用了相应的比特位,则数据包被接收。以太网数据包的设计使得硬件在如此处理时的开销很小——(一般)只要在前6个八进制数(目标地址)之后锁定CRC电路(用来进行错误检查)的6个比特位,把它们作为哈希表的索引(6比特——一个64比特的表)。
这些芯片使用6比特哈希,必须由主机计算并载入哈希表。这也就是说内核必须包含CRC代码。
82586内部使用6比特哈希,但是由自己从接受的广播地址列表计算出哈希表。
注意,这些芯片的过滤效果都不好,还需要一个中间层次的模块完成最后的过滤。同时还要注意,在每种情况下都必须保持一个完整的接受广播地址列表,在出现变化时以重新计算哈希表。
7 Berkeley包过滤器(BPF)
开发者普遍认为BPF的功能不该由内核提供,而是放在一个(但愿很少使用的)兼容库里。
对不了解的人来说:BPF(Berkeley包过滤器)是一种向内核网络层说明对哪些数据包感兴趣的机制。它是用一种建立在底层网络代码中的特殊指令语言解释器实现的。应用程序把一个用这种语言编写的程序传递给内核,然后内核对每一个接收到的数据包执行该程序。如果内核有多个BPF应用程序,对每个数据包都要运行这几个程序。
问题在于很难从数据包过滤器程序推断出应用程序实际上对哪一种数据包感兴趣,所以一般的解决方法就是始终运行过滤器。假设一个应用程序注册的BPF程序是获取发往某个广播地址的低速数据流。绝大多数以太网卡有一个64个入口的哈希表的硬件实现的广播地址过滤器,用来忽略大多数不想要的广播数据包,所以有可能以极低的开销完成这一操作。但是由于有了BPF,内核必须把接口设置为混杂模式,接收所有数据包,并对它们运行过滤器。不管怎样,这样确实可以工作,但考虑到对所要求的数据包进行的处理,就已经变得过于麻烦了。
所有不适合放在别处的相关信息都堆在这里。可能不相干,也可能大家不感兴趣,但还是放在这儿了。
1 向内核传递以太网参数
有两个通用的内核命令可以在启动时向内核传递以太网参数(ether和reserve)。可以用LILO、loadlin或其它接受可选参数的启动工具完成该操作。
例如,如果命令为“blah”,希望接收3个参数(假定为123、456和789),那么在使用LILO时就应该如下:
LILO: linux blah=123,456,789
要了解启动时参数的更多信息(和完全的列表),请参见BootPrompt-HOWTO
ether命令
ether=参数与直接构建在内核的驱动程序一起使用。ether=参数对一个模块化的驱动程序完全不起作用。它的最通用形式如下:
ether=IRQ,BASE_ADDR,PARAM_1,PARAM_2,NAME
所有的参数都是可选的。第一个非数字的参数被用做NAME。
IRQ: 很明显。为“0”的IRQ值(一般为缺省值)意味着autoIRQ。首先设置IRQ而不是base_addr是一个历史性的巧合——无论改变别的什么时都可以改正这一点。
BASE_ADDR: 也很明显。值为“0”(一般为缺省值)意味着探测以太网卡的网卡类型特定的地址列表。
PARAM_1: 这开始是用来覆盖WD80*3这样的共享内存网卡的起始内存的值。有些驱动程序使用该值的低4位来设置诊断信息级别。0 -- 缺省值,1-7 -- 级别1..7,(7是最完全的信息)8 -- 级别0(没有信息)。另外,LANCE驱动程序使用该值的低4位来选择DMA通道。否则就使用auto-DMA。
PARAM_2: 3c503驱动程序使用它来选择内部还是外部收发器。0 -- 缺省/内部, 1 -- 外部AUI。Cabletron的E21XX网卡还使用PARAM_2的低4位来选择输出媒介。否则就自动检测。
NAME: 选择该值所指的网络设备。标准内核对附属于总线的以太网卡使用名称“eth0”、“eth1”、“eth2”和“eth3”,对并口“袖珍”以太网适配器使用“atp0”。arcnet驱动程序使用名称“arc0”。可以使用这些LILO参数明确设置基址来启用多块网卡。1.0内核把基于LANCE的以太网卡作为特殊情况进行处理:LILO参数被忽略,LANCE网卡总是被分配为从“eth0”开始的名称“eth”。附加的非LANCE网卡必须被明确指定为“eth”,并用诸如“ether=0,-1,eth0”的方式禁止通常的“eth0”探测。(对,这是个Bug。)
reserve命令
这个紧接着的LILO命令用法与上面的“ether=”一样,即附加在lilo.conf里指定的启动选择名称后面。
reserve=IO-base,extent{,IO-base,extent...}
在某些机器上,可能需要防止设备驱动程序在某个特定区域里检查设备(自动探测)。其原因可能是由于设计低劣的硬件使启动凝结(如某些以太网卡)、被错误识别的硬件、在较早的探测中状态被改变的硬件、或者仅仅是不想让内核初始化硬件。
启动时的参数reserve通过指定无需探测的I/O端口区域来解决这个问题。该区域保留在内核的端口注册表里,就象该区域里已经发现了一个设备一样。注意,这一机制在大多数机器上是不必要的。只有在有问题或特定的情况下才有必要使用它。
指定区域里的I/O端口受到保护,不被设备探测影响。在某些驱动程序被NE2000挂起,或其它设备被错误地识别为NE2000时使用这一方法。正确的设备驱动程序不该探测一个保留区域,除非另一个启动参数明确指定它这么做。这隐含了reserve经常与其它启动参数一起使用。因此,如果指定了一个reserve区域来保护某个特定设备,通常就必须明确指定对该设备的探测。大多数驱动程序在给定了明确地址后就忽略了端口注册表。
例如,启动行
LILO: linux reserve=0x300,32 ether=0,0x300,eth0
使以太网卡驱动程序以外的所有设备驱动程序都不探测0x300-0x31f。
一般启动时的参数限制是11个,因此每个reserve关键词只能指定5个保留区域。如果请求很复杂,可以使用多个reserve指令。
2 把以太网驱动程序作为模块使用
现在大多数Linux发行版里的内核都只包含很少几个内建的驱动程序。驱动程序都采用独立的动态可加载模块的形式提供。这些模块化驱动程序通常由管理员使用modprobe(8)命令载入,或者在某些情况下由内核通过“kerneld”(在2.0版)或“kmod”(在2.1版)自动载入,然后调用modprobe。
你所用的发行版可能会提供良好的图形配置工具来设置以太网模块。如果可能就先使用它们。下面描述了在这些花俏的配置程序下的信息,以及这些程序改变了哪些东西。
控制使用哪些模块和每个模块提供哪些选项的信息一般保存在文件/etc/conf.modules里。在这个文件里使用的两个感兴趣的主要选项(对以太网卡而言)是alias和options。modprobe命令查阅该文件以了解模块信息。
实际的模块本身一般保存在名为/lib/modules/`uname -r`/net的目录下,其中uname -r命令给出内核的版本(如2.0.34)。你可以在这里看看哪一个模块与你的网卡匹配。
在你的conf.modules文件里首先需要的是告诉modprobe对于eth0(和eth1以及......)网络接口使用什么驱动程序。为此要使用alias命令。例如,有一块使用smc-ultra.o驱动程序模块的ISA SMC EtherEZ网卡,需要增加如下一行把该驱动程序alias到eth0上:
alias eth0 smc-ultra
其次,可能需要一个options行来指出与某个特定模块(或模块别名)一起使用哪些选项。还采用上面那个例子,如果只有一行alias而没有options行,内核会警告(参见dmesg)说自动探测ISA网卡不好。要消除这个警告,需要增加另一行来告诉模块网卡被配置在哪个I/O基址上,比方说是16进制地址0x280。
options smc-ultra io=0x280
大多数ISA模块在insmod命令行接受io=0x340和irq=12这样的参数。提供这些参数以避免探测该网卡是必须的或至少是强烈建议的。与PCI和EISA设备不同,对大多数ISA设备而言没有真正安全的自动探测方法,所以在把驱动程序作为模块使用时应当避免自动探测。
每个模块所接受的所有选项列表可以在以下文件中找到:
/usr/src/linux/Documentation/networking/net-modules.txt
推荐阅读该文件以了解对你的特定网卡可以使用哪些选项。注意,对于单个模块能够处理多个设备的模块,有些支持用逗号分开的值列表,如所有基于8390的驱动程序和PLIP驱动程序。例如:
options 3c503 io=0x280,0x300,0x330,0x350 xcvr=0,1,0,1
上面就是一个模块控制四块3c503网卡,其中网卡2和4使用外部收发器。不要在“=”或逗号周围使用空格。
还要注意,一个忙的模块不能被删除。这也就是说在删除模块前需要使用ifconfig eth0 down(关闭以太网卡)。
命令lsmod可以显示哪些模块被载入、它们是否正在被使用,而rmmod可以删除这些模块。