Linux网卡驱动程序详解

合集下载

linux网卡驱动安装-配ip

linux网卡驱动安装-配ip

第一步看网卡型号,因为你装网卡是要根据网卡芯片的型号来的,不像windows那样方便,可以直接用万能驱动,所以搞清楚网卡型号是关键,不然是装不上的。

下面以r8168的网卡为例(此网卡芯片是瑞丽的)插上U盘之后先查看磁盘分区:fdisk空格-l然后会出现一个列表出来,一般U盘会显示在最后的,后面分区类型可以区别开来fat32的然后我们挂载:mount 空格/dev/sdb1(u盘的绝对路径跟设备名称)空格/mnt(挂载点这里也要写绝对路径这里是根分区下面的mnt目录下面)然后回车,没有提示有错误信息的话说明挂载成功了然后cd空格/mnt然后ls查看下这个目录下面的文件信息看下网卡文件是什么文件名的然后我们复制cp空格-r(此参数是用来复制文件夹用的,可以不加参数试下是什么效果)空格r8168-8.003.00/(这里是指的是要复制的源文件,此路径为相对路径,因为我们已经在/mnt的目录下面了,所以可以省略前面的/mnt,用绝对路径的话是这样的/mnt/r8168-8.003.00)空格/home(这里是指的把源文件复制到哪个位置下面)cp空格-r空格r8168-8.003.00/空格/home然后我们转到/home目录下面用命令cd空格/home然后用ls命令查看下文件然后cd空格r8168-8.003.00/然后用ls命令查看下文件然后进入到src里面cd空格src/然后用ls命令查看下文件然后make空格install然后看后面结果,没有什么错误的话就成功了,一般有问题的话都会有error之类的编译成功产生.o或.ko文件,然后我们用insmod插入模块insmod空格/lib/modules/<KERNELVERSION>/kernel/drivers/net/arl1e.[k]o 【insmod空格/lib/modules/<KERNELVERSION>/kernel/drivers/net/】这一部分只有<KERNELVERSION>会有变化,其他的没有的,所以要记得,这里我们记不得的话可以用tab键来补全的<KERNELVERSION>这里可以直接用tab键补全,这个是内核版本号,这个不同版本的linux会有点不一样的然后我们用Service空格kudzu空格restart 检测新硬件此命令在高版本的centos中是用不了的,然后我们用setup来配IP,提示没有这个命令的话,我们只能手动编辑网卡配置文件了Setup是图形化的配置,比较简单。

linux网卡驱动安装

linux网卡驱动安装

LINUX 网卡驱动安装一、U盘/光盘文件挂载,将驱动拷贝到linux系统中1、mkdir /mnt/USB 创建/USB文件夹2、cat /proc/partitions (U盘插入前后各输入一次,查看多出来的盘符,通常是sda1)3、fdisk -l /dev/sda (查看输出结果)看了输出知道U盘所在的设备,在/dev/sdb1,接着便是挂载了4、mount /dev/sda1 /mnt/usb (U盘挂载到/usb目录)5、cd /mnt/usb (打开/usb,查看挂载成功)6、cp -r /mnt/usb/aa /usb (复制U盘中的文件至linux系统)7、cd umount /mnt/usb (卸载U盘)光盘挂载mount -o loop /root/Red\Hat\Linux1.iso /mnt/cdrom以挂载红帽的第一块.iso安装镜像文件cd /mnt/cdrom 进入挂载点二、驱动安装1、在进入操作后打开光驱或软驱,驱动程序的文件有.rpm 和.tar 两种类型,后缀为.rpm 的文件可以直接双击运行加载驱动,而.tar 后缀的文件需要手动加载。

2、手动加载.tar 文件步骤如下:(1)cp AR....tar.gz /root 将驱动盘中后缀为.tar 的文件拷贝到计算机中(2)按“ctrl+alt+f2”进入终端模式,随后根据系统提示输入正确的用户名及口令(3)键入“cd x”命令,x 为刚才拷贝后缀为.tar 文件的路径(4)键入“dir”或“ls”命令后确保能在屏幕上看到后缀为.tar的文件(5)“mkdir /arl1e“创建文件,”tar zxvf AR..tar.gz -C arl1e “(解压到arl1e)(6)待解压缩成功完成后,在原来的目录中会出现刚刚解压完后的ARXX-[version]文件夹,“cd /arl1e/src“(用“cd”命令进入该文件夹中的src 子文件夹)(7)随后键入“make”或“make install”命令,系统将自动运行,成功运行后会生成“arl1e.o”和“arl1e.ko”的文件(查看redme文件,找生成目录)cd /LIB/modules/2.6..../kernel/drivers/net/atl1e (进入目录on 2.6.x kernels)(8)成功生成上述文件后,键入命令“insmod arl1e.o”或“insmod arl1e.ko (on 2.6.x kernels)”验证光驱,接着键入“make install”命令,如果键入命令后系统无任何提示则代表整个驱动程序安装成功(9)“ifconfig ethx x.x.x.x”为网口配置地址,其中第一个“x”为系统网口的编号(通常以0开始),后面的“x.x.x.x”代表32位地址(例如:192.168.1.11)(10)vi /etc/sysconfig/network-script/ifcfg-eth0 (配置IP)IPADDR=NETMASK=GATEWAY=ONBOOT=answer(激活设备)BOOTPROTO=no/static/dhcp(11)Vi /etc/resolv.conf (配置DNS)mkdir arl1e (创建文件夹)cd arl1e (进入文件目录)。

linux下安装USB无线网卡驱动

linux下安装USB无线网卡驱动

安装方法及步骤:一、准备工作2、到/projects/ndiswrapper下载最新的ndiswrapper包,我的版本是ndiswrapper-1.52.tar.gz3、准备好网卡在Winxp下的驱动程序。

驱动程序最好是最新的,指定给XP用的。

二、安装及配置1、安装(1)解压:tar -zxvf ndiswrapper-1.52.tar.gz(2)进入ndiswrapper-1.52:cd ndiswrapper-1.52(3)编译:make//在此操作之前必需先把编译环境搭建好,即:安装好开发环境。

(4)安装:make install //如果不采取默认的安装路径,则可以用。

configure ——prefix="/etc/local"来指定安装目录。

//此目录是自建。

(5)查看安装后的版本ndiswrapper -v | grep //此处似乎不对。

如安装成功则显示version: 1.51(6)获取该无线网卡的WindowsXP驱动,(7)进入该驱动所在目录:cd /home/tsm/dwl_g520(8)安装无线网卡驱动:ndiswrapper -i net5211.inf(9)查看驱动是否安装:ndiswrapper -l//如:显示net5211(驱动名称):driver installed(10)写入配置文件:ndiswrapper -mndiswrapper -mandiswrapper -mi(11)启动后模块自动加载:modprobe ndiswrapper注:安装出现问题的仔细按照下面小问题的步骤会解决问题的:小问题:我是用ndiswrapper安装windows下的inf驱动的sony本本384M内存,4M集成显存,usb1.0接口,跑windows速度比较慢,就像装个轻量级的操作系统。

无线网卡的ID是0cf3:1006,查询得到芯片对应驱动为ar9271.fw装过几次puppy(4.0、4.1、5.1)多是因为usb无线网卡无法驱动只好作罢。

linux安装网卡驱动教程

linux安装网卡驱动教程

linux安装网卡驱动教程在Linux系统中,网卡驱动是一个必需的组件,它允许计算机与网络相连,进行数据的传输和通信。

虽然大多数Linux发行版会自动安装一些常用的网卡驱动,但某些特殊型号的网卡可能需要手动安装对应的驱动程序。

下面是一个详细的Linux安装网卡驱动的教程,帮助你完成这个过程。

1. 首先,你需要确定你的网卡型号和型号。

可以通过以下命令获取:```lspci | grep Ethernet```这将列出系统中所有的以太网适配器,包括网卡的型号和型号。

2. 一旦你确定了网卡的型号和型号,你可以在厂商的官方网站或者第三方驱动程序网站上查找和下载对应的驱动程序。

确保选择与你的Linux发行版和内核版本兼容的驱动程序。

3. 下载驱动程序后,将其保存在你的计算机上的一个可访问的位置,比如家目录。

4. 打开终端,在命令行中输入以下命令以进入驱动程序所在目录:```cd ~/下载```这里假设你将驱动程序保存在`~/下载`目录下。

如果你将其保存在其他目录,请将命令中的路径替换为实际位置。

5. 解压驱动程序文件。

这可以通过以下命令完成:```tar zxvf 驱动程序文件名.tar.gz```这里的`驱动程序文件名`应该是你下载的驱动程序文件的实际名称。

6. 进入驱动程序文件夹。

这可以通过以下命令完成:```cd 驱动程序文件夹名```这里的`驱动程序文件夹名`是解压后的驱动程序文件夹的实际名称。

7. 阅读驱动程序的安装说明文档。

通常情况下,驱动程序的文件夹中都会包含一个README文件或者INSTALL文件,其中提供了安装驱动程序所需的具体步骤和说明。

8. 一般来说,安装驱动程序的第一步是编译驱动程序的源代码。

在终端中输入以下命令以编译驱动程序:```make```这将根据驱动程序的源代码编译出可执行的二进制文件。

9. 安装编译好的驱动程序。

在终端中输入以下命令以安装驱动程序:```sudo make install```这需要管理员权限,所以你可能需要输入管理员密码。

LINUX下安装无线网卡驱动程序

LINUX下安装无线网卡驱动程序

1.1下载驱动程序一、首先弄清楚当前使用的是什么版本的Linux,如:Redhat9.0、Freda core5等。

Linux用的是什么版本的内核,可以用uname –a得知内核版本号。

二、使用的是什么接口类型的无线网卡,常见的接口类型有PCI (台式机)、Cardbus/PCMCIA(笔记本)、USB(台式机、笔记本)等。

三、无线网卡使用的是哪一种类型的芯片,判断方法如下,到相关网站下载与其相对应的驱动程序。

常用的无线网卡驱动及其支持的芯片类型和接口类型如下表1所示。

PCMCIA卡:将无线网卡插入笔记本电脑中,用命令lspci可以看到芯片类型,如:Realtek Semiconductor Co.. Ltd, RTL8180L802.11b MAC (rev 20)其中RTL8180L即为网卡芯片类型,用参数v可以看到更详细的信息。

USB卡:用命令lsusb可以看到芯片类型(fc5没有此命令)。

通过其它方法查找。

驱动程序芯片类型接口类型官方网站下载页Linux-wlan-ng Prism2/2.5/3 Cardbus/PCI/USB /download.shtml Madwifi Atheros系列Cardbus/PCI /islsm ISL38XX USB/PCI http://jbnote.free.fr/prism54usb/index.html表1:网卡驱动程序有关信息1.2驱动程序的安装每款驱动的安装方法各不相同,阅读INSTALL文档,获得驱动程序的具体安装方法及系统内核要求。

阅读驱动程序附带的README文档,获得有关此驱动的详细信息,如驱动所支持的网络模式、接口类型、所包含的模块的功能及无线网卡的配置等。

下面是安装网卡驱动程序的一般步骤:编译网卡驱动程序#make安装驱动程序模块#make install加载模块#modprobe 主模块名安装好驱动后插入无线网卡,命令iwconfig可以查看驱动是否加载成功,如果出现新的设备名,则说明驱动已经装上并且能够识别出无线网卡。

LINUX PCI设备驱动程序详解

LINUX PCI设备驱动程序详解
内核维护一个内部数组每个元素对应一个驱动程序所谓驱动程序也就是一组定义好的函数集这个函数集放入一个内核指定的数据结构中不难推出这个数据结构的字段都是指向函数地址的指针主设备号被用作数组下标程序员在调用registerchrdev函数注册一个驱动程序时内核将驱动程序的调用地址存入由主设备号指定的数组中的某个位置
实际上,dev_t 是专用于应用程序的一个数据类型,内核使用的是 kdev_t 数据类型。kdev_t 与 dev_t 一样也是一个无符号短整型。现在可以关注 inode 结构了。
Linux 对每个创建文件都会在内存中创建一个对应的 inode 数据结构,i_rdev 字段值就是传 入的设备号(主设备号与次设备号的结合)。注意到 inode 结构中还有个 kdev_t 类型的变量: i_dev,这是这个节点所在设备的设备号。所在设备就是该节点所存储的设备,例如一般将 其存于磁盘上,则这个字段的值就是磁盘设备的设备号,这与该节点所代表设备的设备号是
static struct device_struct chrdevs[MAX_CHRDEV];
其中 device_struct 结构定义如下: struct device_struct { const char * name; struct file_operations * fops; };
这个结构用一个名称标志该驱动程序,这个名称是在用 register_chrdev 函数注册驱动程序时 传入的第二个参数,而 fops 字段则就是上面所说的那个内核指定的数据结构。这个字段的 值也是通过 register_chrdev 函数传入的,此函数调用的原形如下: int register_chrdev(unsigned int major, const char * name, struct file_operations *fops);

Linux E1000网卡驱动分析

Linux E1000网卡驱动分析

Linux-千兆网卡驱动实现机制浅析作者: Minit, 出处:博客,责任编辑: 罗丽艳,2009-03-29 00:001.引言本分析主要针对e1000网卡,驱动源码为7.3.20-k2。

本文的目的不是为了讲述如何编写驱动程序,主要是分析网卡驱动内部的实现机制。

通过此分析,希望可以理解驱动程序中的各个部分的关系,对网卡发送和接收数据包有直观的了解,同时也希望对设计网卡驱动程序有帮助。

由于网卡驱动程序与硬件和操作系统都有很紧密的联系,故要把某些问题完全弄清楚,需要很多的经验与相关知识,介于自身的水平有限,且自身经验较少,故肯定存在很多问题,希望本文的读者发现了问题不吝与作者联系。

2.网卡驱动的体系结构网卡作为一个PCI设备,其必须遵守相应的PCI规范,即必须为网卡定义相应的标识号,每个PCI外设由一个总线编号、一个设备编号及一个功能编号来标识。

网卡驱动程序则需要定义相应的pci_device_id结构来表示其支持的PCI外设的标识,通过在驱动程序的pci_device_id中查找设备标识号,将驱动程序与设备联系起来。

网卡作为PCI设备,其包括两类空间,一种是配置空间,CPU不能直接访问,访问这个空间,需要借助BIOS功能;另一种是普通的控制寄存器空间,这部分经过映射后,CPU可以直接访问控制。

在硬件加电初始化时,BIOS统一检查所有的PCI设备,并为每个设备分配一个物理地址,该地址通过BIOS获得并写到设备的配置空间内,驱动程序就可以将网卡的普通控制寄存器映射到一段内存空间内,CPU通过访问映射后的虚拟地址来操控网卡的寄存器。

当操作系统初始化时,其为每个PCI设备分配一个pci_dev结构,并将前面分配的物理地址写到pci_dev的resource字段中。

在网卡驱动程序中则可以通过读取pci_dev中的resource字段获得网卡的寄存器配置空间地址,其由函数pci_resource_start()和pci_resource_end()获得该空间的起始位置,通过ioremap()将该段位置映射到主存中,以便CPU访问控制网卡的I/O和内存空间。

Linux网卡驱动程序编写

Linux网卡驱动程序编写

Linux网卡驱动程序编写©OldLinux论坛-- 有关早期Linux内核代码发展的论坛。

uz4$])Linux操作系统网络驱动程序编写%A一.Linux系统设备驱动程序概述u51.1 Linux设备驱动程序分类%Vp}1.2 编写驱动程序的一些基本概念*I二.Linux系统网络设备驱动程序"[`]K2.1 网络驱动程序的结构H2.2 网络驱动程序的基本方法U?+2.3 网络驱动程序中用到的数据结构IKo2.4 常用的系统支持v[ZEeu三.编写Linux网络驱动程序中可能遇到的问题/oY3.1 中断共享l@FZkg3.2 硬件发送忙时的处理pZw3.3 流量控制(flow control) $:p.3.4 调试W!07四.进一步的阅读]G_0Q五.杂项Z!h©OldLinux论坛 -- 有关早期Linux内核代码发展的论坛。

")e ©OldLinux论坛-- 有关早期Linux内核代码发展的论坛。

tej$©OldLinux论坛-- 有关早期Linux内核代码发展的论坛。

B12.2©OldLinux论坛-- 有关早期Linux内核代码发展的论坛。

c‘<ae一.Linux系统设备驱动程序概述c1.1 Linux设备驱动程序分类eiD)|wLinux设备驱动程序在Linux的内核源代码中占有很大的比例,源代码的长度日益增加,主要是驱动程序的增加。

在Linux内核的不断升级过程中,驱动程序的结构还是相对稳定。

在2.0.xx到2.2.xx的变动里,驱动程序的编写做了一些改变,但是从2.0.xx的驱动到2.2.xx 的移植只需做少量的工作。

ASyLinux系统的设备分为字符设备(char device),块设备(block device)和网络设备(network device)三种。

字符设备是指存取时没有缓存的设备。

块设备的读写都有缓存来支持,并且块设备必须能够随机存取(random access),字符设备则没有这个要求。

尚观Linux研究室 - Linux驱动程序全解析

尚观Linux研究室 - Linux驱动程序全解析

尚观Linux研究室- Linux驱动程序全解析V2版2006尚观Linux研究室- Linux驱动程序全解析之块设备的驱动及网络驱动的配置IDE硬盘及光驱的设置、IDE刻录机的使用,以及如何安装SCSI硬盘驱动。

然后我们将为大家介绍,以太网卡驱动模块的加载及网络接口的启功过程,如何调整网卡的参数,Modem、ADSL及宽带的驱动安装,怎样PPP连接的设置等内容。

一、尚观Linux研究室- Linux驱动程序全解析之IDE硬盘及光驱1.IDE设备的驱动过程操作系统首先是安装在块设备上,没有对块设备的支持,我们的系统就无法启动,所以我们首先介绍常见块设备的安装。

硬盘就是最常见的块设备,在普通PC上的硬盘通常是IDE 接口的,而服务器上的硬盘通常是SCSI接口的。

一般内核中内置对通用IDE控制芯片的支持。

让我们来看一下IDE硬盘在内核中的驱动过程,dmesg命令可以看到内核在启功和加载内核模块时的信息:# dmesg | less我们可以看见,在Linux内核启动过程中,我们可以发现内核首先驱动初始化CPU,内存,系统时钟部分,接着加载PCI总线的驱动。

然后就加载了通用的IDE驱动程序:Uniform Multi-Platform E-IDE driver Revision: 7.00beta4-2.4接着初始化IDE的控制器,我的IDE控制器集成在Intel的ICH4南桥芯片组中,IDE控制芯片驱动加载后,进行初始化传输模式:ICH4: chipset revision 1ICH4: not 100% native mode: will probe irqs lateride0: BM-DMA at 0xbfa0-0xbfa7, BIOS settings: hda:DMA, hdb:pioide1: BM-DMA at 0xbfa8-0xbfaf, BIOS settings: hdc:DMA, hdd:pio该驱动程序会向核心中注册主设备号为3的block型设备。

基于Linux的光纤通道网卡驱动程序开发分析

基于Linux的光纤通道网卡驱动程序开发分析

基于Linux的光纤通道网卡驱动程序开发分析摘要:linux以其自身内核强大稳定、工作效率高、易于扩展以及丰富的硬件支持等优点,现已被广泛应用于嵌入式系统当中。

驱动程序实质上就是在操作系统当中添加一个代码,其中主要包含与硬件设备相关的信息,拥有这些信息后,便可以实现计算机与设备之间的通信。

如果没有驱动程序,计算机的硬件设备便无法进行正常工作。

而网卡驱动程序是驱动程序中较为重要的一个部分。

基于此点,本文就基于linux的光纤通道网卡驱动程序开发进行浅谈。

关键词:linux;光纤通道;网卡驱动;程序开发中图分类号:tp311.52 文献标识码:a 文章编号:1007-9599 (2012)18-0000-021 基于linux系统下的网卡驱动原理当前,在科学技术不断发展的推动下,使linux操作系统获得了进一步完善,该系统的核心部分现已实现了osi的网络层及更上层部分。

其中网络层的实现是以数据链路的高效、可靠运行为基础,它的实现为网卡驱动程序提供了可靠的接口。

1.1 网卡驱动程序由上述分析可知,在基于linux的操作系统当中,驱动程序可以为系统以及物理层提供接口,下面分别对系统的接口和物理层的接口进行介绍。

(1)系统接口。

驱动程序为系统的接口包括以下一些例程:发现网卡、检测网卡参数、数据接收和数据发送等等。

当驱动程序启动后,系统会自行对相关的例程进行检测和调用,借助该过程发现网卡,如果系统使用的是即插即用型网卡,系统在对例程进行检测时便可以发现其中的参数,如果不是即插即用型网卡,那么便需要在驱动程序运行前,预先设置好网卡的具体参数,以供驱动程序使用。

当系统核心需要对数据进行发送时,其便会通过调用由驱动程序发送的例程来完成这一操作。

这一过程实质上就是发送例程将数据写入空间的过程。

(2)物理层接口。

该接口属于中断处理例程,它的工作原理可概括为网卡在接收或发送数据时,如果发现数据有误,其会自动生成一个中断,此时系统的核心会对中断处理例程进行调用,并在判断其产生的原因后完成响应处理。

Linux网络设备驱动_PCI网卡

Linux网络设备驱动_PCI网卡

⏹ Linux 网络设备驱动结构Linux的加载和卸载设备的注册初始化和注销设备的打开和释放据包的发送和接收络连接状况数设置和统计数据此驱动所支持的网卡系列初始化网络设备注销网络设备设备挂起函数设备恢复函数打开网络设备关闭网络设备读取包的网卡收发包的状态,统计数据用户的ioctl 命令系统调用硬件处理数据包发送ISR 数据包发送和接收⏹ struct pci_driver如果网络设备(包括wireless )是PCI 规范的,则先是向内核注册该PCI 设备(pci_register_driver),然后由pci_driver 数据结构中的probe 函数指针所指向的侦测函数来初始化该PCI 设备,并且同时注册和初始化该网络设备。

如果网络设备(包括wireless )是PCMCIA 规范的,则先是向内核注册该PCMCIA 设备(register_pccard_driver),然后driver_info_t 数据结构中的attach 函数指针所指向的侦测函数来初始化该PCMCIA 设备,并且同时注册和初始化该网络设备。

1. 申明为PCI 设备:static struct pci_driver tg3_driver = {.name = DRV_MODULE_NAME,//此驱动所支持的网卡系列,vendor_id, device_id.id_table = tg3_pci_tbl,//初始化网络设备的回调函数.probe = tg3_init_one,//注销网络设备的回调函数.remove = __devexit_p(tg3_remove_one),//设备挂起函数.suspend = tg3_suspend,//设备恢复函数.resume = tg3_resume};2. 驱动模块的加载和卸载static int __init tg3_init(void){//先注册成PCI设备,并初始化,如果是其他的ESIA,PCMCIA,用其他函数return pci_module_init(&tg3_driver);}static void __exit tg3_cleanup(void){pci_unregister_driver(&tg3_driver);//注销PCI设备}module_init(tg3_init); //驱动模块的加载module_exit(tg3_cleanup); //驱动模块的卸载3. PCI设备探测函数probe,初始化网络设备主要工作:申请并设置pci资源(内存),申请并设置net_device网络设备结构,IO映射,注册网络设备static int __devinit tg3_init_one(struct pci_dev *pdev, const struct pci_device_id *ent){//初始化设备,使I/O,memory可用,唤醒设备pci_enable_device(pdev);//申请内存空间,配置网卡的I/O,memory资源pci_request_regions(pdev, DRV_MODULE_NAME);pci_set_master(pdev);//设置DMA属性pci_set_dma_mask(pdev, (u64) 0xffffffffffffffff);//网卡 I/O,memory资源的启始地址tg3reg_base = pci_resource_start(pdev, 0);//网卡I/O,memory资源的大小tg3reg_len = pci_resource_len(pdev, 0);//分配并设置网络设备dev = alloc_etherdev(sizeof(*tp));//申明为内核设备模块SET_MODULE_OWNER(dev);//初始化私有结构中的各成员值tp = dev->priv;tp->pdev = pdev;tp->dev = dev;……//锁的初始化spin_lock_init(&tp->lock);//映射I/O,memory地址到私有域中的寄存器结构tp->regs = (unsigned long) ioremap(tg3reg_base, tg3reg_len);dev->irq = pdev->irq;//网络设备回调函数赋值dev->open = tg3_open;dev->stop = tg3_close;dev->get_stats = tg3_get_stats;dev->set_multicast_list = tg3_set_rx_mode;dev->set_mac_aDDRess = tg3_set_mac_addr;dev->do_ioctl = tg3_ioctl;dev->tx_timeout = tg3_tx_timeout;dev->hard_start_xmit= tg3_start_xmit;//网卡的MAC地址赋值dev->addrtg3_get_device_address(tp);//注册网络设备register_netdev(dev);//把网络设备指针地址放入PCI设备中的设备指针中pci_set_drvdata(pdev, dev);}4. 注销网络设备主要工作:注销并释放网络设备,取消地址映射,释放PCI资源static void __devexit tg3_remove_one(struct pci_dev *pdev){struct net_device *dev = pci_get_drvdata(pdev);//注销网络设备unregister_netdev(dev);//取消地址映射iounmap((void *) ((struct tg3 *)(dev->priv))->regs);//释放网络设备kfree(dev);//释放PCI资源pci_release_regions(pdev);//停用PCI设备pci_disable_device(pdev);//PCI设备中的设备指针赋空pci_set_drvdata(pdev, NULL);}5. 网络设备挂起主要工作:停用网卡的中断寄存器,停止网卡收发包,停用网卡某些硬件,设置电源状态static int tg3_suspend(struct pci_dev *pdev, u32 state){//停用网卡的中断寄存器tg3_disable_ints(tp);//停止网卡收发包netif_device_detach(dev);//停止网卡某些硬件,fireware的一些功能tg3_halt(tp);//设置网卡的电源状态tg3_set_power_state(tp, state);}6. 网络设备恢复主要工作:恢复网卡电源,允许收发包,初始化收发包的缓冲区,初始化网卡硬件,打开网卡中断寄存器static int tg3_resume(struct pci_dev *pdev){//恢复网卡电源tg3_set_power_state(tp, 0);//允许网卡收发包netif_device_attach(dev);//初始化收发包的缓冲区tg3_init_rings(tp);//初始化网卡硬件tg3_init_hw(tp);//打开网卡中断寄存器tg3_enable_ints(tp);}struct net_device1. 打开网络设备主要工作:分配中断及注册中断处理函数,初始化硬件及收发缓冲区,初始化定时器及注册超时函数,允许网卡开始传送包static int tg3_open(struct net_device *dev){//分配一个中断request_irq(dev->irq, tg3_interrupt, SA_SHIRQ, dev->name, dev);/* int request_irq(unsigned int irq,void (*handler)(int irq, void *dev_id, struct pt_regs *regs),unsigned long irqflags,const char * devname,void *dev_id);irq是要申请的硬件中断号。

基于Linux操作系统的网卡驱动程序及其安装

基于Linux操作系统的网卡驱动程序及其安装

基于Linux 操作系统的网卡驱动程序及其安装刘金梅1周 彤1李紫薇2刘立军3(1.北华航天工业学院电子工程系,河北廊坊065000; 2.唐山建龙实业有限公司,河北唐山064200;3.廊坊开发区昂瑞自动化工程有限公司,河北廊坊065000)摘 要:本文介绍了Linux 操作系统中网络设备和驱动程序的工作原理,针对不同的网卡提出了配置T CP/IP 网络参数的方法,并重点对于网卡安装过程中出现的一些问题进行了分析,提出了相应的解决办法。

关键词:L inux ;网卡;驱动程序中图分类号:T P393 文献标识码:A 文章编号:1673-7938(2008)03-0019-04收稿日期:2008-03-28作者简介:刘金梅(1977-),女,助教,硕士,河北秦皇岛人,从事嵌入式系统研究与开发。

随着Linux 的迅速普及和发展,越来越多的企业和个人开始使用Linux 系统,而Linux 以其系统强大的功能、对多用户的支持和运行稳健的特性越来越多的应用在网络方面,很适合为Web Server 或网络数据库管理系统等服务提供操作系统平台[1]。

但网卡驱动程序的加载与配置比较特殊,Linux 的支持函数少,只能依赖内核中的函数,许多操作需要自己编写,而且调试也非常不方便,因此在Linux 下进行网卡的加载和配置的设计就显得十分重要。

1 Linu x 系统网络设备在Linux 系统中有3种类型的设备:字符设备、块设备和网络设备[2]。

其中前两种设备的驱动都是以文件形式存在于/dev 目录下,可以在用户进程中对其进行读写操作。

而网络设备驱动提供的网络界面并未存在于文件系统之中。

在使用网络设备时,用户进程不再是以文件读写的方式对设备进行操作,而是通过针对特定协议的界面与操作系统的网络协议软件进行通信。

首先,网络设备在Linux 下有其不同于其它设备的处理方式[3]。

Linux 对所有的网络设备进行了抽象并定义了一个统一的概念,称之为接口(inter -face)。

Linux中写个简单的网卡驱动程序

Linux中写个简单的网卡驱动程序

Linux中写个简单的网卡驱动程序学习应该是一个先把问题简单化,再把问题复杂化的过程。

一开始就着手处理复杂的问题,难免让人有心惊胆颤,捉襟见肘的感觉。

读Linux网卡驱动也是一样。

那长长的源码夹杂着那些我们陌生的变量和符号,望而生畏便是理所当然的了。

不要担心,事情总有解决的办法,先把一些我们管不着的代码切割出去,留下必须的部分,把框架掌握了,那其他的事情自然就水到渠成了,这是笔者的心得。

一般在使用的Linux网卡驱动代码动辄3000行左右,这个代码量以及它所表达出来的知识量无疑是庞大的,我们有没有办法缩短一下这个代码量,使我们的学习变的简单些呢?经过笔者的不懈努力,在仍然能够使网络设备正常工作的前提下,把它缩减到了600多行,我们把暂时还用不上的功能先割出去。

这样一来,事情就简单多了,真的就剩下一个框架了。

下面我们就来剖析这个可以执行的框架。

限于篇幅,以下分析用到的所有涉及到内核中的函数代码,我都不予列出,但给出在哪个具体文件中,请读者自行查阅。

首先,我们来看看设备的初始化。

当我们正确编译完我们的程序后,我们就需要把生成的目标文件加载到内核中去,我们会先ifconfig eth0 down和rmmod 8139too来卸载正在使用的网卡驱动,然后insmod 8139too.o把我们的驱动加载进去(其中8139too.o是我们编译生成的目标文件)。

就像C程序有主函数main()一样,模块也有第一个执行的函数,即module_init(rtl8139_init_module);在我们的程序中,rtl8139_init_module()在insmod之后首先执行,它的代码如下:static int __init rtl8139_init_module (void){return pci_module_init (&rtl8139_pci_driver);}它直接调用了pci_module_init(),这个函数代码在Linux/drivers/net/eepro100.c中,并且把rtl8139_pci_driver(这个结构是在我们的驱动代码里定义的,它是驱动程序和PCI设备联系的纽带)的地址作为参数传给了它。

linux网卡驱动安装方法

linux网卡驱动安装方法

linux网卡驱动安装方法1、查找网卡芯片,确立网卡型号,并确立linux内核版本,到对应的网卡官网下载驱动。

2、安装步骤(这里以RTL8110为例)1)cd到网卡驱动所在文件夹,解压:# tar vjxf r8168-8.aaa.bb.tar.bz2//关于解压命令查看《Linux下软件包的解压方式大全》2)进入文件夹:# cd r8168-8.aaa.bb3)安装(需安装kernel-source,否则会失败):# make clean modules (as root or with sudo)# make install# depmod -a# insmod ./src/r8168.ko (or r8168.o in linux kernel 2.4.x)4)通过以下指令查看驱动是否已经启动.# lsmod | grep r8168# ifconfig -a如果能看到类似“ethX”的结果输出, 说明已成功驱动网卡.5)配置并激活ethX.# ifconfig ethX up,where X=0,1,2,...3、附realtek RTL8168B驱动readme文档:<Linux device driver for Realtek Ethernet controllers>This is the Linux device driver released for RealTek RTL8168B/8111Band RTL8168C/8111C, Gigabit Ethernet controllers with PCI-Expressinterface.<Requirements>- Kernel source tree (supported Linux kernel 2.6.x and 2.4.x)- For linux kernel 2.4.x, this driver supports 2.4.20 and latter.- Compiler/binutils for kernel compilation<Quick install with proper kernel settings>Unpack the tarball :# tar vjxf r8168-8.aaa.bb.tar.bz2Change to the directory:# cd r8168-8.aaa.bbIf you are running the target kernel, then you should be able to do :# make clean modules (as root or with sudo)# make install# depmod -a# insmod ./src/r8168.ko (or r8168.o in linux kernel 2.4.x)You can check whether the driver is loaded by using following commands.# lsmod | grep r8168# ifconfig -aIf there is a device name, ethX, shown on the monitor, the linuxdriver is loaded. Then, you can use the following command to activate the ethX.# ifconfig ethX up,where X=0,1,2,...<Set the network related information>1. Set manuallya. Set the IP address of your machine.# ifconfig ethX "the IP address of your machine"b. Set the IP address of DNS.Insert the following configuration in /etc/resolv.conf.nameserver "the IP address of DNS"c. Set the IP address of gateway.# route add default gw "the IP address of gateway"2. Set by doing configurations in /etc/sysconfig/network-scripts/ifcfg-ethX for Redhat and Fedora, or /etc/sysconfig/network/ifcfg-ethX for SuSE. There are two examples to set network configurations.a. Fix IP address:DEVICE=eth0BOOTPROTO=staticONBOOT=yesTYPE=ethernetNETMASK=255.255.255.0IPADDR=192.168.1.1GA TEWAY=192.168.1.254BROADCAST=192.168.1.255b. DHCP:DEVICE=eth0BOOTPROTO=dhcpONBOOT=yes<Modify the MAC address>There are two ways to modify the MAC address of the NIC.1. Use ifconfig:# ifconfig ethX hw ether YY:YY:YY:YY:YY:YY,where X is the device number assigned by Linux kernel, andYY:YY:YY:YY:YY:YY is the MAC address assigned by the user.2. Use ip:# ip link set ethX address YY:YY:YY:YY:YY:YY,where X is the device number assigned by Linux kernel, andYY:YY:YY:YY:YY:YY is the MAC address assigned by the user.<Force Link Status>1. Force the link status when insert the driver.If the user is in the path ~/r8168, the link status can be forcedto one of the 5 modes as following command.# insmod ./src/r8168.ko speed=SPEED_MODE duplex=DUPLEX_MODE autoneg=NWAY_OPTION,whereSPEED_MODE = 1000 for 1000Mbps= 100 for 100Mbps= 10 for 10MbpsDUPLEX_MODE = 0 for half-duplex= 1 for full-duplexNW AY_OPTION = 0 for auto-negotiation off (true force)= 1 for auto-negotiation on (nway force)For example:# insmod ./src/r8168.ko speed=100 duplex=0 autoneg=1will force PHY to operate in 100Mpbs Half-duplex(nway force).2. Force the link status by using ethtool.a. Insert the driver first.b. Make sure that ethtool exists in /sbin.c. Force the link status as the following command.# ethtool -s ethX speed SPEED_MODE duplex DUPLEX_MODE autoneg NWAY_OPTION,whereSPEED_MODE = 1000 for 1000Mbps= 100 for 100Mbps= 10 for 10MbpsDUPLEX_MODE = half for half-duplex= full for full-duplexNW AY_OPTION = off for auto-negotiation off (true force)= on for auto-negotiation on (nway force)For example:# ethtool -s eth0 speed 100 duplex full autoneg on本文出自:中国E动网[]中国领先互联网接入服务提供商!11年品质保障!-- 中国E动网[]。

linux下安装无线网卡驱动

linux下安装无线网卡驱动

凡使用红旗LINUX 6.0 sp1的可在开始菜单点击实用工具--软件包管理器如图,找到带有“BCM43XX”字样的软件包卸载。

重启红旗LINUX 6.0 sp1。

1,从/下载ndiswrapper 1.53软件。

此软件是用来使Linux 运行windows无线网卡驱动,从而达到驱动无线的目的。

2,从windows系统中提取无线网卡驱动文件。

以便供ndiswrapper使用。

到windows中找驱动文件也比较简单,先是我的电脑-->属性-->硬件-->设备管理器。

如图:点击无线网卡的属性弹出属性框。

如图:年在“驱动程序详细信息”中,可以看到ar5211.sys文件。

用系统的搜索功能搜索c盘中的“5211”文件,可以查出4个包含“5211”的文件。

其中有两个是相同的,复制其中3个到自建的net文件夹中。

拷贝到Linux中。

3,解压ndiswrapper 1.53软件。

到压缩文件目录下运行tar -xzvf ndiswrapper-1.53.tar.gz 如图:4,进入ndiswrapper目录编译软件。

命令:make如图:5,保持在ndiswrapper目录安装软件,先用su命令切换到root用户,确定变成root用户后目录没有变。

如图:成功切换到root用户后,命令:make install 安装软件。

如图就此ndiswrapper安装就完成了。

接下来安装windows驱动到系统中。

6,进入net目录,也就是刚拷贝到Linux中的windows无线网卡驱动文件目录。

运行命令:/usr/sbin/ndiswrapper -i net5211.inf安装windows驱动,如图:7,检查是否安装成功。

命令 /usr/sbin/ndiswrapper -l看见如图红圈中的内容就恭喜你成功80%了。

再输入命令ndiswrapper -m8,用命令:/sbin/modprobe ndiswrapper加载ndiswrapper核心模块,网卡灯就立即亮了。

26.Linux-网卡驱动介绍以及制作虚拟网卡驱动(详解)

26.Linux-网卡驱动介绍以及制作虚拟网卡驱动(详解)

26.Linux-⽹卡驱动介绍以及制作虚拟⽹卡驱动(详解)1.描述⽹卡的驱动其实很简单,它还是与硬件相关,主要是负责收发⽹络的数据包,它将上层协议传递下来的数据包以特定的媒介访问控制⽅式进⾏发送,并将接收到的数据包传递给上层协议。

⽹卡设备与字符设备和块设备不同,⽹络设备并不对应于/dev⽬录下的⽂件,不过会存放在/sys/class/net⽬录下如下图所⽰,我们通过ls /sys/class/net/命令,可以看到有两个⽹卡:2.Linux系统对⽹络设备驱动定义了4个层次,这4个层次有到下分为:1)⽹络协议接⼝层:实现统⼀的数据包收发的协议,该层主要负责调⽤dev_queue_xmit()函数发送数据, netif_rx()函数接收数据2)⽹络设备接⼝层:通过net_device结构体来描述⼀个具体的⽹络设备的信息,实现不同的硬件的统⼀3)设备驱动功能层:⽤来负责驱动⽹络设备硬件来完成各个功能, 它通过hard_start_xmit() 函数启动发送操作,并通过⽹络设备上的中断触发接收操作,4)⽹络设备与媒介层:⽤来负责完成数据包发送和接收的物理实体, 设备驱动功能层的函数都在这物理上驱动的层次结构如下图所⽰:3.⽹卡驱动初始化⽽我们的⽹卡驱动程序,只需要编写⽹络设备接⼝层,填充net_device数据结构的内容并将net_device注册⼊内核,设置硬件相关操作,使能中断处理等3.1其中net_device结构体的重要成员,整理后如下所⽰:struct net_device{char name[IFNAMSIZ]; //⽹卡设备名称unsigned long mem_end; //该设备的内存结束地址unsigned long mem_start; //该设备的内存起始地址unsigned long base_addr; //该设备的内存I/O基地址unsigned int irq; //该设备的中断号unsigned char if_port; //多端⼝设备使⽤的端⼝类型 unsigned char dma; //该设备的DMA通道unsigned long state; //⽹络设备和⽹络适配器的状态信息struct net_device_stats* (*get_stats)(struct net_device *dev); //获取流量的统计信息 //运⾏ifconfig便会调⽤该成员函数,并返回⼀个net_device_stats结构体获取信息struct net_device_stats stats; //⽤来保存统计信息的net_device_stats结构体unsigned long features; //接⼝特征,unsigned int flags; //flags指⽹络接⼝标志,以IFF_(Interface Flags)开头//当flags =IFF_UP(当设备被激活并可以开始发送数据包时,内核设置该标志)、 IFF_AUTOMEDIA(设置设备可在多种媒介间切换)、IFF_BROADCAST(允许⼴播)、IFF_DEBUG(调试模式,可⽤于控制printk调⽤的详细程度)、 IFF_LOOPBACK(回环)、IFF_MULTICAST(允许组播)、 IFF_NOARP(接⼝不能执⾏ARP,点对点接⼝就不需要运⾏ ARP)和IFF_POINTOPOINT(接⼝连接到点到点链路)等。

嵌入式Arm—Linux系统的网卡驱动程序的分析与实现

嵌入式Arm—Linux系统的网卡驱动程序的分析与实现
息 。该 n t d vc 结 构正是 网络驱 动层 的核 心 , 一 e e ie 有 个 完整 的描 述 。 个 网络设备 都具 有这 样 的一个 结 构 , 每
一一一 Ⅺ 一

包 括 实 际 的设 备 ( 网卡 ) 虚 拟 设 备 ( 虚 拟 局 域 如 和 如
网) 。网络设 备可 分为 不 同的类 型 , 以太 网 和令 牌 环 如
作 。 因此 , 编写驱 动程序 , 要 首先 要对设 备具 有准确 的
a a y i g h Li u n t r d i e s r c u e e e r h n a d e l i g h wo k n p i cp e n k y n l z n t e n x e wo k rv r t u t r ,r s a c i g n r a i n t e z r i g rn i l a d e t c n lge f e h o o is o Ar Li u e wo k d v c m a i g o t e e a mp r a t p r s o e e a n t r rv r m- n x n t r e ie, k n u s v r l i o t n a t f g n r l e wo k d i e s r c u e, i h i c u e t e d vc e it a i n,I i a ii g e c e i e t u t r wh c n l d h e ie r g s r to n t l n a h d v c ,mo u e u l a i g,t ed vc t o s i z d l n o d n h e ie me h d ( p n, t p, e d n n e ev n ) I d ii n,i t r u to s i t o u e o e p a n t e p o e s o e e v n o e s o s n i g a d r c i i g . n a d to n e r p in i n r d c d t x li h r c s fr c ii g i f r t n F n l r v d e wo k i t r a e s a d r b s d o h r e . n o ma i . i a l we p o i e a n t r n e f c t n a d, a e n Et e n t o y Ke r s n t r e ie d i e , mb d e o t r Ar — i u CS 9 0 y wo d : e wo k d v c r r e e d d s fwa e, m L n x, 8 0 v

linux服务器安装网卡驱动一般步骤(总结)

linux服务器安装网卡驱动一般步骤(总结)

1.linux服务器安装网卡驱动一般步骤(总结)现在很多服务器安装linux可能会出现网卡,硬盘raid驱动无法等现象,现在将一般安装驱动方法介绍如下:一般服务器自带的光盘里面,有一张是专门的驱动盘。

1、将盘放入光驱2、挂载mount /dev/cdrom /mnt/cdrom 当挂载U盘时,可以到vi /etc/fstab 查看U盘的挂载属性目录3、找到驱动目录,猜测是如下目录cd /mnt/cdrom/driver/net/linux/redhat4.24、里面会有tgz后缀的文件,拷贝到rootcp 名.tgz /root5、解压编辑安装tar xvfz 名.tgzcd 名makemake install6、扫描硬件service kudzu start7、会跳出兰框,按任意键,选择configure,设置IP地址。

OK案例1:IBM X 3650 安装网卡驱动Red Hat Enterprise Linux4.2以后版本可以自动识别IBM X 3650网卡驱动,但是以前版本却不行,下面介绍低版本安装网卡驱动方法:1 Red Hat Enterprise Linux 3版本U6以上可以识别阵列卡,但是无法加载网卡驱动;如果不是U6的版本,需要手动输入linux dd加载阵列卡驱动来继续安装操作系统。

2 安装前题:在开发工具和系统内核全安装安装GCC、kernel-devel、rpmbuild 3个相关包。

说明:rpmbuild ---SRPM是RPM包source 的发布形式, 可分为包含原始代码的(src.rpm)包和不包含原始代码的(nosrc.rpm)包两种.其中,src.rpm包含原始代码也就是src.rpm类似的包都要用rpmbuild命令安装。

rpm -ivh rpm-build-4.2-1.i386.rpmrpm -ivh kernel-devel-2.6.9-5.EL.i686.rpm3、下载网卡驱动bnx2-1.4.36c-1.src.rpm,下载页面:/support/397307-AA1/more_info_local_24340.html4 安装网卡驱动rpm –ivh bnx2-1.4.36c-1.src.rpmcd /usr/src/redhatrpmbuild –bb SPECS/bnx2.specrpm -ivh RPMS/i386/bnx2-1.4.36c-1.i386.rpm --forcerpm -ivh RPMS/i386/bnx2-debuginfo-1.4.36c-1.i386.rpm --force5 reboot后机器启动到扫描新硬件时弹出配置向导,可以配置IP或者跳过此步骤进系统再用系统工具-网络设备控制来配。

linux设备驱动程序入门

linux设备驱动程序入门

Linux设备驱动程序入门在Linux操作系统中,驱动程序是操作系统与硬件设备之间进行通信的重要组成部分。

通过编写设备驱动程序,可以让我们的硬件在Linux系统中得到充分的利用。

本文将介绍Linux设备驱动程序的基本概念和编写方法。

设备驱动程序的基本概念什么是设备驱动程序设备驱动程序是指操作系统内核中的一部分代码,它用于控制和管理硬件设备。

设备驱动程序扮演着操作系统与硬件之间的桥梁,通过操作硬件设备的寄存器和控制器来完成设备的初始化、读取和写入等操作。

同时,设备驱动程序还需要将底层硬件接口和操作系统的抽象层进行对接。

设备驱动程序的类型在Linux系统中,设备驱动程序大致可以分为字符设备驱动程序、块设备驱动程序和网络设备驱动程序三类。

•字符设备驱动程序:字符设备驱动程序用于管理字符设备,它以字符为单位进行数据处理。

在Linux系统中,串口和终端是典型的字符设备。

字符设备驱动程序通常使用文件操作接口(也称为系统调用)来与用户进程进行数据传输。

•块设备驱动程序:块设备驱动程序用于管理块设备,它以块大小为单位进行数据处理。

在Linux系统中,硬盘和U盘是典型的块设备。

块设备驱动程序通常使用块操作接口来与用户进程进行数据传输,它也可以使用文件操作接口。

•网络设备驱动程序:网络设备驱动程序用于管理网络接口设备,它以数据包为单位进行数据处理。

网络设备驱动程序负责管理数据包的发送和接收,以及对数据包进行处理。

在Linux系统中,以太网卡是最常见的网络设备。

Linux设备驱动程序的框架Linux设备驱动程序的框架可以分为五个部分:设备模型、驱动模型、总线模型、中断处理和定时器。

•设备模型:设备模型是指Linux内核中对设备进行抽象和描述的一种模型。

设备模型可以帮助我们更加高效地对设备进行管理和操作。

•驱动模型:驱动模型是指Linux内核中对驱动程序进行管理的一种模型。

驱动模型可以帮助我们更好地管理和协调驱动程序和设备之间的关系。

LINUX内核网卡驱动解析

LINUX内核网卡驱动解析
Linux 具有作为网络操作系统尤其是服务器端操作系统的优势。网络部分代码量很大,TCP/IP 协议本身就很复杂,因而 本章就主要介绍数据包的传递过程、与应用层的接口、与底层的接口及网络驱动程序的编写。
目录
• •
[隐藏]1 1 Linux 网络系统分层结构 2 2 数据包结构
• • • •
2.1 msghdr 结构 2.2 socket 结构 2.3 sk_buff 结构及管理
unsigned int
len,//实际的数据长度 data_len,//数据长度 mac_len,//链路层头的长度 csum;//校验结构
struct icmphdr struct igmphdr struct iphdr struct ipv6hdr unsigned char } h;
*icmph; *igmph; *ipiph; *ipv6h; *raw; //
union { struct iphdr struct ipv6hdr struct arphdr unsigned char } nh; *iph; //IP 层头 *ipv6h; *arph; *raw;
int int struct socket struct sock struct scm_cookie struct msghdr struct iovec struct kiocb };
flags; size; *sock; *sk; *scm; *msg, async_msg; async_iov; *kiocb;
Linux 系统调用建立新的套接字时,需要传递套接字的地址族标识符、 套接字类型以及协议。 内核套接字分配新的 socket 数据结构。Socket 数据结构实际是 VFS 索引节点数据结构的一部分,分配新的 socket 数据结构实际就是分配新的 VFS 索引节点。socket 在文件系统位置如下图所示:
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

当网络上一台计算机准备发送数据时,他的网卡开始工作了,首先网卡的芯片侦听在网络上是否有数据在流动,如果没有,他就把数据发送到网络上,在侦听和发送之间有一段极小的时间延迟,在这段时间内,也有可能在网络上有其他的计算机也准备发送数据,也侦听到网络上没有数据在流动,这就可能两台甚至多台的数据一起发送到网络上,产生数据的碰撞,发送数据的计算机的网卡芯片当然要在发送完成后再校验返回的数据,如果发现和发送的数据不一致,那就是说产生了碰撞,所以在一个以太网络中的计算机数量不宜过多,他不但会增加广播包在网络中的数量,也请也会增加数据包的碰撞次数.我们的计算机的网卡芯片在接收到一完整的数据包后,芯片的一引脚通知8259中断控制器,中断控制器再发出中断给CPU,由此,CPU随即调用该网卡的中断例程,如:DOS是这样的屏蔽所有中断(cli)push any register因为中断向量在段0所以xor ax,axmov ds,axmul ax,中断号那么在数据段的[ax]偏移处是该中断例程的指针了call [ax]就到该中断例程了...(DOS是比较遥远的事情了,我所描述的是他的原理,当然不会这么简单,如果那位网友有兴趣详细描述一下上面的原理,纠正或替换掉我所写的就感激不尽了)总之,在本例程中,CPU将调用elintr中断例程,并带有参数unit即该种网卡的第几块(因为在计算机中,你有可能装了相同的网卡有几块),elintr的作用是把数据从网卡的数据存储器中读到我们在该网卡初始化时预先分配好的数据缓冲区中,他调用的函数就只有elread,同样elread也只调用了elget一个函数.elread函数比较简单,就是调用elget,elget则相对比较复杂一点,涉及到核心内存分配mbuf,mbuf是比较恐怖的东西,正如STEVEN所写的,为了节约当时"巨大"的4M内存,牺牲了性能搞出了这个mbuf东东,mbuf是必须要弄懂的,虽然在设备驱动程序中调用他的宏和函数不多,但在后面的IP协议,TCP协议中有不少涉及的地方.关于数据发送方面和接收差不多,在上层协议放置好数据到mbuf链后,调用el_start函数,该函数把mbuf链中的数据放置到本块网卡的发送队列缓冲el_pktbuf中,然后再调用el_xmit 函数,此函数把发送队列缓冲el_pktbuf中的数据有传递到网卡的数据存储器中.我认为,这中间的内存拷贝是多于的,应该在el_start函数中直接把mbuf中的数据传递到网卡的数据存储器中,这样会使性能有较大幅度的提升,因为在驱动程序设计时,最好减少大量的内存拷贝,他占用的时间太多了.*//* FreeBSD的3COM以太网设备驱动程序*//*本段头文件是在编译核心时产生的*/#include "el.h" /*此三文件为编译时产生的头文件,内容是定制核心的一些常量*/#include "opt_inet.h"#include "opt_ipx.h"#include#include#include#include#include#include#include#include#include#include#include#include#include#include /*此头文件是3COM卡的寄存器常量*//* 为了调试方便*/#ifdef EL_DEBUG#define dprintf(x) printf x /*如果定义了DEBUG调试,则打印到屏幕*/#else#define dprintf(x)#endif/* softc结构,每种网卡的该结构是不同的,主要是该第一个成员必须是一以太网的共用结构arpcom*/static struct el_softc {struct arpcom arpcom; /* 以太网公共部分*/u_short el_base; /* 基本输入输出地址*/char el_pktbuf[EL_BUFSIZ]; /* 帧缓冲大小2048 */} el_softc[NEL]; /*NEL在el.h中定义,即编译时产生的头文件,意思为支持的网卡数*//*看看arpcom结构吧* 该结构是以太网设备驱动程序和ARP程序所共享.struct arpcom {/** ifnet 结构必须在此结构的第一个位置./struct ifnet ac_if;u_char ac_enaddr[6]; /* 以太网硬件地址/int ac_multicnt; /* 多播地址列表数/void *ac_netgraph; /* netgraph 节点信息,即我们所说的PPPoE,也就是ADSL宽带所用到的/ };*//* 一些函数申明*/static int el_attach(struct isa_device *);/*第二步:填充相关的数据结构*/static void el_init(void *); /*不用说,是初始化,在probe,attach之后被调用*/static int el_ioctl(struct ifnet *,u_long,caddr_t);/*控制网卡的函树指针*/static int el_probe(struct isa_device *);/*第一步:探测程序.查看是否卡存在和是否在正确的位置.*/static void el_start(struct ifnet *);/*把数据包从硬件接口输出去*/static void el_reset(void *);/* 该例程重设接口. 在el_watchdog()中调用*/static void el_watchdog(struct ifnet *);/*一般该函数用于包在一定时间内没发送出去,就调用他,在本驱动程序中并不支持该函数,在我的rtl8139说明中有*/static void el_stop(void *);/*停止接口,在el_ioctl()和el_reset()中调用*/static int el_xmit(struct el_softc *,int);/*把数据包放到芯片内,发送到以太网上*/static ointhand2_t elintr;/*中断例程*/static __inline void elread(struct el_softc *,caddr_t,int);/* 传递包到更高一级协议处理,即ether_input()例程.由elintr()调用*/static struct mbuf *elget(caddr_t,int,struct ifnet *); /* 从网卡上下载数据包,len是数据的长度,本地以太网头部被分开*/static __inline void el_hardreset(void *);/* 这是一个子程序,目的是重设硬件.*//* isa_driver结构为autoconf准备*//* isa_driver结构说明:该结构来之于文件isa_device.h头文件结构成员:/** 通用的设备驱动程序结构.** 没一设备驱动程序定义一组例程入口,由设置程序在启动时使用.struct isa_driver {int (*probe) __P((struct isa_device *idp));/* 测试设备是否存在int (*attach) __P((struct isa_device *idp));/* 为设备设置驱动程序char *name; /* 设备名称int sensitive_hw; /* 探测发生有冲突时为真,ISA设备的老毛病};*/struct isa_driver eldriver = {el_probe, el_attach, "el"};/* 探测程序.查看是否卡存在和是否在正确的位置. */static intel_probe(struct isa_device *idev){/*isa_device 是设备的通用结构,该结构说明在isa_device.h头文件中,说明如下:struct isa_device {int id_id; /* 设备的idstruct isa_driver *id_driver; 指向设备的驱动程序结构int id_iobase; /* 基本IO地址int id_iosize; /* 基本IO地址的长度u_int id_irq; /* 中断int id_drq; /* DMAcaddr_t id_maddr; /* 在总线中的物理IO内存地址(即便要)int id_msize; /* IO内存的大小union {inthand2_t *id_i;ointhand2_t *id_oi;中断例程指针} id_iu; /* 中断接口例程#define id_intr id_iu.id_i#define id_ointr id_iu.id_oiint id_unit; /* 在该类设备中是第几个int id_flags; /* flagsint id_enabled; /* 设备激活了吗struct isa_device *id_next; /* 在userconfig()中用于isa_devliststruct device *id_device;};*/struct el_softc *sc;u_short base; /* 仅仅为了方便*/u_char station_addr[ETHER_ADDR_LEN];/*以太网的硬件地址*/int i;/* 搜集一些信息*/sc = &el_softc[idev->id_unit];/*sc是softc结构,如果你有NEL块el卡的话就有NEL个softc 结构,当然也有可能同时还有其他的xx_softc结构*/sc->el_base = idev->id_iobase;/*该块卡的基本I/O地址*/base = sc->el_base;/*有一点多余,只是为了方便下面的引用*//* 第一次检查地址,看看基本地址是否在0X280到0X3F0之内*/if((base < 0x280) || (base > 0x3f0)) {printf("el%d: ioaddr must be between 0x280 and 0x3f0n",idev->id_unit);return(0);}/* 现在尝试从PROM中获取地址,看看是否包含了3COM供应商的标识代码.*/dprintf(("Probing 3c501 at 0x%x...n",base));/*在调试时会打印出*//* 重置板卡*/dprintf(("Resetting board...n"));outb(base+EL_AC,EL_AC_RESET);/*我们一般定义基地址为0X300,EL_AC=0E,是辅助命令寄存器*/DELAY(5);/*延迟5毫秒*/outb(base+EL_AC,0);dprintf(("Reading station address...n"));/* 读硬件地址,共六次*/for(i=0;ioutb(base+EL_GPBL,i);station_addr = inb(base+EL_EAW);/*EL_EAW是该卡的地址口,station_addr是函数内部变量, 下面判断了生产厂家后就没用的*/}dprintf(("Address is mn",station_addr, ":"));/* 如果厂商标识代码正确,那么返回1.*/if((station_addr[0] != 0x02) || (station_addr[1] != 0x60)|| (station_addr[2] != 0x8c)) {dprintf(("Bad vendor code.n"));/*3COM厂商此种卡的代码为02608C*/return(0);} else {dprintf(("Vendor code ok.n"));/* 把地址拷贝到arpcom结构中*/bcopy(station_addr,sc->arpcom.ac_enaddr,ETHER_ADDR_LEN);return(1);}}/* 这是一个子程序,目的是重设硬件. 在el_init()中调用,在elintr()中调用,产生中断,有溢出发生时调用*/static __inline voidel_hardreset(xsc)void *xsc;{register struct el_softc *sc = xsc;/*记住在C中,寄存器变量只能有三个,可加快速度*/ register int base;register int j;base = sc->el_base;/* 第一步,重设板卡,和el_probe中的一样(前面) */outb(base+EL_AC,EL_AC_RESET);DELAY(5);outb(base+EL_AC,0);/* 又把地址填回去,为什么?没有为什么,就是厂商规定的,一些端口填什么数据时会怎么样,只有厂商知道,我相信,在同一厂商之间的网卡,交换机,路由器进行秘密通讯是非常可能的,他可以不返回到CPU层*/for(j=0;j<ether_addr_len;j++)outb(base+j,sc->arpcom.ac_enaddr[j]);}/* 连接该接口到核心数据结构.被调用时,我们已经知道该卡已经存在在给定的I/O* 地址,我们还假定中断号是正确的.*/static intel_attach(struct isa_device *idev){struct el_softc *sc;struct ifnet *ifp;/*该结构是一个巨大的结构,在STEVEN的书中有描述,我也写了一篇*/u_short base;/*没用上,可以去掉*/dprintf(("Attaching el%d...n",idev->id_unit));/* 放置一些指针. */idev->id_ointr = elintr;/*放置中断例程指针,中断例程在下面*/sc = &el_softc[idev->id_unit];/*定位本设备的softc结构指针*/ifp = &sc->arpcom.ac_if;/*定位ifnet结构*/base = sc->el_base;/*从程序来看,这一句可以去掉,根本没用,因为在该函数中没用到base*/ /* 重设板卡*/dprintf(("Resetting board...n"));el_hardreset(sc);/*该程序在上面*//* 初始化ifnet结构,该结构的成员经常用来被ether网子程序,arp,bridge等调用*/ifp->if_softc = sc;/*该网卡的IFP(通用接口结构)的专用结构指针(softc结构)*/ifp->if_unit = idev->id_unit;/*第几块网卡*/ifp->if_name = "el";/*网络卡的名称*/ifp->if_mtu = ETHERMTU;/*1500*/ifp->if_output = ether_output;/*以太网的输出子程序指针(不要搞错了,是向IP层输出,按我们的理解是数据输入了,再转送到上一层协议)*/ifp->if_start = el_start;/*把数据包从硬件接口输出去*/ifp->if_ioctl = el_ioctl;/*控制网卡的函树指针*/ifp->if_watchdog = el_watchdog;/*一般该函数用于包在一定时间内没发送出去,就调用他,在本驱动程序中并不支持该函数,在我的rtl8139说明中有*/ifp->if_init = el_init; /*不用说,是初始化,在probe,attach之后被调用*/ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX);/*支持广播和单播*/</ether_addr_len;j++)/* 调用通用以太网初始化例程*/dprintf(("Attaching interface...n"));ether_ifattach(ifp, ETHER_BPF_SUPPORTED);/*在if_ethersubr.c中的ether_ifattach例程void ether_ifattach(ifp, bpf) 调用时,ETHER_BPF_SUPPORTED是BSD的包过滤器,如果在编译时设置文件没有打开包过滤器,那么代表0,否则是1register struct ifnet *ifp;int bpf;{register struct ifaddr *ifa;register struct sockaddr_dl *sdl;if_attach(ifp); 此例程在if.c 中ifp->if_type = IFT_ETHER;代表以太网ifp->if_addrlen = 6;硬件地址长度是6ifp->if_hdrlen = 14;包的头长度是6+6+2=14,其中2是协议类型ifp->if_mtu = ETHERMTU; 为1500,多此一举,在前面你可看到,已经填充了.ifp->if_resolvemulti = ether_resolvemulti; 以太网解析多播例程指针if (ifp->if_baudrate == 0) 波特率ifp->if_baudrate = 10000000;ifa = ifnet_addrs[ifp->if_index - 1];在ifnet_addrs[]数组中找到本地址指针KASSERT(ifa != NULL, ("%s: no lladdr!n", __FUNCTION__));sdl = (struct sockaddr_dl *)ifa->ifa_addr; ifa->ifa_addr在此时指向的是sockaddr_dl结构.sdl->sdl_type = IFT_ETHER;sdl->sdl_alen = ifp->if_addrlen;bcopy((IFP2AC(ifp))->ac_enaddr, LLADDR(sdl), ifp->if_addrlen);把硬件地址拷贝到sdl结构中if (bpf) bpf为真,即加入了BSD包过滤bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));if (ng_ether_attach_p != NULL)(*ng_ether_attach_p)(ifp);}*/printf("el%d: 3c501 address mn",idev->id_unit,sc->arpcom.ac_enaddr, ":");dprintf(("el_attach() finished.n"));return(1);}/* 该例程重设接口. 在el_watchdog()中调用,因为watchdog不在本驱动程序中支持,所以从不被调用*/static voidel_reset(xsc)/*上面的一个函数,重设硬件*/void *xsc;{struct el_softc *sc = xsc;int s;dprintf(("elreset()n"));s = splimp();/*关网络中断*/el_stop(sc);/*下面的一个函数*/el_init(sc);/*重新初始化卡*/splx(s);/*开网络中断*/}/*停止接口,在el_ioctl()和el_reset()中调用*/static void el_stop(xsc)void *xsc;{struct el_softc *sc = xsc;outb(sc->el_base+EL_AC,0);/*用0写辅助命令寄存器*/}/* 初始化接口. */static voidel_init(xsc)void *xsc;{struct el_softc *sc = xsc;struct ifnet *ifp;int s;u_short base;ifp = &sc->arpcom.ac_if;/*定位ifnet结构*/base = sc->el_base;/*网卡基本I/O地址*//* 如果地址不知道,什么也不做. */if(TAILQ_EMPTY(&ifp->if_addrhead)) /* 在if.c中的if_attach例程中已经填充,由el_attach调用ether_attach时再调用if_attach */return;s = splimp();/*关网络中断*//* 重设板卡. */dprintf(("Resetting board...n"));el_hardreset(sc);/*该函数在上面,重设硬件*//* 设置接收寄存器rx */dprintf(("Configuring rx...n"));if(ifp->if_flags & IFF_PROMISC) /*是混杂模式?EL_RXC是0X6接收命令寄存器*/outb(base+EL_RXC,(EL_RXC_PROMISC|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_D OFLOW));elseoutb(base+EL_RXC,(EL_RXC_ABROAD|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_D OFLOW));outb(base+EL_RBC,0);/*接收缓冲寄存器清0*//* 设置传输寄存器TX */dprintf(("Configuring tx...n"));outb(base+EL_TXC,0);/* 开始接收*/dprintf(("Starting reception...n"));outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));/*EL_AC_IRQE是IRQ enable(可用) EL_AC_RX为接收寄存器*//* 设置一些开始使用的标志*/ifp->if_flags |= IFF_RUNNING;/*加上正在运行标志*/ifp->if_flags &= ~IFF_OACTIVE;/*去掉正在传输标志*//* 调用输出. */el_start(ifp);splx(s);/*开网络中断*/}/* 开始在接口上输出.从队列中得到包并输出他们,在输出中,留出接收用一部分时间,即打开中断再关闭中断,这样使接口接到的一些数据包不会丢失.*/static voidel_start(struct ifnet *ifp){struct el_softc *sc;u_short base;struct mbuf *m, *m0;int s, i, len, retries, done;/* 定位softc结构的指针*/sc = ifp->if_softc;base = sc->el_base;/*基地址在输入输出指令时常要用到*/dprintf(("el_start()...n"));s = splimp();/*因为下面涉及到if_flags的操作,所以要关闭网络中断*//* 如果输出正在进行,则退出*/if(sc->arpcom.ac_if.if_flags & IFF_OACTIVE)return;sc->arpcom.ac_if.if_flags |= IFF_OACTIVE;/*加上输出正在进行传输标志*//* 主循环*/while(1) {/*唯一出口是准备传输的数据为空,即m0==NULL时*//* 从队列中移出下一数据包到m0中,请看头文件if_var.h#define IF_DEQUEUE(ifq, m) {(m) = (ifq)->ifq_head; ifq是一mbuf指针队列,把第一个mbuf指针放到m中if (m) {if (((ifq)->ifq_head = (m)->m_nextpkt) == 0) 重排队列(ifq)->ifq_tail = 0;(m)->m_nextpkt = 0;(ifq)->ifq_len--;}}*/IF_DEQUEUE(&sc->arpcom.ac_if.if_snd,m0);/* &sc->arpcom.ac_if.if_snd指向发送队列,该宏取出第一个mubf的指针放到m0中,看上面的说明.这是数据结构的好教材*//* 如果发送缓冲区指针为空,即已经发送完,则退出,此是该无穷循环的唯一出口. */if(m0 == NULL) {sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;/*出去前当然要去掉输出正在进行标志*/splx(s);return;}/* 关闭接收*/outb(base+EL_AC,EL_AC_HOST);/*EL_AC_HOST为系统总线可访问缓冲,即系统总线网卡要用*/outb(base+EL_RBC,0);/*接收缓冲寄存器清0*//* 拷贝mbuf中的数据到softc结构定义的成员el_pktbuf中,缓冲大小是EL_BUFSIZ即2048. */ len = 0;for(m = m0; m != NULL; m = m->m_next) { /* m0是一mbuf指针,也是一mbuf链的第一个,此次要发送的是一mbuf链,不是一单个mbuf*/if(m->m_len == 0)continue;bcopy(mtod(m,caddr_t),sc->el_pktbuf+len,m->m_len);/*m->len是该mbuf链的数据长度,sc->el_pktbuf是该卡的发送临时缓冲,要发送的数据在这集中,然后传送到网卡上,太费时间了,应该直接放置到网卡的存储器中.*/len += m->m_len; /*len是这次要发送的总数*/}m_freem(m0); /*释放该mbuf链*/len = max(len,ETHER_MIN_LEN); /*ETHER_MIN_LEN是发送的最小长度*//* 如果有BPF,就交给BPF验证*/if(sc->arpcom.ac_if.if_bpf)bpf_tap(&sc->arpcom.ac_if, sc->el_pktbuf, len);/*你当然可以在这写一点自己的验证过程*//* 传送数据包到板卡*/dprintf(("el: xfr pkt length=%d...n",len));i = EL_BUFSIZ - len;/*EL_BUFSIZ=2048字节*/outb(base+EL_GPBL,(i & 0xff)); /*告诉发送的长度*/outb(base+EL_GPBH,((i>>&0xff));outsb(base+EL_BUF,sc->el_pktbuf,len);/*传输数据到板卡*//* 开始发送数据包*/retries=0;/*下面做循环用的,在发不出去时,循环15次*/done=0; /*done=1时发送成功了*/while(!done) {if(el_xmit(sc,len)) { /* 调用发送例程,其实只要传送base就可以了*/done = -1;break;}/* 检查输出后的状态,如果你要对watchdog支持,可以在这加上代码,即在5毫秒没发送出去,就调用el_watchdog() */i = inb(base+EL_TXS);dprintf(("tx status=0x%xn",i));if(!(i & EL_TXS_READY)) { /* 如果传输状态寄存器不是准备接受新帧就绪*/dprintf(("el: err txs=%xn",i)); /*那就是出错了*/sc->arpcom.ac_if.if_oerrors++;if(i & (EL_TXS_COLL|EL_TXS_COLL16)) {/*网络数据包碰撞*/if((!(i & EL_TXC_DCOLL16)) && retries < 15) {/*做循环的目的是为了有错误时可重传15次*/ retries++;outb(base+EL_AC,EL_AC_HOST);/*EL_AC_HOST为系统总线可访问缓冲*/}}elsedone = 1;}else {sc->arpcom.ac_if.if_opackets++;/*统计用的,说明该卡成功发送一包*/done = 1;}}if(done == -1) /* 包没有传输(失败) */continue;/* 现在给板卡一个机会接收.注意:在linux中曾经ALEN先生批评此卡好恐怖(他说要进博物馆,哈哈),并说在传输时会丢失进来的数据包,我查看了LINUX的驱动程序,确实是这样,但在FreeBSD中,给了一个机会,由此可证明他的关于"丢失包的说法不一定成立",但关于一个缓冲和一次只能发送一数据包的说法确实是真的,还有多播方面也不支持,我也希望大家最好不要去买这东西,和NE2000,PCI中的RTL8139芯片的网卡一样,性能太差了.*/(void)inb(base+EL_AS);/*读辅助状态寄存器*/outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));/* 用IRQ(中断)使能和接收写辅助命令寄存器*/ splx(s);/* 这有可能接收到中断(包到达) */s = splimp();}}/* 这是真正的传输包,由el_start()调用*/static intel_xmit(struct el_softc *sc,int len){int gpl;int i;gpl = EL_BUFSIZ - len;dprintf(("el: xmit..."));outb((sc->el_base)+EL_GPBL,(gpl & 0xff));outb((sc->el_base)+EL_GPBH,((gpl>>&0xff));outb((sc->el_base)+EL_AC,EL_AC_TXFRX);/*真正的传送指令*/i = 20000;while((inb((sc->el_base)+EL_AS) & EL_AS_TXBUSY) && (i>0))/*如果传送还在忙,循环20000次等待*/i--;if(i == 0) {/*这里有一个bug,大家发现没有,i到了0时也有可能传送成功,解决办法是把(i>0)这条件放到前面*//*我稍微讲一下C,在编译C程序时,象while ( (a>b) && (i>0) )时,是这个样子top:if a>b thenif i>0 then执行体endifendifgoto top也就是说,当i=0时候,inb((sc->el_base)+EL_AS)这指令还会执行,也有可能这时候传送完成了,而下面有给打出一个什么"tx not ready"的东东,而且返回失败,有得重新传送一次.*/dprintf(("tx not readyn"));sc->arpcom.ac_if.if_oerrors++;return(-1);}dprintf(("%d cycles.n",(20000-i)));return(0);/*成功*/}/* 传递包到更高一级协议处理,即ether_input()例程.由elintr()调用*/static __inline voidelread(struct el_softc *sc,caddr_t buf,int len){register struct ether_header *eh;struct mbuf *m;eh = (struct ether_header *)buf;/*从buf中分出以太网头部*//** elget函数是把包放入一mbuf缓冲链中*/m = elget(buf,len,&sc->arpcom.ac_if);if(m == 0)/*出错了*/return;ether_input(&sc->arpcom.ac_if,eh,m);/*传输给上一层的包括ifnet结构,以太网头部,一mbuf*/ }/* 中断例程*/static voidelintr(int unit){register struct el_softc *sc;register int base;int stat, rxstat, len, done;/* 寻址softc和I/O基地址*/sc = &el_softc[unit];base = sc->el_base;dprintf(("elintr: "));/* 检查板卡状态*/stat = inb(base+EL_AS);if(stat & EL_AS_RXBUSY) {/*接收忙*/(void)inb(base+EL_RXC);/*读接收命令寄存器*/outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));/* 用IRQ(中断)使能和接收写辅助命令寄存器*/ return;}done = 0;while(!done) {rxstat = inb(base+EL_RXS);if(rxstat & EL_RXS_STALE) {/*EL_RXS_STALE代表接受状态没有改变*/(void)inb(base+EL_RXC);/*读接收命令寄存器*/outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));/* 用IRQ(中断)使能和接收写辅助命令寄存器*/ return;}/* 如果这有一个溢出发生,重新初始化板卡. */if(!(rxstat & EL_RXS_NOFLOW)) {dprintf(("overflow.n"));el_hardreset(sc);/* 使板卡回到接收模式*/if(sc->arpcom.ac_if.if_flags & IFF_PROMISC)outb(base+EL_RXC,(EL_RXC_PROMISC|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_D OFLOW));elseoutb(base+EL_RXC,(EL_RXC_ABROAD|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_D OFLOW));(void)inb(base+EL_AS);/*读辅助状态寄存器*/outb(base+EL_RBC,0);/*清除接收缓冲*/(void)inb(base+EL_RXC);/*读接收命令寄存器*/outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));/* 用IRQ(中断)使能和接收写辅助命令寄存器*/ return;}/* 到这应该是进来了一数据包*/len = inb(base+EL_RBL);len |= inb(base+EL_RBH) << 8;/*包长度的高低位组合为该包的长度*/dprintf(("receive len=%d rxstat=%x ",len,rxstat));outb(base+EL_AC,EL_AC_HOST);/*EL_AC_HOST为系统总线可访问缓冲*//* 如果包太短或太长,回到接收模式并返回*/if((len <= sizeof(struct ether_header)) || (len > ETHER_MAX_LEN)) {/*如果包小于以太网头部的长度或大于最大长度*/if(sc->arpcom.ac_if.if_flags & IFF_PROMISC)/*为重置硬件准备if_flags*/outb(base+EL_RXC,(EL_RXC_PROMISC|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_D OFLOW));outb(base+EL_RXC,(EL_RXC_ABROAD|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_D OFLOW));(void)inb(base+EL_AS);/*读辅助状态寄存器*/outb(base+EL_RBC,0);/*清除接收缓冲*/(void)inb(base+EL_RXC);/*读接收命令寄存器*/outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));/* 用IRQ(中断)使能和接收写辅助命令寄存器*/ return;}sc->arpcom.ac_if.if_ipackets++;/*统计使用,说明接收包总数*//* 拷贝数据到我们的缓冲*/outb(base+EL_GPBL,0);outb(base+EL_GPBH,0);insb(base+EL_BUF,sc->el_pktbuf,len);/*从端口读一串数据到指定地址()*/outb(base+EL_RBC,0);outb(base+EL_AC,EL_AC_RX);dprintf(("m-->",sc->el_pktbuf+6,":"));/*也放置到el_pktbuf中,发送也放在他中,在发送时有一个开中断接数据包的过程不过那时候el_pktbuf中没有数据,不会相互影响.*/dprintf(("mn",sc->el_pktbuf,":"));/* 把数据传递到上一层*/len -= sizeof(struct ether_header);elread(sc,(caddr_t)(sc->el_pktbuf),len);/* 对状态? */stat = inb(base+EL_AS);/* 如果忙不为真则继续*/if(!(stat & EL_AS_RXBUSY))dprintf((" "));elsedone = 1; /*退出循环*/}(void)inb(base+EL_RXC);outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));return;}/** 从网卡上下载数据包,len是数据的长度,本地以太网头部被分开*/static struct mbuf *elget(buf, totlen, ifp)/*由elread调用,buf是指向sc->el_pktbuf缓冲区,并且数据已经存在, totlen是整个数据包长度-sizeof(struct ether_header)即以太网头部的长度*/caddr_t buf;int totlen;struct ifnet *ifp;struct mbuf *top, **mp, *m;int len;register caddr_t cp;char *epkt;buf += sizeof(struct ether_header);/*调用前buf指向...请看下图|--------------------------------整个数据----------------------------------------------||--ether头部14字节----|--------IP或ARP或其他协议头部及数据-----------------------------|^调用前buf指向这^执行之后buf指向这因为在向上传递数据的过程是一层一层的剥,在本次要剥掉ether_header即以太网头部*/cp = buf;/*放入寄存器中*/epkt = cp + totlen;/*epkt在计算后指向数据的尾部*/MGETHDR(m, M_DONTWAIT, MT_DATA);/*得到一标记了头部的mbuf*//*MGETHDR宏说明#define MGETHDR(m, how, type) do {struct mbuf *_mm;int _mhow = (how);int _mtype = (type);int _ms = splimp(); 屏蔽中断if (mmbfree == NULL) mmbfree是内存管理初始化时的全局变量,意思是还有可用的内存块吗? (void)m_mballoc(1, _mhow); 没有就分配一个,1代表分配一个MSIZE大小的块,该函数调用kmem_malloc核心内存分配函数,返回的可用mbuf指针在mmbfree中_mm = mmbfree;if (_mm != NULL) {mmbfree = _mm->m_next; 如果上面的m_mballoc函数执行了,_mm->m_next肯定为NULL mbtypes[MT_FREE]--;_mm->m_type = _mtype; 看上下文可知,_mtype是MT_DATAmbtypes[_mtype]++;_mm->m_next = NULL; 从这开始是初始化mbuf一些指针_mm->m_nextpkt = NULL;_mm->m_data = _mm->m_pktdat;_mm->m_flags = M_PKTHDR; 加入mbuf链首标志,即该链的第一个包,该宏和MGET的不同之处_mm->m_pkthdr.rcvif = NULL;_mm->m_pkthdr.csum_flags = 0;_mm->m_pkthdr.aux = (struct mbuf *)NULL;(m) = _mm;splx(_ms); 恢复中断} else {splx(_ms);_mm = m_retryhdr(_mhow, _mtype); 再来一次MGETHDR,不过m_retryhdr已经定义为空,防止死循环if (_mm == NULL && _mhow == M_WAIT) 还为空(m) = m_mballoc_wait(MGETHDR_C, _mtype); 强制用阻塞型else(m) = _mm;}} while (0)*/if (m == 0)return (0);m->m_pkthdr.rcvif = ifp;/*指向接收该包的网络卡的ifp指针,后面好多协议要用到他*/m->m_pkthdr.len = totlen;/*已经把以太网头部剥离,数据长度没算他了*/m->m_len = MHLEN;/*该出是链首,所以该mbuf的长度是MHLEN,而不是MLEN*//* 这就是MHLEN*/#define MSIZE 256 /* mbuf的大小*/#define MLEN (MSIZE - sizeof(struct m_hdr)) /* 普通数据区的长度*/#define MHLEN (MLEN - sizeof(struct pkthdr)) /* 链首数据区的长度*/top = 0;mp = &while (totlen > 0) {if (top) {/*如果不是链的第一个*/MGET(m, M_DONTWAIT, MT_DATA);/*MGET和MGETHDR差不多,只不过少一个m_flags = M_PKTHDR*/if (m == 0) {m_freem(top);return (0);}m->m_len = MLEN;/*非链首mbuf的长度为MLEN,这个if(top)就代表不是链首mbuf*/}/*如果跳过了上面哪个if,那肯定是链的第一个mbuf,并且m已经在循环外就分配好了.*/ len = min(totlen, epkt - cp);/*epkt在计算后指向数据的尾部,cp指向首部*/if (len >= MINCLSIZE) {/*#define MINCLSIZE (MHLEN + 1) 这意味着只要数据大于MHLEN,就要分配一个簇*/MCLGET(m, M_DONTWAIT);/*看到宏展开后好恐怖,有空我再说一说*/if (m->m_flags & M_EXT)/*在mbuf中注明是扩展型mbuf(即带有簇)*/m->m_len = len = min(len, MCLBYTES);/*如果大于2048则先装2048吧,装的语句在下面*/ elselen = m->m_len;} else {/** 如果到这了,就意味着要么这个包小于MINCLSIZE,要么是后面一点尾巴且小于MINCLSIZE. */if (len < m->m_len) {if (top == 0 && len + max_linkhdr <= m->m_len)m->m_data += max_linkhdr;m->m_len = len;} elselen = m->m_len;}bcopy(cp, mtod(m, caddr_t), (unsigned)len);/*第一次数据移动,费时的操作*/cp += len;*mp = m;mp = &m->m_next;/*把mbuf链接起来*/totlen -= len;if (cp == epkt)cp = buf;}return (top);/*返回装填数据的mbuf链首*/}/*总结:在该函数中,所做的事情非常费时,主要是做内存的申请,大批数据的拷贝,如果象NFS 传送数据,会出现大量的簇的申请和大量簇的数据的拷贝,一次循环需要拷贝2048个32位的双字.如果是发给本机的,那还行,如果是本机做为桥转发及防活墙,即数据不上传到IP层处理,那么可以直接改写mbuf的分配方案,根据不同的网络流量可初始化一定数量的大容量的缓冲链(可以以一个以太网的整页数来分配,如是100M以太网是1514字节,可分配2048字节,是有一点浪费,但性能可提高,sc->el_pktbuf可变为一队列,用来和其他网卡的接收队列进行数据交换.这意味着光数据进入就少拷贝一次,性能将大大提高,目前我正在研究中.)*//** 处理一个IOCTL请求.*/static intel_ioctl(ifp, command, data)register struct ifnet *ifp;u_long command; /*IOCTL的命令*/caddr_t data;{int s, error = 0;s = splimp(); /*先关闭网络中断*/switch (command) {case SIOCSIFADDR:case SIOCGIFADDR:case SIOCSIFMTU:error = ether_ioctl(ifp, command, data);break;case SIOCSIFFLAGS:/** 如果接口已经DOWN但FLAG还有RUNNING, 那么先停止它*/if (((ifp->if_flags & IFF_UP) == 0) &&(ifp->if_flags & IFF_RUNNING)) {el_stop(ifp->if_softc);ifp->if_flags &= ~IFF_RUNNING;/*在FALG中去掉IFF_RUNNING标志*/} else {/** 如果接口已经DOWN,FLAG没有RUNNING, 只要调用el_init例程*/if ((ifp->if_flags & IFF_UP) &&((ifp->if_flags & IFF_RUNNING) == 0))el_init(ifp->if_softc);}break;default:error = EINVAL;}(void) splx(s);return (error);}/* 一般是数据在规定的时间内没有发出后被调用的程序,目前该驱动程序不支持*/ static voidel_watchdog(struct ifnet *ifp){log(LOG_ERR,"el%d: device timeoutn", ifp->if_unit);ifp->if_oerrors++;el_reset(ifp->if_softc);}。

相关文档
最新文档