Linux设备驱动之pci设备的枚举

上传人:m**** 文档编号:564644377 上传时间:2023-04-12 格式:DOC 页数:25 大小:297.50KB
返回 下载 相关 举报
Linux设备驱动之pci设备的枚举_第1页
第1页 / 共25页
Linux设备驱动之pci设备的枚举_第2页
第2页 / 共25页
Linux设备驱动之pci设备的枚举_第3页
第3页 / 共25页
Linux设备驱动之pci设备的枚举_第4页
第4页 / 共25页
Linux设备驱动之pci设备的枚举_第5页
第5页 / 共25页
点击查看更多>>
资源描述

《Linux设备驱动之pci设备的枚举》由会员分享,可在线阅读,更多相关《Linux设备驱动之pci设备的枚举(25页珍藏版)》请在金锄头文库上搜索。

1、、/一:前言Pci,是PeripheralComponentInterconnect的缩写,翻译成中文即为外部设备互联.与传统的总线相比.它的传输速率较高能为用户提供动态查询pcideivce和局部总线信息的方法,此外,它还能自动为总线提供仲裁在近几年的发展过程中,被广泛应用于多种平台.pci协议比较复杂,关于它的详细说明,请查阅有关pci规范的资料,本文不会重复这些部份.对于驱动工程师来说,Pci设备的枚举是pci设备驱动编写最复杂的操作。分析和理解这部份,是进行深入分析pci设备驱动架构的基础。我们也顺便来研究一下,linux是怎么对这个庞然大物进行封装的。二:pci架构概貌上图展现了pc

2、i驱动架构中,pci_bus、pci_dev之间的关系。如上图所示,所有的根总线都链接在pci_root_buses链表中。Pci_bus-device链表链接着该总线下的所有设备。而pci_bus-children链表链接着它的下层总线。对于pci_dev来说。pci_dev-bus指向它所属的pci_bus。Pci_dev-bus_list链接在它所属bus的device链表上。此外,所有pci设备都链接在pci_device链表中。三:pci设备的配置空间每个pci设备都有最多256个连续的配置空间。配置空间中包含了设备的厂商ID,设备ID,IRQ,设备存储区信息等摘下LDD3中的一副说

3、明图,如下:要注意了,上图是以字节为单位的,而不是以位为单位.那怎么去读取每个设备的配置空间呢?我们在开篇的时候提到过,pci总线为用户提供了动态查询pci设备信息的方法。在X86上,保留了0xCF80xCFF的8个寄存器实际上就是对应地址为0xCF8的32位寄存器和地址为OxCFC的32位寄存器。在0xCF8寄存中写入要访问设备对应的总线号,设备号、功能号和寄存器号组成的一个32位数写入0xCF8然后从OxCFC上就可以取出对应pci设备的信息.写入到0xCF8寄存器的格式如下:低八位(07):(寄存器地址)&OxFC低二位为零810:功能位.有时候,一个pci设备对应多个功能.将每个功能单

4、元分离出来,对应一个独立的pcidevice1115位:设备号对应该pci总线上的设备序号1623位:总线号根总线的总线号为0每遍历到下层总线,总线号+131:有效位如果该位为1则说明写入的数据有效,否则无效例如:要读取n总线号m设备号f功能号对应设备的vendorid和Deviceid.过程如下:要写入到0xCF8中的数为:l=0x8023In16Im11If8)&0xFF。四:总线枚举入口分析Pci的代码分为两个部份。一个部份是与平台相关的部份,存放在linux-2.6.25archXXXpci。在x86,对应为linux-2.6.25archx86pci另一个部份是平台无关的代码,存放在

5、linux-2.6.25driverpci下面。大致浏览一下这两个地方的init函数.发现可能枚举pci设备是由函数pcibios_scan_root()完成的.不过搜索源代码后,发现有两个地方会调用这个调数.一个是在linux-2.6.25archx86pcinuma.c另一个是linux-2.6.25archx86pciLegacy.c这两个地方都是封装在一个subsys_initcall()所引用的初始化函数呢?到底哪一个文件才是我们要分析的呢?分析一下linux-2.6.25archx86pci下的Makefile_32.内容如下:obj-y:=i386.oinit.oobj-$(CO

6、NFIG_PCI_BIOS)+=pcbios.oobj-$(CONFIG_PCI_MMCONFIG)+=mmconfig_32.odirect.ommconfig-shared.oobj-$(CONFIG_PCI_DIRECT)+=direct.opci-y:=fixup.opci-$(CONFIG_ACPI)+=acpi.opci-y+=legacy.oirq.opci-$(CONFIG_X86_VISWS):=visws.ofixup.opci-$(CONFIG_X86_NUMAQ):=numa.oirq.oobj-y+=$(pci-y)common.oearly.o从这个makefile

7、中可以看出:legacy.c是一定会编译到了.而numa.c只有在编译选择了CONFIG_X86_NUMAQ的时候才起效.所以,我们可以毫不犹豫的将眼光放到了legacy.c中.该文件中的初始化函数如下:staticint_initpci_legacy_init(void)if(!raw_pci_ops)printk(PCI:SystemdoesnotsupportPCIn);return0;if(pcibios_scanned+)return0;printk(PCI:ProbingPCIhardwaren);pci_root_bus=pcibios_scan_root(0);if(pci_r

8、oot_bus)pci_bus_add_devices(pci_root_bus);pcibios_fixup_peer_bridges();return0;subsys_initcall(pci_legacy_init);由subsys_initcall()引用的函数都会放在init区域,这里面的函数是kernel启动的时候会自己执彳亍的函数.首先我们碰到的问题是raw_pci_ops是在什么地方被赋值的.搜索整个代码树,发现是在pci_access_init()中初始化的.如下:static_initintpci_access_init(void)inttype_maybe_unused=

9、0;#ifdefCONFIG_PCI_DIRECTtype=pci_direct_probe();#endif#ifdefCONFIG_PCI_MMCONFIGpci_mmcfg_init(type);#endifif(raw_pci_ops)return0;#ifdefCONFIG_PCI_BIOSpci_pcbios_init();#endif/*dontcheckforraw_pci_opsherebecausewewantpcbiosaslast*fallback,yetitsneededtorunfirsttosetpcibios_last_bus*incaselegacyPCIpr

10、obingisused.otherwisedetectingpeerbusses*fails.*/#ifdefCONFIG_PCI_DIRECTpci_direct_init(type);#endifif(!raw_pci_ops)printk(KERN_ERRPCI:Fatal:Noconfigspaceaccessfunctionfoundn);return0;arch_initcall(pci_access_init);由于arch_initcall()的优先级比subsys_initcall要高.因此,会先运行完pci_access_init之后,才会执行pci_legacy_init

11、.上面的代码看起来很复杂,没关系,去掉几个我们没有用到的编译代码就简单了.在x86中,bios其实提供了pci设备的枚举功能这也是CONFIG_PCI_BIOS的作用,如果对它进行了定义,那么就用bios的pci枚举功能如果没有定义,说明不采用bios的功能,而是自己手动去枚举,这就是CONFIG_PCI_DIRECT的作用.为了一般性,我们分析CONFIG_PCI_DIRECT的过程把其它不相关的代码略掉剩余的就简单了.在pci规范中,定义了两种操作配置空间的方法,即typel和type2.在新的设计中,type2的配置机制不会被采用,通常会使用typel.因此,在代码中pci_direct

12、_probe()般会返回1,即使用typel.pci_direct_init()的代码如下:void_initpci_direct_init(inttype)if(type=0)return;printk(KERN_INFOPCI:Usingconfigurationtype%dn,type);if(type=l)raw_pci_ops=&pci_direct_confl;elseraw_pci_ops=&pci_direct_conf2;在这里看到,ram_pci_ops最终会指向pci_direct_conf1顺便看下这个结构:structpci_raw_opspci_direct_con

13、fl=read=pci_conf1_read,.write=pci_conf1_write,;这个结构其实就是pci设备配置空间操作的接口.五:pci设备的枚举过程返回到pci_legacy_init()中staticint_initpci_legacy_init(void)printk(PCI:ProbingPCIhardwaren);pci_root_bus=pcibios_scan_root(0);if(pci_root_bus)pci_bus_add_devices(pci_root_bus);Pci设备的枚举过程是由pcibios_scan_root()完成的.在这里调用是以0为参数

14、.说明是从根总线起开始枚举.pcibios_scan_root()代码如下:structpci_bus*_devinitpcibios_scan_root(intbusnum)structpci_bus*bus=NULL;structpci_sysdata*sd;dmi_check_system(pciprobe_dmi_table);while(bus=pci_find_next_bus(bus)!=NULL)if(bus-number=busnum)/*Alreadyscanned*/returnbus;/*Allocateper-root-bus(notperbus)arch-speci

15、ficdata.* TODO:leak;thismemoryisneverfreed.* Itsarguablewhetheritsworththetroubletocare.*/sd=kzalloc(sizeof(*sd),GFP_KERNEL);if(!sd)printk(KERN_ERRPCI:OOM,notprobingPCIbus%02xn,busnum);returnNULL;printk(KERN_DEBUGPCI:ProbingPCIhardware(bus%02x)n,busnum);returnpci_scan_bus_parented(NULL,busnum,&pci_root

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 办公文档 > 解决方案

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号