




已阅读5页,还剩16页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
一:前言 Pci,是Peripheral Component Interconnect的缩写,翻译成中文即为外部设备互联.与传统的总线相比.它的传输速率较高.能为用户提供动态查询pci deivce.和局部总线信息的方法,此外,它还能自动为总线提供仲裁.在近几年的发展过程中,被广泛应用于多种平台.pci协议比较复杂,关于它的详细说明,请查阅有关pci规范的资料,本文不会重复这些部份. 对于驱动工程师来说,Pci设备的枚举是pci设备驱动编写最复杂的操作。分析和理解这部份,是进行深入分析pci设备驱动架构的基础。我们也顺便来研究一下,linux是怎么对这个庞然大物进行封装的。二:pci架构概貌 上图展现了pci驱动架构中,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中的一副说明图,如下:要注意了,上图是以字节为单位的,而不是以位为单位. 那怎么去读取每个设备的配置空间呢?我们在开篇的时候提到过,pci总线为用户提供了动态查询pci设备信息的方法。在x86上,保留了0xCF80xCFF的8个寄存器.实际上就是对应地址为0xCF8的32位寄存器和地址为0xCFC的32位寄存器。在0xCF8寄存中写入要访问设备对应的总线号, 设备号、功能号和寄存器号组成的一个32位数写入0xCF8.然后从0xCFC上就可以取出对应pci设备的信息.写入到0xCF8寄存器的格式如下:低八位(07): (寄存器地址)&0xFC.低二位为零 810:功能位. 有时候,一个pci设备对应多个功能.将每个功能单元分离出来,对应一个独立的pci device 1115位:设备号 对应该pci总线上的设备序号1623位:总线号 根总线的总线号为0.每遍历到下层总线,总线号+1 31:有效位 如果该位为1.则说明写入的数据有效,否则无效 例如:要读取n总线号m设备号f功能号对应设备的vendor id和Device id.过程如下:要写入到0xCF8中的数为: l = 0x8023 | n16 | m11 | f8)&0xFF。四:总线枚举入口分析 Pci的代码分为两个部份。一个部份是与平台相关的部份,存放在linux-2.6.25archXXXpci。在x86,对应为linux-2.6.25archx86pci 另一个部份是平台无关的代码,存放在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.o init.o obj-$(CONFIG_PCI_BIOS) += pcbios.o obj-$(CONFIG_PCI_MMCONFIG) += mmconfig_32.o direct.o mmconfig-shared.o obj-$(CONFIG_PCI_DIRECT) += direct.o pci-y := fixup.o pci-$(CONFIG_ACPI) += acpi.o pci-y += legacy.o irq.o pci-$(CONFIG_X86_VISWS) := visws.o fixup.o pci-$(CONFIG_X86_NUMAQ) := numa.o irq.o obj-y += $(pci-y) common.o early.o 从这个makefile中可以看出:legacy.c是一定会编译到了.而numa.c只有在编译选择了CONFIG_X86_NUMAQ的时候才起效.所以,我们可以毫不犹豫的将眼光放到了legacy.c中. 该文件中的初始化函数如下: static int _init pci_legacy_init(void) if (!raw_pci_ops) printk(PCI: System does not support PCIn); return 0; if (pcibios_scanned+) return 0; printk(PCI: Probing PCI hardwaren); pci_root_bus = pcibios_scan_root(0); if (pci_root_bus) pci_bus_add_devices(pci_root_bus); pcibios_fixup_peer_bridges(); return 0; subsys_initcall(pci_legacy_init); 由subsys_initcall()引用的函数都会放在init区域,这里面的函数是kernel启动的时候会自己执行的函数.首先我们碰到的问题是raw_pci_ops是在什么地方被赋值的.搜索整个代码树,发现是在pci_access_init()中初始化的.如下: static _init int pci_access_init(void) int type _maybe_unused = 0; #ifdef CONFIG_PCI_DIRECT type = pci_direct_probe(); #endif #ifdef CONFIG_PCI_MMCONFIG pci_mmcfg_init(type); #endif if (raw_pci_ops) return 0; #ifdef CONFIG_PCI_BIOS pci_pcbios_init(); #endif /* * dont check for raw_pci_ops here because we want pcbios as last * fallback, yet its needed to run first to set pcibios_last_bus * in case legacy PCI probing is used. otherwise detecting peer busses * fails. */ #ifdef CONFIG_PCI_DIRECT pci_direct_init(type); #endif if (!raw_pci_ops) printk(KERN_ERR PCI: Fatal: No config space access function foundn); return 0; arch_initcall(pci_access_init); 由于arch_initcall()的优先级比subsys_initcall要高.因此,会先运行完pci_access_init之后,才会执行pci_legacy_init. 上面的代码看起来很复杂,没关系,去掉几个我们没有用到的编译代码就简单了. 在x86中,bios其实提供了pci设备的枚举功能.这也是CONFIG_PCI_BIOS的作用,如果对它进行了定义,那么就用bios的pci枚举功能.如果没有定义,说明不采用bios的功能,而是自己手动去枚举,这就是CONFIG_PCI_DIRECT的作用.为了一般性,我们分析CONFIG_PCI_DIRECT的过程.把其它不相关的代码略掉.剩余的就简单了. 在pci规范中,定义了两种操作配置空间的方法,即type1 和type2.在新的设计中,type2的配置机制不会被采用,通常会使用type1.因此,在代码中pci_direct_probe()一般会返回1,即使用type1. pci_direct_init()的代码如下: void _init pci_direct_init(int type) if (type = 0) return; printk(KERN_INFO PCI: Using configuration type %dn, type); if (type = 1) raw_pci_ops = &pci_direct_conf1; else raw_pci_ops = &pci_direct_conf2; 在这里看到,ram_pci_ops最终会指向pci_direct_conf1.顺便看下这个结构: struct pci_raw_ops pci_direct_conf1 = .read = pci_conf1_read, .write = pci_conf1_write, ; 这个结构其实就是pci设备配置空间操作的接口. 五:pci设备的枚举过程 返回到pci_legacy_init()中 static int _init pci_legacy_init(void) printk(PCI: Probing PCI hardwaren); pci_root_bus = pcibios_scan_root(0); if (pci_root_bus) pci_bus_add_devices(pci_root_bus); Pci设备的枚举过程是由pcibios_scan_root()完成的.在这里调用是以0为参数.说明是从根总线起开始枚举. pcibios_scan_root()代码如下: struct pci_bus * _devinit pcibios_scan_root(int busnum) struct pci_bus *bus = NULL; struct pci_sysdata *sd; dmi_check_system(pciprobe_dmi_table); while (bus = pci_find_next_bus(bus) != NULL) if (bus-number = busnum) /* Already scanned */ return bus; /* Allocate per-root-bus (not per bus) arch-specific data. * TODO: leak; this memory is never freed. * Its arguable whether its worth the trouble to care. */ sd = kzalloc(sizeof(*sd), GFP_KERNEL); if (!sd) printk(KERN_ERR PCI: OOM, not probing PCI bus %02xn, busnum); return NULL; printk(KERN_DEBUG PCI: Probing PCI hardware (bus %02x)n, busnum); return pci_scan_bus_parented(NULL, busnum, &pci_root_ops, sd); 先在pci_root_buses中判断是否存在这个根总线对应的总线号.如果存在,说明这条总线已经遍历过了,直接退出. Pci_root_ops这是定义的pci设备配置空间的操作.在没有选择CONFIG_PCI_MMCONFIG的情况下,它的操作都会转入我们在上面的分析的,ram_pci_ops中.这个过程非常简单,可以自行分析. 然后,流程转入pci_scan_bus_parented().代码如下: struct pci_bus * _devinit pci_scan_bus_parented(struct device *parent, int bus, struct pci_ops *ops, void *sysdata) struct pci_bus *b; b = pci_create_bus(parent, bus, ops, sysdata); if (b) b-subordinate = pci_scan_child_bus(b); return b; 在pci_create_bus()中,为对应总线号构建pci_bus,然后将其挂入到pci_root_buses链表.该函数代码比较简单,请自行分析.然后,调用然后pci_scan_child_bus枚举该总线下的所有设备.pci_bus-subordinate表示下流总线的最大总线号.pci_sacn_child_bus()代码如下: unsigned int _devinit pci_scan_child_bus(struct pci_bus *bus) unsigned int devfn, pass, max = bus-secondary; struct pci_dev *dev; pr_debug(PCI: Scanning bus %04x:%02xn, pci_domain_nr(bus), bus-number); /* Go find them, Rover! */ /按功能号扫描设备号对应的pci 设备 for (devfn = 0; devfn number); pcibios_fixup_bus(bus); for (pass=0; pass devices, bus_list) if (dev-hdr_type = PCI_HEADER_TYPE_BRIDGE | dev-hdr_type = PCI_HEADER_TYPE_CARDBUS) max = pci_scan_bridge(bus, dev, max, pass); /* * Weve scanned the bus and so we know all about whats on * the other side of any bridges that may be on this bus plus * any devices. * * Return how far weve got finding sub-buses. */ pr_debug(PCI: Bus scan for %04x:%02x returning with max=%02xn, pci_domain_nr(bus), bus-number, max); return max; 这节的难点就是在这个地方了,从我们之前分析的pci设备配置空间的读写方式可得知.对特定总线.下面最多个32个设备号.每个设备号又对应8 个功能号.我们可以将设备号和功能号放到一起,即占815位.在这面的代码中.对每个设备号调用pci_scan_slot()去扫描它下面的8个功能号对应的设备.总而言之,把该总线下面的所有设备都要枚举完. pci_scan_slot()代码如下: nt pci_scan_slot(struct pci_bus *bus, int devfn) int func, nr = 0; int scan_all_fns; scan_all_fns = pcibios_scan_all_fns(bus, devfn); for (func = 0; func multifunction) if (func 0) dev-multifunction = 1; else break; else if (func = 0 & !scan_all_fns) break; return nr; 对其它的每个设备都会调用pci_scan_single_device().如果是单功能设备(dev-multifunction = 0).则只要判断它的第一个功能号可以了,不需要判断之后功能号对应的设备. Pci_scan_single_device()代码如下: struct pci_dev *_ref pci_scan_single_device(struct pci_bus *bus, int devfn) struct pci_dev *dev; dev = pci_scan_device(bus, devfn); if (!dev) return NULL; /将pci_dev加至pci_bus-devices pci_device_add(dev, bus); return dev; 对每个设备,都会调用pci_scan_device()执行扫描的过程,如果该设备存在,就会将该设备加入到所属总线的devices链表上.这是在pci_device_add()函数中完成的,这个函数比较简单.这里不做详细分析.我们把注意力集中到pci_scan_device(),这函数有点长,分段分析如下: static struct pci_dev * _devinit pci_scan_device(struct pci_bus *bus, int devfn) struct pci_dev *dev; u32 l; u8 hdr_type; int delay = 1; if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l) return NULL; /* some broken boards return 0 or 0 if a slot is empty: */ if (l = 0xffffffff | l = 0x00000000 | l = 0x0000ffff | l = 0xffff0000) return NULL; /* Configuration request Retry Status */ while (l = 0xffff0001) msleep(delay); delay *= 2; if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l) return NULL; /* Card hasnt responded in 60 seconds? Must be stuck. */ if (delay 60 * 1000) printk(KERN_WARNING Device %04x:%02x:%02x.%d not respondingn, pci_domain_nr(bus), bus-number, PCI_SLOT(devfn), PCI_FUNC(devfn); return NULL; 从配置空间中读取该设备对应的vendor id和device id.如果读出来的值,有一个是空的,则说明该功能号对应的设备不存在,或者是配置非法. 如果读出来的是0xffff0001.则需要重新读一次,如果重读次数过多,也会退出 if (pci_bus_read_config_byte(bus, devfn, PCI_HEADER_TYPE, &hdr_type) return NULL; dev = alloc_pci_dev(); if (!dev) return NULL; dev-bus = bus; dev-sysdata = bus-sysdata; dev-dev.parent = bus-bridge; dev-dev.bus = &pci_bus_type; dev-devfn = devfn; dev-hdr_type = hdr_type & 0x7f; dev-multifunction = !(hdr_type & 0x80); dev-vendor = l & 0xffff; dev-device = (l 16) & 0xffff; dev-cfg_size = pci_cfg_space_size(dev); dev-error_state = pci_channel_io_normal; set_pcie_port_type(dev); /* Assume 32-bit PCI; let 64-bit PCI cards (which are far rarer) set this higher, assuming the system even supports it. */ dev-dma_mask = 0xffffffff; 接着,将不同类型设备的共同头部配置读出来,然后赋值给pci_dev的相应成员.这里有个特别要值得注意的地方: dev-dev.bus = &pci_bus_type.即将pci_dev里面封装的device结构的bus设置为了pci_bus_type.这个是很核心的一个步骤.我们先将它放到这里,之后的再来详细分析 特别的, HEADER_TYPE的最高位为0,表示该设备是一个单功能设备 if (pci_setup_device(dev) bus), dev-bus-number, PCI_SLOT(dev-devfn), PCI_FUNC(dev-devfn); pci_read_config_dword(dev, PCI_CLASS_REVISION, &class); dev-revision = class & 0xff; class = 8; /* upper 3 bytes */ dev-class = class; class = 8; pr_debug(PCI: Found %s %04x/%04x %06x %02xn, pci_name(dev), dev-vendor, dev-device, class, dev-hdr_type); /* Unknown power state */ dev-current_state = PCI_UNKNOWN; /* Early fixups, before probing the BARs */ pci_fixup_device(pci_fixup_early, dev); class = dev-class 8; switch (dev-hdr_type) /* header type */ case PCI_HEADER_TYPE_NORMAL: /* standard header */ if (class = PCI_CLASS_BRIDGE_PCI) goto bad; pci_read_irq(dev); pci_read_bases(dev, 6, PCI_ROM_ADDRESS); pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, &dev-subsystem_vendor); pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &dev-subsystem_device); /* * Do the ugly legacy mode stuff here rather than broken chip * quirk code. Legacy mode ATA controllers have fixed * addresses. These are not always echoed in BAR0-3, and * BAR0-3 in a few cases contain junk! */ if (class = PCI_CLASS_STORAGE_IDE) u8 progif; pci_read_config_byte(dev, PCI_CLASS_PROG, &progif); if (progif & 1) = 0) dev-resource0.start = 0x1F0; dev-resource0.end = 0x1F7; dev-resource0.flags = LEGACY_IO_RESOURCE; dev-resource1.start = 0x3F6; dev-resource1.end = 0x3F6; dev-resource1.flags = LEGACY_IO_RESOURCE; if (progif & 4) = 0) dev-resource2.start = 0x170; dev-resource2.end = 0x177; dev-resource2.flags = LEGACY_IO_RESOURCE; dev-resource3.start = 0x376; dev-resource3.end = 0x376; dev-resource3.flags = LEGACY_IO_RESOURCE; break; case PCI_HEADER_TYPE_BRIDGE: /* bridge header */ if (class != PCI_CLASS_BRIDGE_PCI) goto bad; /* The PCI-to-PCI bridge spec requires that subtractive decoding (i.e. transparent) bridge must have programming interface code of 0x01. */ pci_read_irq(dev); dev-transparent = (dev-class & 0xff) = 1); pci_read_bases(dev, 2, PCI_ROM_ADDRESS1); break; case PCI_HEADER_TYPE_CARDBUS: /* CardBus bridge header */ if (class != PCI_CLASS_BRIDGE_CARDBUS) goto bad; pci_read_irq(dev); pci_read_bases(dev, 1, 0); pci_read_config_word(dev, PCI_CB_SUBSYSTEM_VENDOR_ID, &dev-subsystem_vendor); pci_read_config_word(dev, PCI_CB_SUBSYSTEM_ID, &dev-subsystem_device); break; default: /* unknown header */ printk(KERN_ERR PCI: device %s has unknown header type %02x, ignoring.n, pci_name(dev), dev-hdr_type); return -1; bad: printk(KERN_ERR PCI: %s: class %x doesnt match header type %02x. Ignoring class.n, pci_name(dev), class, dev-hdr_type); dev-class = PCI_CLASS_NOT_DEFINED; /* We found a fine healthy device, go go go. */ return 0; 总共有三种类型的设备,分别为常规设备(PCI_HEADER_TYPE_NORMAL) ,pci-pci桥设备(PCI_HEADER_TYPE_BRIDGE),笔记本电脑上使用的cardbus(PCI_HEADER_TYPE_CARDBUS).这里的操作不外乎是IRQ的确定,设备存储区间映射等.先将这几个操
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 在线医疗咨询平台建设及运营策略
- 护理查对制度试题及答案
- 2025年宠物美容师基础知识考试题(附含答案)
- 经济学专业毕业论文帮做csdn
- 地下排水管网应急排水能力提升方案
- 医疗器械生产监督管理办法2025年培训考试试卷和答案
- 2025年二级建造师之二建公路工程实务综合练习试卷附答案
- 铝灰渣渣滓处理过程中安全规范
- 2025初级经济师考试试题及答案
- 毕业论文专业不合格
- 超声新技术新项目应用与发展
- 四川省广安市2024-2025学年高一下学期期末考试数学试题(含答案)
- 2025年全国新高考语文一卷评讲课件(共66张)
- 工程专项考核管理办法
- 电缆测试技术课件
- DB45∕T 1098-2024 橡胶沥青路面施工技术规范
- 应急管理局应急物资储备项目方案投标文件(技术方案)
- 政协大走访活动方案
- 公路养护应急培训课件
- 个人养老金课件
- 2025至2030中国氧化钪行业需求状况及未来趋势前景研判报告
评论
0/150
提交评论