您好,欢迎访问三七文档
PCI设备驱动1.PCI设备驱动一一、PCI简介PCI是一种外设总线规范。我们先来看一下什么是总线:总线是一种传输信号的路径或信道。典型情况是,总线是连接于一个或多个导体的电气连线,总线上连接的所有设备可在同一时间收到所有的传输内容。总线由电气接口和编程接口组成。本文讨论Linux下的设备驱动,所以,重点关注编程接口。PCI是PeripheralComponentInterconnect(外围设备互联)的简称,是普遍使用在桌面及更大型的计算机上的外设总线。PCI架构被设计为ISA标准的替代品,它有三个主要目标:获得在计算机和外设之间传输数据时更好的性能;尽可能的平台无关;简化往系统中添加和删除外设的工作。二、PCI寻址从现在开始,我想尽可能通过一些实际的例子来说明问题,而减少理论方面的问题的描述,因为,相关的理论的东西,可以在其它地方找到。我们先来看一个例子,我的电脑装有1G的RAM,1G以后的物理内存地址空间都是外部设备IO在系统内存地址空间上的映射。/proc/iomem描述了系统中所有的设备I/O在内存地址空间上的映射。我们来看地址从1G开始的第一个设备在/proc/iomem中是如何描述的:40000000-400003ff:0000:00:1f.1这是一个PCI设备,40000000-400003ff是它所映射的内存地址空间,占据了内存地址空间的1024bytes的位置,而0000:00:1f.1则是一个PCI外设的地址,它以冒号和逗号分隔为4个部分,第一个16位表示域,第二个8位表示一个总线编号,第三个5位表示一个设备号,最后是3位,表示功能号。因为PCI规范允许单个系统拥有高达256个总线,所以总线编号是8位。但对于大型系统而言,这是不够的,所以,引入了域的概念,每个PCI域可以拥有最多256个总线,每个总线上可支持32个设备,所以设备号是5位,而每个设备上最多可有8种功能,所以功能号是3位。由此,我们可以得出上述的PCI设备的地址是0号域0号总线上的31号设备上的1号功能。那上述的这个PCI设备到底是什么呢?下面是我的电脑上的lspci命令的输出:00:00.0Hostbridge:IntelCorporation82845845(Brookdale)ChipsetHostBridge(rev04)00:01.0PCIbridge:IntelCorporation82845845(Brookdale)ChipsetAGPBridge(rev04)00:1d.0USBController:IntelCorporation82801CA/CAMUSB(Hub#1)(rev02)00:1d.1USBController:IntelCorporation82801CA/CAMUSB(Hub#2)(rev02)00:1e.0PCIbridge:IntelCorporation82801MobilePCIBridge(rev42)00:1f.0ISAbridge:IntelCorporation82801CAMISABridge(LPC)(rev02)00:1f.1IDEinterface:IntelCorporation82801CAMIDEU100(rev02)00:1f.3SMBus:IntelCorporation82801CA/CAMSMBusController(rev02)00:1f.5Multimediaaudiocontroller:IntelCorporation82801CA/CAMAC'97AudioController(rev02)00:1f.6Modem:IntelCorporation82801CA/CAMAC'97ModemController(rev02)01:00.0VGAcompatiblecontroller:nVidiaCorporationNV17[GeForce4420Go](reva3)02:00.0FireWire(IEEE1394):VIATechnologies,Inc.IEEE1394HostController(rev46)02:01.0Ethernetcontroller:RealtekSemiconductorCo.,Ltd.RTL-8139/8139C/8139C+(rev10)02:04.0CardBusbridge:O2Micro,Inc.OZ6933CardbusController(rev01)02:04.1CardBusbridge:O2Micro,Inc.OZ6933CardbusController(rev01)lspci没有标明域,但对于一台PC而言,一般只有一个域,即0号域。通过这个输出我们可以看到它是一个IDEinterface。由上述的输出可以看到,我的电脑上共有3个PCI总线(0号,1号,2号)。在单个系统上,插入多个总线是通过桥(bridge)来完成的,桥是一种用来连接总线的特殊PCI外设。所以,PCI系统的整体布局组织为树型,我们可以通过上面的lspci输出,来画出我的电脑上的PCI系统的树型结构:00:00.0(主桥)--00:01.0(PCI桥)-----01:00:0(nVidia显卡)||---00:1d(USB控制器)--00:1d:0(USB1号控制器)||||--00:1d:1(USB2号控制器)||-00:1e:0(PCI桥)--02:00.0(IEEE1394)||||-02:01.0(8139网卡)||||-02:04(CardBus桥)-02:04.0(桥1)||||--02:04.1(桥2)||-00:1f(多功能板卡)-00:1f:0(ISA桥)||--00:1f:1(IDE接口)||--00:1f:3(SMBus)||--00:1f:5(多媒体声音控制器)||--00:1f:6(调制解调器)由上图可以得出,我的电脑上共有8个PCI设备,其中0号总线上(主桥)上连有4个,1号总线上连有1个,2号总线上连有3个。00:1f是一个连有5个功能的多功能板卡。每一个PCI设备都有它映射的内存地址空间和它的I/O区域,这点是比较容易理解的。除此之外,PCI设备还有它的配置寄存器。有了配置寄存器,PCI的驱动程序就不需要探测就能访问设备。配置寄存器的布局是标准化的,配置空间的4个字节含有一个独一无二的功能ID,因此,驱动程序可通过查询外设的特定ID来识别其设备。所以,PCI接口标准在ISA之上的主要创新在于配置地址空间。2.PCI设备驱动二前文已讲过,PCI驱动程序不需要探测就能访问设备,而这得益于配置地址空间。在系统引导阶段,PCI硬件设备保持未激活状态,但每个PCI主板均配备有能够处理PCI的固件,固件通过读写PCI控制器中的寄存器,提供了对设备配置地址空间的访问。配置地址空间的前64字节是标准化的,它提供了厂商号,设备号,版本号等信息,唯一标识一个PCI设备。同时,它也提供了最多可多达6个的I/O地址区域,每个区域可以是内存也可以是I/O地址。这几个I/O地址区域是驱动程序找到设备映射到内存和I/O空间的具体位置的唯一途径。有了这两点,PCI驱动程序就完成了相当于探测的功能。关于这64个字节的配置空间的详细情况,可参阅《Linux设备驱动程序第三版》P306,不再详述。下面,我们来看一下8139too网卡设备的配置空间的详细情况。在2.6内核的系统中,可以在目录/sys/bus/pci/drivers/下看到很多以PCI设备名命名的目录,但不是说这些设备都存在于你的系统中。我们进入8139too目录,其中有一个以它的设备地址0000:02:01.0命名的目录。在这个目录下可以找到该网卡设备相关的很多信息。其中resource记录了它的6个I/O地址区域。内容如下:0x00000000000034000x00000000000034ff0x00000000000001010x00000000e00008000x00000000e00008ff0x00000000000002000x00000000000000000x00000000000000000x00000000000000000x00000000000000000x00000000000000000x00000000000000000x00000000000000000x00000000000000000x00000000000000000x00000000000000000x00000000000000000x00000000000000000x00000000000000000x00000000000000000x0000000000000000由该文件可以看出,8139too设备使用了两个I/O地址区域,第一个是它映射的I/O端口范围,第二个是它映射的内存地址空间。关于这两个值可以在/proc/iomem和/proc/ioport中得到验证。3.PCI设备驱动三为了能看到实际的运行效果,我们选择8139too网卡作为示例,从该网卡的linux驱动程序中裁剪相关代码。一个PCI设备的驱动程序必须要向内核中的PCI核心描述自己。同时,它也必须告诉PCI核心自己能够驱动哪些设备。下面,就介绍两个相关的重要数据结构。structpci_device_id{__u32vendor,device;/*VendoranddeviceIDorPCI_ANY_ID*/__u32subvendor,subdevice;/*SubsystemID'sorPCI_ANY_ID*/__u32class,class_mask;/*(class,subclass,prog-if)triplet*/kernel_ulong_tdriver_data;/*Dataprivatetothedriver*/};structpci_driver{structlist_headnode;char*name;structmodule*owner;conststructpci_device_id*id_table;//驱动所能操纵的设备id列表。int(*probe)(structpci_dev*dev,conststructpci_device_id*id);//插入新设备void(*remove)(structpci_dev*dev);//移除设备。int(*suspend)(structpci_dev*dev,pm_message_tstate);int(*resume)(structpci_dev*dev);int(*enable_wake)(structpci_dev*dev,pci_power_tstate,intenable);void(*shutdown)(structpci_dev*dev);structdevice_driverdriver;structpci_dynidsdynids;};pci_device_id唯一标识一个PCI设备。它的几个成员依次分别表示:厂商号,设备号,子厂商号,子设备号,类别,类别掩码(类可分为基类,子类),私有数据。每一个PCI设备的驱动程序都有一个pci_device_id的数组,用于告诉PCI核心自己能够驱动哪些设备。8139too的驱动程序定义它的pci_device_id数组如下:staticstructpci_device_idrtl8139_pci_tbl[];该数组被初始化为8139系列的一组网卡,当PCI核心得到这个数组后,会拿数组中的每一项跟从PCI配置空间中读取到的数据进行比对,从而为该驱动程序找到正确的设备。而pci_driver代表一个pci驱动程序。成员id_talbe即是指向pci_device_id数组的指针。name是驱动程序的名字,probe完成探测工作,即拿pci_device_id数组与内核中的数据进行比对。remove完成驱动程序的移除工作。关键的成员就这几个。驱动程序通过pci_modu
本文标题:PCI设备驱动
链接地址:https://www.777doc.com/doc-2849232 .html