在PC时代的早期外部设备通过ISA总線接入计算机。ISA总线只有24根地址线因此其上的外部设备只能访问内存的低16M地址空间。这种硬件上的限制对于如今需要大量IO memory的外设来说是鈈可接受的PCI总线的出现,就是为了解决ISA的这些先天缺陷而提出
PCI设计使用32位地址线,使得PCI设备可以映射到32位内存空间的任意位置PCI最大嘚优势在于,设计了各设备独立的配置空间:使得各个设备在系统加电过程中就分配好自身所需的IO内存中断irq等资源(当然,也可以在系統运行过程中动态指定资源但通常不必这么做)。这样做的好处是各PCI设备所需的资源可以通过跟PCI控制器协商动态确定,驱动不必关心資源的分配和管理PCI配置空间长度为256字节,前64字节是协议固定的后面的192个字节预留给厂商定义。下图(图片来源于网络)展示了PCI配置涳间的前64bit定义:
那么,我们如何表示一个特定的PCI设备呢PCI使用32位数字标识一个设备,通过 域:总线:设备:功能 的划分来表示域,一般占用16位;总线一般占用8位;设备一般占用5位;功能,一般占用3位因此,一个PCI总线通常可以支持32个设备一个设备通常可以支持8个功能。
PCI总线设备驱动框架遵循linux的总线/设备/驱动框架模型使用pci_bus表示一条PCI总线,pci_dev表示一个pci设备pci_driver表示一个pci驱动。通常在系统启动过程中,会扫描系统内所有的pci设备并加入pci_bus。pci_driver在注册到pci_bus的过程中会扫描pci_bus下挂的pci_dev,并匹配关联相关的pci设备
memory,irq等设备资源但仍然需要定义字符设备/块設备,或者sysfs接口(用于向上提供用户访问接口)以及提供设备特定的操作方法(用来实现设备特定的功能)一个标准的PCI设备驱动,需要哃时包含一个字符设备(块设备sysfs等),和一个pci_driverpci_driver在注册的过程中,匹配到相应的pci_dev后通常调用pci_driver的probe()方法来初始化设备特定的资源(IO
memory/ irq等)。鼡户通过字符设备(块设备,sysfs等)获取到该pci设备后通过初始化好的设备资源,就可以跟设备进行通信从而实现设备的特定功能。
至此PCI根总线和设备树已经建立好。剩下的事情就是注册每个总线下挂的设备
由此,可以看到pci_driver内嵌一个device_driver,基于设备模型架构注册一个pci_driver,其實就是将内嵌的device_driver注册进设备模型中这部分遵循设备模型的总线/设备/驱动架构,搜索这个pci_bus中下挂的pci_dev调用pci_bus的match()方法匹配设备和驱动的device_id,如果匹配再调用pci_driver的probe()方法(该方法由用户设计,通常用来从pci_dev中获取设备ioirq等资源,初始化设备特定的结构)最后将该驱动和设备关联,并将驅动加入pci_bus总线中
总结:系统启动时,bios会构建好PCI总线和设备信息PCI模块在初始化的过程中,首先根据bios构建好的信息创建好根总线及下挂嘚子总线,并初始化注册总线下的所有设备。至此PCI总线和设备信息已经构建完毕。当注册pci设备驱动时遵循linux设备模型架构,搜索该总線下所有的设备匹配,探测并关联相应的设备最终,基于PCI的特定设备通过pci驱动获取到 io
}