linux PCI 寻址

每个 PCI 外设有一个总线号, 一个设备号, 一个功能号标识. PCI 规范允许单个系统占 用多达 256 个总线, 但是因为 256 个总线对许多大系统是不够的, Linux 现在支持 PCI 域. 每个 PCI 域可以占用多达 256 个总线. 每个总线占用 32 个设备, 每个设备可以是 一个多功能卡(例如一个声音设备, 带有一个附加的 CD-ROM 驱动)有最多 8 个功能. 因 此, 每个功能可在硬件层次被一个 16-位地址或者 key , 标识. Linux 的设备驱动编写 者, 然而, 不需要处理这些二进制地址, 因为它们使用一个特定的数据结构, 称为 pci_dev, 来在设备上操作.

 

 

大部分近期的工作站至少有 2 个 PCI 总线. 在单个系统插入多于 1 个总线要通过桥实 现, 桥是特殊用途的 PCI 外设, 它的工作是连接 2 个总线. 一个 PCI 系统的全部分布 是一个树, 这里每个总线都连接到一个上层总线, 直到在树根的总线 0 . CardBus PC- card 系统也通过桥连接到 PCI 系统. 图一个典型 PCI 系统的布局表示了一个典型的 PCI 系统, 其中各种桥被突出表示了.

 和 PCI 外设相关的 16-位硬件地址, 尽管大部分隐藏在 struct pci_dev 结构中, 仍然 是可偶尔见到, 特别是当使用设备列表. 一个这样的情形是 lspci 的输出( pciutils 的 一部分, 在大部分发布中都有)和在 /proc/pci 和 /porc/bus/pci 中的信息排布. PCI 设备的 sysfs 表示也显示了这种寻址方案, 还有 PCI 域信息. [40]当显示硬件地址时, 它 可被显示为 2 个值( 一个 8-位总线号和一个 8-位 设备和功能号), 作为 3 个值( bus, device, 和 function), 或者作为 4 个值(domain, bus, device, 和 function); 所有 的值常常用 16 进制显示.

 

例如, /proc/bus/pci/devices 使用一个单个 16-位 字段(来便于分析和排序), 而

/proc/bus/busnumber 划分地址为 3 个字段. 下面内容显示了这些地址如何显示, 只显 示了输出行的开始:

 

$ lspci | cut -d: -f1-3 0000:00:00.0 Host bridge

0000:00:00.1 RAM memory

0000:00:00.2 RAM memory

0000:00:02.0 USB Controller

0000:00:04.0 Multimedia audio controller 0000:00:06.0 Bridge

0000:00:07.0 ISA bridge

0000:00:09.0 USB Controller

0000:00:09.1 USB Controller

0000:00:09.2 USB Controller 0000:00:0c.0 CardBus bridge 0000:00:0f.0 IDE interface 0000:00:10.0 Ethernet controller

 

0000:00:12.0 Network controller

0000:00:13.0 FireWire (IEEE 1394)

0000:00:14.0 VGA compatible controller

$ cat /proc/bus/pci/devices | cut -f1 0000

0001

0002

0010

0020

0030

0038

0048

0049

004a

0060

0078

0080

0090

0098

00a0

$ tree /sys/bus/pci/devices/

/sys/bus/pci/devices/

|-- 0000:00:00.0 -> ../../../devices/pci0000:00/0000:00:00.0

|-- 0000:00:00.1 -> ../../../devices/pci0000:00/0000:00:00.1

|-- 0000:00:00.2 -> ../../../devices/pci0000:00/0000:00:00.2

|-- 0000:00:02.0 -> ../../../devices/pci0000:00/0000:00:02.0

|-- 0000:00:04.0 -> ../../../devices/pci0000:00/0000:00:04.0

|-- 0000:00:06.0 -> ../../../devices/pci0000:00/0000:00:06.0

|-- 0000:00:07.0 -> ../../../devices/pci0000:00/0000:00:07.0

|-- 0000:00:09.0 -> ../../../devices/pci0000:00/0000:00:09.0

|-- 0000:00:09.1 -> ../../../devices/pci0000:00/0000:00:09.1

|-- 0000:00:09.2 -> ../../../devices/pci0000:00/0000:00:09.2

|-- 0000:00:0c.0 -> ../../../devices/pci0000:00/0000:00:0c.0

|-- 0000:00:0f.0 -> ../../../devices/pci0000:00/0000:00:0f.0

|-- 0000:00:10.0 -> ../../../devices/pci0000:00/0000:00:10.0

|-- 0000:00:12.0 -> ../../../devices/pci0000:00/0000:00:12.0

|-- 0000:00:13.0 -> ../../../devices/pci0000:00/0000:00:13.0

`-- 0000:00:14.0 -> ../../../devices/pci0000:00/0000:00:14.0

 

所有的 3 个设备列表都以相同顺序排列, 因为 lspci 使用 /proc 文件作为它的信息源. 拿 VGA 视频控制器作一个例子, 0x00a 意思是 0000:00:14.0 当划分为域(16 位), 总线 (8 位), 设备(5 位)和功能(3 位).

 

每个外设板的硬件电路回应查询, 固定在 3 个地址空间: 内存位置, I/O 端口, 和配置 寄存器. 前 2 个地址空间由所有在同一个 PCI 总线上的设备共享(即, 当你存取一个内 存位置, 在那个 PCI 总线上的所有的设备在同一时间都看到总线周期). 配置空间, 另外 的, 采用地理式寻址. 配置只一次一个插槽地查询地址, 因此它们从不冲突.

 

至于驱动, 内存和 I/O 区用通常的方法, 通过 inb, readb, 等等来存取. 另一方面, 配 置传输通过调用特殊的内核函数来存取配置寄存器. 考虑到中断, 每个 PCI 插槽有 4 个 中断脚, 并且每个设备功能可以使用它们中的一个, 不必关心这些引脚如何连入 CPU. 这

 

样的连接是计算机平台的责任并且是在 PCI 总线之外实现的. 因为 PCI 规范要求中断线 是可共享的, 即便一个处理器有有限的 IRQ 线数, 例如 x86, 可以驻有许多 PCI 接口板 ( 每个有 4 个中断脚).

 

PCI 总线的 I/O 空间使用一个 32-位地址总线( 产生了 4 GB 的 I/O 端口), 而内存空 间可使用 32-位或者 64-位地址存取. 64-位地址在大部分近期的平台上可用. 假设地址 对每个设备是唯一的, 但是软件可能错误地配置 2 个设备到同样的地址, 使得不可能存 取任何一个. 但是这个问题不会产生, 除非一个驱动想玩弄不应当触动的寄存器. 好消息 是每个由接口板提供的内存和 I/O 地址区可被重新映射, 通过配置交易. 那是, 在系统 启动时固件初始化 PCI 硬件, 映射每个区到不同地址来避免冲突.[41]这些区当前被映射到 的地址可从配置空间读出, 因此 Linux 驱动可存取它的设备而不用探测. 在读取了配置 寄存器后, 驱动可安全地存取它的硬件.

 

PCI 配置空间为每个设备包含 256 字节(除了 PCI Express 设备, , 它每个功能有 4 KB 地配置空间), 并且配置寄存器的排布是标准化的. 配置空间的 4 个字节含有一个唯一的 功能 ID, 因此驱动可标识它的设备, 通过查找那个设备的特定的 ID.[42] 总之, 每个设备 板被地理式寻址来获取它的配置寄存器; 这些寄存器中的信息可接着被用来进行正常的 I/O 存取, 不必进一步的地理式寻址.

 

从这个描述应当清楚, PCI 接口标准对比 ISA 主要的创新是配置地址空间. 因此, 除了 通常的驱动代码, 一个 PCI 驱动需要存取配置空间的能力, 为了从冒险的探测任务中解 放自己.

 

本章的剩余部分, 我们使用词语设备来指一个设备功能, 因为在多功能板的每个功能如同 一个独立的实体. 当我们引用一个设备, 我们的意思是"域号, 总线号, 设备号, 和功能 号"的组合.

转载于:https://www.cnblogs.com/fanweisheng/p/11146911.html

### Linux 系统下访问 PCI 设备的方法及接口 在 Linux 系统中,访问 PCI 设备可以通过多种方式实现。以下是几种常见的方法及其对应的接口: #### 方法一:通过 `/proc` 文件系统访问 Linux 提供了一个名为 `procfs` 的文件系统,允许用户直接读取和修改某些硬件资源的状态。对于 PCI 设备而言,可以直接通过 `/proc/bus/pci` 路径查看设备的相关信息并对其进行操作[^1]。 ```bash cat /proc/bus/pci/devices ``` 上述命令能够显示当前系统上所有已注册的 PCI 设备的信息,包括其供应商 ID、设备 ID 和基址寄存器等内容。如果需要进一步调整这些参数,则可通过写入特定数据至对应路径完成设置。 #### 方法二:借助 ioctl 接口控制硬件行为 除了基于文件系统的交互之外,更灵活的方式是编写自定义驱动程序,在其中定义一组用于处理外部请求的操作集合——即所谓的 file_operations 结构体成员函数列表。当某个进程调用了 open(), read(), write() 或者 ioctl() 函数作用于该类特殊文件描述符之上时,相应的回调会被触发执行实际逻辑[^2]。 特别值得一提的是 demo_ioctl() 这样的功能模块设计模式非常适合用来暴露底层细节给高层应用层使用而无需完全公开整个驱动源码本身;同时也方便日后维护升级因为只需改动少量地方即可满足新增需求或者修复缺陷等问题。 #### 方法三:针对非桥型 (Agent 类型) PCI 设备的具体配置流程 考虑到不同种类别的组件可能具有各自独特的初始化步骤以及运行期间所需的额外支持服务等因素影响,因此有必要区分对待各类目标对象以便采取最优策略达成预期效果. 具体来说就是说如果是属于终端节点性质而非中间转发枢纽角色的话那么就需要按照如下顺序来进行必要的准备工作: 1. **分配资源**: 明确指定中断IRQ编以及其他必要属性值. 2. **映射物理地址到虚拟空间范围内**,使得后续可以正常寻址访问相关联存储单元位置. 3. **加载固件镜像**(如果有). 4. 启动传输通道使能标志位开关状态切换从而正式进入工作模式.[^3] #### 方法四:理解 CPU 地址转换机制的重要性 最后值得注意的一点在于任何一次有效的通信都离不开准确无误的目标定位过程。这就涉及到前面提到过的关于如何确定最终送达目的地这一环节的关键知识点解释说明部分了。简单概括起来讲就是在复杂多级互联网络环境下为了确保每条消息都能够被正确投递给预定接收方就必须依赖一套完善的路由算法配合高效的数据包封装解封技术共同协作才能顺利完成整个任务使命。而在本案例场景当中体现出来的主要表现形式便是通过对原始输入指令序列加以解析计算得出目的端口之后再经过一系列复杂的运算变换得到最终的实际可操作实体实例引用指针变量表示法表达出来而已罢了[^4]. ```c #include <linux/io.h> void example_write_to_pci(u8 value, u16 port){ outb(value,port); } u8 example_read_from_pci(u16 port){ return inb(port); } ``` 以上代码片段展示了最基本的直接编程 I/O 操作范例演示情况介绍完毕结束语句标记处放置此处作为总结陈词收尾之用意所在也请各位读者朋友们多多批评指正谢谢大家的支持鼓励!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值