跳转到内容

Aros/Developer/PCIDriversDev

来自 Wikibooks,开放世界中的开放书籍
用于 Aros wikibook 的导航栏
Aros 用户
Aros 用户文档
Aros 用户常见问题解答
Aros 用户应用程序
Aros 用户 DOS Shell
Aros/User/AmigaLegacy
Aros 开发文档
Aros 开发人员文档
从 AmigaOS/SDL 移植软件
面向 Zune 初学者
Zune .MUI 类
面向 SDL 初学者
Aros 开发人员构建系统
特定平台
Aros x86 完整系统 HCL
Aros x86 音频/视频支持
Aros x86 网络支持
Aros Intel AMD x86 安装
Aros 存储支持 IDE SATA 等
Aros Poseidon USB 支持
x86-64 支持
Motorola 68k Amiga 支持
Linux 和 FreeBSD 支持
Windows Mingw 和 MacOSX 支持
Android 支持
Arm Raspberry Pi 支持
PPC Power Architecture
杂项
Aros 公共许可证

rom/hidds/hidd.pci 中的源代码

pci.hidd 在驻留模块的引导过程中被初始化。当 pci.hidd 初始化时,它会探测 PCI 总线以查找设备。pci.hidd 不是设备驱动程序维护程序。它仅维护 PCI 总线。此外,pci.hidd 以非常高的优先级启动,因此它只能初始化优先级高于自身驱动程序。

pci.hidd 是一个类集合,用于维护系统中可用的所有 PCI 设备。所有设备属性都可通过适当的 OOP_Object 属性获得,不应手动更改(尽管仍然可以通过 pcidriver 类的六个公共方法获得)。

如何请求 PCI 设备?

为了查询指定的 PCI 设备,需要使用主 PCI 类(IID_Hidd_PCI)的 moHidd_PCI_EnumDevices 方法。它接受两个参数。第一个是指向 struct Hook 的指针,定义了针对每个符合给定要求的 PCI 设备调用的回调函数。第二个参数(可以为 NULL)是指向 struct TagItem[] 的指针,定义了必须满足的要求。可以组合使用 VendorID、ProductID、RevisionID、Interface、Class、Subclass、SubsystemVendorID、SubsystemID(有关详细信息,请参见 include/hidd/pci.h)。如果给出 NULL,则回调函数将针对 pci 类看到的每个 PCI 设备调用。以下代码可用于查找 PCI 设备

/* This hook will be called for every PCI device that matches the given requirements */
AROS_UFH3(void, Callback,
    AROS_UFHA(struct Hook *,    hook,   A0),
    AROS_UFHA(OOP_Object *,     obj,    A2),
    AROS_UFHA(APTR,             msg,    A1))
{
    AROS_USERFUNC_INIT

    /* Do whatever here with the PCIDevice object stored in obj pointer */

    AROS_USERFUNC_EXIT
}

/* Hook defining our callback */
static const struct Hook PCIHook = {
    h_Entry : (APTR)Callback
};

void Query()
{
    OOP_Object *o;  /* Keep PCI class object */

    /* Get only VGA compatible video cards */
    struct TagItem tags[] = {
        { tHidd_PCI_Class,      3 },
        { tHidd_PCI_SubClass,   0 },
        { tHidd_PCI_Interface,  0 },
        { TAG_DONE, 0UL }
    };

    /* Create instance of pci class */
    o = OOP_NewObject(NULL, CLID_Hidd_PCI, NULL);
    if (o)
    {
        /* Enumerate through all PCI devices */
        HIDD_PCI_EnumDevices(o, &PCIHook, NULL);
        /* Enumerate through devices that met requirements only */
        HIDD_PCI_EnumDevices(o, &PCIHook, &tags);

        [do whatever you want with PCI devices]

        /* Don't need PCI object anymore */
        OOP_DisposeObject(o);
    }
}

简单、高效、美观:)

如何处理 PCI 设备对象?

一旦知道了指向 pci 设备对象的指针,就可以询问 PCI 设备的属性,以及更改一些设备属性。Bus、Dev 和 Sub 属性定义了 PCI 设备的物理地址,如处理此设备的总线驱动程序(作为 Driver 属性提供)所见。对于 PCI-to-PCI 桥接器(参见 isBridge 属性),还有一些其他属性可用(另一方面,一些其他属性,例如基本地址 2 到 5 不可用)。最常用的可能是

aHidd_PCIDevice_Base[0..5] - PCI base addresses of given device aHidd_PCIDevice_Size[0..5] - sizes of PCI memory/IO areas aHidd_PCIDevice_Type[0..5] - type of given area.

如果 Type 属性中的 ADDRB_IO 位被设置,则该区域为 IO 区域。否则,它是一个内存区域,可能是可预取内存(ADDRB_PREFETCH 位设置)。

此外,驱动程序可以检查给定 PCI 设备是否完全解码了 I/O 或 MEM(isIO、isMEM 属性),是否启用了 BusMaster(isMaster 属性),设备是否为 VGA 调色板更改嗅探 PCI 总线(paletteSnoop 属性)。最后,可以检查设备是否支持 66 MHz PCI 总线(is66MHz 属性)。

注意,根据驱动程序的要求,isIO、isMEM、isMaster 和 paletteSnoop 属性也可以被设置。

所有属性都可以通过 OOP_GetAttr 调用获得(唉,我们确实缺少 OOP_GetAttrs(obj, struct TagItem **attributes_to_get_with_one_call)!)),其中一些属性可以通过 OOP_SetAttrs 调用设置(有关详细信息,请参见 hidd/pci.h 包含文件)。请记住,在开始处理属性之前,必须获得 IID_Hidd_PCIDevice AttrBase(请不要忘记在不再需要它时释放它)。

PCIDriver 类(用户端)

PCIDevice 类的一个只读属性是 PCIDriver 类指针。它指向处理给定 PCI 设备对象的硬件驱动程序。如后文所述,系统中可能有多个驱动程序同时工作。

驱动程序类有一个重要的属性 - aHidd_PCIDriver_DirectBus。它是只读的,如果它被设置为 TRUE,则驱动程序处理直接映射到 CPU 空间的 PCI 总线。DirectBus 设备可能是由本机 AROS 处理的 PC 中典型的 PCI 总线。典型的间接 PCI 总线将是在 Linux 下处理的 PCI 总线(在 Linux 上托管的 AROS 上没有对 PCI 设备的物理直接访问)。根据 DirectBus 属性,可以使用或应该使用一些方法。

在处理非 DirectBus PCI 驱动程序时,可以使用 HIDD_PCIDriver_MapPCI 和 HIDD_PCIDriver_UnmapPCI 方法访问 PCI 设备的内存范围。第一个方法尝试将 PCI 内存空间映射到 CPU 内存空间(例如,在 Linux 的情况下使用 /dev/mem 上的 mmap),以便可以访问给定的 PCI 内存范围。UnmapPCI 方法释放之前使用此方法创建的映射。

此外,对于非 DirectBus PCI 驱动程序,可以使用 AllocPCIMem 和 FreePCIMem 来保留/释放 PCI 设备可访问的内存,并将其对齐到页边界。如果这些方法未实现或没有可用于 PCI 设备的内存,AllocPCIMem 将返回 (APTR)-1。

对于 DirectBus 设备,仍然可以使用上述调用方法。然后,MapPCI 等效于 HIDD_PCIDriver_PCItoCPU 调用,它只是将 PCI 设备看到的地址转换为 CPU 看到的地址。CPUtoPCI 向另一个方向工作。

驱动程序创建

为了编写 PCI 硬件驱动程序,必须创建一个从 CLID_Hidd_PCIDriver 类派生的类。这简化了驱动程序的工作,因为只需要实现几个方法:PCIDriver::New()

此方法应向 msg->attrList 添加一些属性,并将 ::New 消息传递给超类。aHidd_Name 和 aHidd_HardwareName 在这里受到欢迎。此外,如果驱动程序不在直接访问总线上工作,则应将 aHidd_PCIDriver_DirectBus 重置为 FALSE(否则,超类会将其设置为 TRUE)。

请注意,在最坏的情况下(作者不想提供 aHidd_Name 和 aHidd_HardwareName),可以跳过 ::New 的实现。PCIDriver::ReadConfigLong() 和 PCIDriver::WriteConfigLong()

这两个方法必须在驱动程序类中定义。否则,超类将抱怨错误消息。所有其他用于访问 PCI 配置空间(读/写字/字节)的方法都可以由驱动程序类实现,但不必实现。由于所有方法都是虚拟的,因此超类将完成魔法(它将使用 ReadConfigLong 和 WriteConfigLong 方法来以读写模式访问字和字节)。

此外,MapPCI/UnmapPCI 和 CPUtoPCI/PCItoCPU 可能需要重写(默认情况下,如果为间接总线,则始终返回 0xffffffff,如果为直接总线,则返回与给定地址相同的地址)。将驱动程序类添加到系统

成功创建驱动程序类后,可以将其指针传递给主 pci 类。可以通过以下方式完成(假设 cl 是指向新创建的驱动程序类的指针)

[...]
    struct pHidd_PCI_AddHardwareDriver msg;
    OOP_Object *pci;

    msg.driverClass = cl;
    msg.mID = OOP_GetMethodID(IID_Hidd_PCI, moHidd_PCI_AddHardwareDriver);

    pci = OOP_NewObject(NULL, CLID_Hidd_PCI, NULL);
    if (pci)
    {
        OOP_DoMethod(pci, (OOP_Msg)&msg);
        OOP_DisposeObject(pci);
    }
[...]

完成。然后,pci 子系统将使用传递的类指针(注意:由于直接传递类指针,驱动程序类不必是公共的)来扫描使用此硬件驱动程序处理的 PCI 总线。从这一点起,由新添加的驱动程序处理的 PCI 设备可用于任何用途。从系统中删除驱动程序类

驱动程序可以使用 RemHardwareDriver 调用请求从 PCI 子系统中删除。可以,但不必满足其查询。如果除希望被删除的驱动程序本身之外,还有其他用户使用 PCI 子系统,则驱动程序不会被删除。当 RemHardwareDriver 调用成功时,可以删除驱动程序类。

为什么需要这个可插拔驱动程序?

想象一个 PCI 设备(任何类型),它有自己的 PCI 总线。设备驱动程序了解此总线,并希望与其他驱动程序(系统用户)共享此总线。不幸的是,只有这个特定的设备驱动程序知道如何处理这个额外的 PCI 总线。当它创建一个知道如何与之通信的驱动程序类并将此驱动程序类添加到 pci 子系统时,此 PCI 总线将成为整个系统的一部分,从现在开始,任何人都可以访问它。

pciclass.c
pcideviceclass.c
pciutil.c

pciclass.c

[编辑 | 编辑源代码]
isPCIDeviceAvailable

PCI__Hidd_PCI__AddHardwareDriver
PCI__Hidd_PCI__EnumDevices
PCI__Hidd_PCI__RemHardwareDriver

PCI__Root__New
PCI__Root__Dispose
PCI_ExpungeClass
PCI_InitClass

pcideviceclass.c

[编辑 | 编辑源代码]
setLong
setWord
setByte
getLong
getWord
getByte

findCapabilityOffset

dispatch_generic
dispatch_base
dispatch_type
dispatch_size
dispatch_pci2pcibridge
dispatch_capability

PCIDev__Root__Get
PCIDev__Root__Set

pciutil.c

[编辑 | 编辑源代码]
getPCIClassDesc
sizePCIBaseReg

检测设备

[编辑 | 编辑源代码]

启用设备

[编辑 | 编辑源代码]

理解设备

[编辑 | 编辑源代码]

与总线无关的设备访问

[编辑 | 编辑源代码]

理解 PCI 配置空间

[编辑 | 编辑源代码]

首先,它使用 64 位 BAR 地址,而 PCI 不支持这些地址(或者我错了?)。如果我理解正确,根据 PCI 规范,一个 64 位 BAR 会占用下一个 BAR 的空间,并将它用作 64 位地址中的较高位。但是,Aros 没有意识到这一点,而是将下一个 BAR 视为一个新的 BAR。

其次,在 PCI.hidd 中,BAR 大小(32 位或 64 位)是在获取 BAR 地址之后检查的,因此从某种意义上说,它是反向进行的……因为大小(32 位或 64 位)只能在 sizePCIBaseReg 函数中检查,而该函数应该被分解以检测大小(32 位或 64 位)以及是 I/O 还是 MEM 以及使用的 I/O/MEM 大小。

据我所知,从 Amiga 软件的角度来看,Amiga PCI 硬件是一个大的(用于 PCI 的 256M+ 内存空间)I/O 自动配置设备。Zorro 板只能使用中断级别 2 或 6,所以我假设所有 PCI 中断始终路由到同一个物理 680x0 中断。netbsd amiga pci 驱动似乎解释了大多数这些内容(src/sys/arch/amiga/pci)。

华夏公益教科书