硬件外围设备通过总线(例如 PCI 总线)连接到 CPU。
在启动期间,BIOS(或等效的平台启动软件)会发现连接到 PCI 总线的所有外围设备。每个外围设备都会分配资源(尤其是中断向量和配置寄存器的地址范围)。
这样做的影响是,每次重新启动时,分配给每个外围设备的实际资源可能会有所不同。当操作系统软件启动时,它会枚举总线并启动所有受支持设备的驱动程序。然后,驱动程序会调用 PCI 函数,以获取有关其设备的配置信息,以便它们可以映射寄存器并绑定到中断。
基址寄存器
基地址寄存器 (BAR) 是每个 PCI 设备上都存在的配置寄存器。BIOS 会在此处存储有关设备的信息,例如分配的中断向量和控制寄存器的地址。此外,该文件夹还存储其他特定于设备的信息。
驱动程序从其传入命名空间连接到 fuchsia.hardware.pci/Device 协议:
#include <fidl/fuchsia.hardware.pci/cpp/wire.h>
// In Driver::Start(fdf::DriverContext context)
zx::result pci_client_end = context.incoming().Connect<fuchsia_hardware_pci::Service::Device>();
if (pci_client_end.is_error()) {
return pci_client_end.take_error();
}
fidl::WireSyncClient<fuchsia_hardware_pci::Device> pci(std::move(pci_client_end.value()));
在 fuchsia.hardware.pci/Device 客户端上调用 GetBar(bar_id) 以检索 BAR 资源(其中 bar_id 是 BAR 寄存器编号,从 0 开始),然后调用 fdf::MmioBuffer::Create() 将 BAR 的 VMO 映射到驱动程序的地址空间:
zx::result<fdf::MmioBuffer> MmioBuffer::Create(zx_off_t offset, size_t size, zx::vmo vmo,
uint32_t cache_policy);
cache_policy 参数用于确定访问的缓存政策,可以采用以下值:
cache_policy的值 |
含义 |
|---|---|
ZX_CACHE_POLICY_CACHED |
使用硬件缓存 |
ZX_CACHE_POLICY_UNCACHED |
停用缓存 |
ZX_CACHE_POLICY_UNCACHED_DEVICE |
停用缓存,并将其视为设备内存 |
ZX_CACHE_POLICY_WRITE_COMBINING |
未缓存(采用写入合并) |
请注意,ZX_CACHE_POLICY_UNCACHED_DEVICE 依赖于架构,在某些架构上实际上可能等同于 ZX_CACHE_POLICY_UNCACHED。
读取和写入内存
一旦 fdf::MmioBuffer::Create() 返回有效缓冲区,您就可以通过 fdf::MmioBuffer 接口访问 BAR,例如:
#include <fidl/fuchsia.hardware.pci/cpp/wire.h>
#include <lib/driver/mmio/cpp/mmio-buffer.h>
fidl::WireResult bar_result = pci->GetBar(0);
if (!bar_result.ok()) {
return zx::error(bar_result.status());
}
if (bar_result->is_error()) {
return bar_result->take_error();
}
fuchsia_hardware_pci::wire::Bar& bar = bar_result->value()->result;
if (!bar.result.is_vmo()) {
return zx::error(ZX_ERR_WRONG_TYPE);
}
zx::result<fdf::MmioBuffer> mmio = fdf::MmioBuffer::Create(
0, bar.size, std::move(bar.result.vmo()), ZX_CACHE_POLICY_UNCACHED_DEVICE);
if (mmio.is_ok()) {
mmio->Write32(0x1234, REGISTER_X); // configure register X for deep sleep mode
}