5. 通过sysfs访问PCI设备资源¶
sysfs 通常挂载在 /sys,它提供了在支持它的平台上访问 PCI 资源的能力。例如,某个总线可能看起来像这样
/sys/devices/pci0000:17
|-- 0000:17:00.0
| |-- class
| |-- config
| |-- device
| |-- enable
| |-- irq
| |-- local_cpus
| |-- remove
| |-- resource
| |-- resource0
| |-- resource1
| |-- resource2
| |-- revision
| |-- rom
| |-- subsystem_device
| |-- subsystem_vendor
| `-- vendor
`-- ...
最顶层元素描述了 PCI 域和总线号。在本例中,域号是 0000,总线号是 17(两个值都是十六进制)。该总线在槽位 0 包含一个单功能设备。为方便起见,域号和总线号被复制了一份。设备目录下有几个文件,每个文件都有自己的功能。
文件
功能
class
PCI 类 (ascii, 只读)
config
PCI 配置空间 (二进制, 读写)
device
PCI 设备 (ascii, 只读)
enable
设备是否已启用 (ascii, 读写)
irq
IRQ 号 (ascii, 只读)
local_cpus
邻近 CPU 掩码 (cpumask, 只读)
remove
从内核列表中移除设备 (ascii, 只写)
resource
PCI 资源主机地址 (ascii, 只读)
resource0..N
PCI 资源 N,如果存在 (二进制, mmap, 读写[1])
resource0_wc..N_wc
PCI WC 映射资源 N,如果可预取 (二进制, mmap)
revision
PCI 修订版本 (ascii, 只读)
rom
PCI ROM 资源,如果存在 (二进制, 只读)
subsystem_device
PCI 子系统设备 (ascii, 只读)
subsystem_vendor
PCI 子系统厂商 (ascii, 只读)
vendor
PCI 厂商 (ascii, 只读)
ro - read only file
rw - file is readable and writable
wo - write only file
mmap - file is mmapable
ascii - file contains ascii text
binary - file contains binary data
cpumask - file contains a cpumask type
只读文件用于提供信息,对其的写入将被忽略,'rom' 文件除外。可写文件可用于对设备执行操作(例如,更改配置空间、分离设备)。可 mmap 的文件通过对文件进行偏移量为 0 的 mmap 操作来获取,并可用于从用户空间进行实际的设备编程。请注意,某些平台不支持某些资源的 mmap,因此请务必检查任何 mmap 尝试的返回值。其中最值得注意的是 I/O 端口资源,它们也提供读写访问。
‘enable’ 文件提供了一个计数器,指示设备已被启用的次数。如果 ‘enable’ 文件当前返回 ‘4’,并向其中写入 ‘1’,它将返回 ‘5’。向其中写入 ‘0’ 将减少计数。然而,即使计数返回到 0,某些初始化可能也无法被撤销。
‘rom’ 文件很特殊,因为它提供对设备 ROM 文件的只读访问(如果可用)。但是,它默认是禁用的,因此应用程序在尝试读取调用之前应向文件写入字符串“1”以启用它,并在访问后通过写入“0”到文件来禁用它。请注意,设备必须处于启用状态,ROM 读取才能成功返回数据。如果驱动程序未绑定到设备,则可以使用上面提到的 ‘enable’ 文件来启用它。
‘remove’ 文件用于移除 PCI 设备,通过向该文件写入一个非零整数来实现。这不涉及任何热插拔功能,例如关闭设备电源。设备将从内核的 PCI 设备列表中移除,其 sysfs 目录将被移除,并且设备将从任何附加到它的驱动程序中移除。不允许移除 PCI 根总线。
5.1. 通过sysfs访问传统资源¶
如果底层平台支持,sysfs 也提供了传统 I/O 端口和 ISA 内存资源。它们位于 PCI 类层次结构中,例如:
/sys/class/pci_bus/0000:17/
|-- bridge -> ../../../devices/pci0000:17
|-- cpuaffinity
|-- legacy_io
`-- legacy_mem
legacy_io 文件是一个读写文件,应用程序可以使用它来执行传统端口 I/O。应用程序应打开该文件,寻址到所需的端口(例如 0x3e8),并执行 1、2 或 4 字节的读写操作。legacy_mem 文件应该使用与所需内存偏移量对应的偏移量进行 mmap,例如 VGA 帧缓冲区的 0xa0000。然后,应用程序可以简单地解引用返回的指针(当然,在检查错误之后)来访问传统内存空间。
5.2. 在新平台上支持 PCI 访问¶
为了支持上述 PCI 资源映射,Linux 平台代码应理想地定义 ARCH_GENERIC_PCI_MMAP_RESOURCE 并使用该功能的通用实现。为了支持通过 /proc/bus/pci 中的文件进行 mmap() 的历史接口,平台还可以设置 HAVE_PCI_MMAP。
或者,设置了 HAVE_PCI_MMAP 的平台可以提供自己的 pci_mmap_resource_range() 实现,而不是定义 ARCH_GENERIC_PCI_MMAP_RESOURCE。
支持 PCI 资源写合并映射的平台必须定义 arch_can_pci_mmap_wc(),该函数在允许写合并时在运行时应评估为非零值。支持 I/O 资源映射的平台类似地定义 arch_can_pci_mmap_io()。
传统资源受 HAVE_PCI_LEGACY 定义的保护。希望支持传统功能的平台应定义它并提供 pci_legacy_read、pci_legacy_write 和 pci_mmap_legacy_page_range 函数。