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 函数。