在 Windows 10 及更高版本中,提供了可从用户模式直接访问通用输入/输出 (GPIO)、集成电路间总线 (I2C)、串行外设接口 (SPI) 和通用异步收发器 (UART) 的 API。 开发板(如 Raspberry Pi 2)公开了这些连接的子集,使你能够扩展具有自定义线路的基本计算模块,以解决特定应用程序的问题。 这些低级别总线通常与其他关键载入函数共享,只有一部分 GPIO 引脚和总线在标头上公开。 为了保持系统稳定性,必须指定哪些引脚和总线可以安全地由用户模式应用程序进行修改。
本文档介绍如何在高级配置和 Power Interface(ACPI)中指定此配置,并提供用于验证配置是否正确指定的工具。
Important
本文档的受众是统一可扩展固件接口(UEFI)和 ACPI 开发人员。 假定读者熟悉 ACPI、ACPI 源语言 (ASL) 编写以及 SpbCx/GpioClx。
在 Windows 上,对低级总线的用户模式访问是通过现有的 GpioClx 和 SpbCx 框架实现的。 Windows IoT Core 和 Windows Enterprise 上提供名为 RhProxy 的新驱动程序向用户模式公开 GpioClx 和 SpbCx 资源。 若要启用这些 API,必须在 ACPI 表中为 rhproxy 声明一个设备节点,并包含每个需要向用户模式公开的 GPIO 资源和 SPB 资源。 本文档逐步讲解如何创作和验证 ASL。
ASL 示例
让我们来逐步了解 Raspberry Pi 2 上 rhproxy 设备节点的声明。 首先,在 \_SB 范围内创建 ACPI 设备声明。
Device(RHPX)
{
Name(_HID, "MSFT8000")
Name(_CID, "MSFT8000")
Name(_UID, 1)
}
- _HID – 硬件 ID。将此 ID 设置为特定于供应商的硬件 ID。
- _CID – 兼容 ID。必须是“MSFT8000”。
- _UID – 唯一 ID。设置为 1。
接下来,我们声明应公开给用户模式的每个 GPIO 和 SPB 资源。 声明资源的顺序很重要,因为资源的索引用于将属性与资源关联起来。 如果公开了多个 I2C 或 SPI 总线,则第一个声明的总线将被视为该类型的“默认”总线,并且将是 Windows.Devices.I2c.I2cController 和 Windows.Devices.Spi.SpiController 的 GetDefaultAsync() 方法返回的实例。
SPI
Raspberry Pi 有两辆公开的 SPI 巴士。 SPI0 有两个硬件芯片选择线,SPI1 有一个硬件芯片选择线。 对于每条总线上的每条片选线,都需要一个 SPISerialBus() 资源声明。 以下两个 SPISerialBus 资源声明适用于 SPI0 上的两个芯片选择行。 DeviceSelection 字段包含一个唯一值,驱动程序将其解释为硬件芯片选择行标识符。 放入 DeviceSelection 字段的确切值取决于驱动程序如何解释 ACPI 连接描述符的此字段。
注释
本文包含对术语“slave”的引用。Microsoft 不认可该术语,并且已在新产品和文档中停止使用它。 在从软件中删除该术语后,我们会将其从本文中删除。
// Index 0
SPISerialBus( // SCKL - GPIO 11 - Pin 23
// MOSI - GPIO 10 - Pin 19
// MISO - GPIO 9 - Pin 21
// CE0 - GPIO 8 - Pin 24
0, // Device selection (CE0)
PolarityLow, // Device selection polarity
FourWireMode, // wiremode
0, // databit len: placeholder
ControllerInitiated, // slave mode
0, // connection speed: placeholder
ClockPolarityLow, // clock polarity: placeholder
ClockPhaseFirst, // clock phase: placeholder
"\\_SB.SPI0", // ResourceSource: SPI bus controller name
0, // ResourceSourceIndex
// Resource usage
) // Vendor Data
// Index 1
SPISerialBus( // SCKL - GPIO 11 - Pin 23
// MOSI - GPIO 10 - Pin 19
// MISO - GPIO 9 - Pin 21
// CE1 - GPIO 7 - Pin 26
1, // Device selection (CE1)
PolarityLow, // Device selection polarity
FourWireMode, // wiremode
0, // databit len: placeholder
ControllerInitiated, // slave mode
0, // connection speed: placeholder
ClockPolarityLow, // clock polarity: placeholder
ClockPhaseFirst, // clock phase: placeholder
"\\_SB.SPI0", // ResourceSource: SPI bus controller name
0, // ResourceSourceIndex
// Resource usage
) // Vendor Data
软件如何知道这两个资源应该与同一总线相关联? 在 DSD 中指定了总线友好名称和资源索引之间的映射:
Package(2) { "bus-SPI-SPI0", Package() { 0, 1 }},
这会创建一个名为“SPI0”的总线,其中包含两条芯片选择线 - 资源索引 0 和 1。 还需要多个属性来声明 SPI 总线的功能。
Package(2) { "SPI0-MinClockInHz", 7629 },
Package(2) { "SPI0-MaxClockInHz", 125000000 },
MinClockInHz 和 MaxClockInHz 属性指定控制器支持的最小和最大时钟速度。 API 将阻止用户在此范围之外指定值。 时钟速度会在连接描述符的 _SPE 字段中传递给 SPB 驱动程序(ACPI 第 6.4.3.8.2.2 节)。
Package(2) { "SPI0-SupportedDataBitLengths", Package() { 8 }},
SupportedDataBitLengths 属性列出了控制器支持的数据位长度。 可以在逗号分隔的列表中指定多个值。 API 将阻止用户指定超出此列表范围的值。 数据位长度通过连接描述符的 _LEN 字段传递给你的 SPB 驱动程序(ACPI 第 6.4.3.8.2.2 节)。
可以将这些资源声明视为“模板”。 某些字段在系统启动时是固定的,而另一些字段是在运行时动态指定的。 SPISerialBus 描述符的以下字段是固定的:
- 设备选择
- 设备选择极性
- WireMode
- SlaveMode
- 资源源
以下字段是用户在运行时指定的值的占位符:
- 数据位长度
- 连接速度
- 时钟极性
- ClockPhase
由于 SPI1 仅包含单个芯片选择行,因此声明单个 SPISerialBus() 资源:
// Index 2
SPISerialBus( // SCKL - GPIO 21 - Pin 40
// MOSI - GPIO 20 - Pin 38
// MISO - GPIO 19 - Pin 35
// CE1 - GPIO 17 - Pin 11
1, // Device selection (CE1)
PolarityLow, // Device selection polarity
FourWireMode, // wiremode
0, // databit len: placeholder
ControllerInitiated, // slave mode
0, // connection speed: placeholder
ClockPolarityLow, // clock polarity: placeholder
ClockPhaseFirst, // clock phase: placeholder
"\\_SB.SPI1", // ResourceSource: SPI bus controller name
0, // ResourceSourceIndex
// Resource usage
) // Vendor Data
随附的友好名称声明(是必需的)在 DSD 中指定,并引用此资源声明的索引。
Package(2) { "bus-SPI-SPI1", Package() { 2 }},
这会创建一个名为“SPI1”的总线,并将其与资源索引 2 关联。
SPI 驱动程序要求
- 必须使用
SpbCx或与 SpbCx 兼容 - 必须已通过 MITT SPI 测试
- 必须支持 4Mhz 时钟速度
- 必须支持 8 位数据长度
- 必须支持所有 SPI 模式:0、1、2、3
I2C
接下来,声明 I2C 资源。 树莓派在引脚 3 和 5 上引出了一条 I2C 总线。
// Index 3
I2CSerialBus( // Pin 3 (GPIO2, SDA1), 5 (GPIO3, SCL1)
0xFFFF, // SlaveAddress: placeholder
, // SlaveMode: default to ControllerInitiated
0, // ConnectionSpeed: placeholder
, // Addressing Mode: placeholder
"\\_SB.I2C1", // ResourceSource: I2C bus controller name
,
,
) // VendorData
DSD 中指定了随附的友好名称声明,该声明是必需的:
Package(2) { "bus-I2C-I2C1", Package() { 3 }},
这会声明一个 I2C 总线,其友好名称为“I2C1”,该总线引用资源索引 3,它是上面声明的 I2CSerialBus() 资源的索引。
I2CSerialBus() 描述符的以下字段是固定的:
- SlaveMode
- 资源源
以下字段是用户在运行时指定的值的占位符。
- SlaveAddress
- 连接速度
- 寻址模式
I2C 驱动程序要求
- 必须使用 SpbCx 或与 SpbCx 兼容
- 必须已通过 MITT I2C 测试
- 必须支持 7 位寻址
- 必须支持 100kHz 时钟速度
- 必须支持 400kHz 时钟速度
GPIO
接下来,我们声明向用户模式公开的所有 GPIO 引脚。 我们在确定要公开的引脚时提供以下指导:
- 声明外露排针上的所有引脚。
- 声明与按钮和 LED 等常用板载功能相连的引脚。
- 请勿定义为系统功能保留的引脚,或未连接到任何对象的引脚。
下面的 ASL 代码块声明了两个引脚,即 GPIO4 和 GPIO5。 为简洁起见,此处未显示其他引脚。 附录 C 包含可用于生成 GPIO 资源的示例 powershell 脚本。
// Index 4 – GPIO 4
GpioIO(Shared, PullUp, , , , “\\_SB.GPI0”, , , , ) { 4 }
GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, “\\_SB.GPI0”,) { 4 }
// Index 6 – GPIO 5
GpioIO(Shared, PullUp, , , , “\\_SB.GPI0”, , , , ) { 5 }
GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, “\\_SB.GPI0”,) { 5 }
声明 GPIO 引脚时必须遵守以下要求:
- 仅支持内存映射 GPIO 控制器。 不支持通过 I2C/SPI 进行接口的 GPIO 控制器。 如果控制器驱动程序在CLIENT_CONTROLLER_BASIC_INFORMATION结构中设置 MemoryMappedController 标志以响应CLIENT_QueryControllerBasicInformation回调,则控制器驱动程序是内存映射控制器。
- 每个引脚都需要 GpioIO 和 GpioInt 资源。 GpioInt 资源必须紧接在 GpioIO 资源之后,并且必须指向同一引脚编号。
- GPIO 资源必须通过增加引脚数进行排序。
- 每个 GpioIO 和 GpioInt 资源都必须在引脚列表中仅包含一个引脚编号。
- 两个描述符的 ShareType 字段必须共享
- GpioInt 描述符的 EdgeLevel 字段必须是 Edge
- GpioInt 描述符的 ActiveLevel 字段必须是 ActiveBoth
- PinConfig 字段
- 必须在 GpioIO 和 GpioInt 描述符中保持相同
- 必须是 PullUp、PullDown 或 PullNone 之一。 它不能为 PullDefault。
- 拉取配置必须与引脚的开机状态匹配。 从开机状态将引脚置于指定的拉取模式不得更改引脚的状态。 例如,如果数据手册说明该引脚默认为上拉,请将 PinConfig 指定为 PullUp。
固件、UEFI 和驱动程序初始化代码在启动期间不应将引脚状态从其上电状态更改。 只有用户知道引脚上连接了什么,因此也只有用户才知道哪些状态转换是安全的。 必须记录每个引脚的开机状态,以便用户可以设计与引脚正确接口的硬件。 引脚在启动期间不得意外更改状态。
支持的驱动器模式
如果你的 GPIO 控制器除了支持高阻输入和 CMOS 输出外,还支持内置上拉和下拉电阻,则必须通过可选的 SupportedDriveModes 属性指定这一点。
Package (2) { “GPIO-SupportedDriveModes”, 0xf },
SupportedDriveModes 属性指示 GPIO 控制器支持哪些驱动器模式。 在上面的示例中,支持以下所有驱动器模式。 该属性是以下值的位掩码:
| 标志值 | 驱动模式 | Description |
|---|---|---|
| 0x1 | 输入高阻抗 | 该引脚支持高阻抗输入,对应 ACPI 中的“PullNone”值。 |
| 0x2 | 输入上拉 | 该引脚支持内置上拉电阻,对应 ACPI 中的“PullUp”值。 |
| 0x4 | InputPullDown | 该引脚支持内置下拉电阻,对应于 ACPI 中的“PullDown”值。 |
| 0x8 | OutputCmos | 引脚支持生成强高点和强低点(而不是打开排水)。 |
几乎所有 GPIO 控制器都支持 InputHighImpedance 和 OutputCmos。 如果未指定 SupportedDriveModes 属性,则这是默认值。
如果 GPIO 信号在到达外露排针之前先经过电平转换器,请声明 SOC 支持的驱动模式,即使该驱动模式在外部排针上无法观察到。 例如,如果某个引脚经过一个双向电平转换器后,在外部表现为带电阻上拉的开漏引脚,那么即使该引脚被配置为高阻输入,你也永远无法在外露排针上观察到高阻态。 你仍应声明该引脚支持高阻抗输入。
引脚编号
Windows支持两个引脚编号方案:
- 顺序引脚编号 – 用户将看到类似 0、1、2……这样的编号,直到外露引脚的数量。 0 是在 ASL 中声明的第一个 GpioIo 资源,1 是在 ASL 中声明的第二个 GpioIo 资源,依此说明。
- 原生引脚编号 – 用户看到的是 GpioIo 描述符中指定的引脚编号,例如 4、5、12、13……
Package (2) { “GPIO-UseDescriptorPinNumbers”, 1 },
UseDescriptorPinNumbers 属性指定 Windows 使用原生引脚编号,而不是顺序引脚编号。 如果未指定 UseDescriptorPinNumbers 属性或其值为零,则Windows默认为顺序引脚编号。
如果使用原生引脚编号,则还必须指定 PinCount 属性。
Package (2) { “GPIO-PinCount”, 54 },
PinCount 属性应与驱动程序的CLIENT_QueryControllerBasicInformation回调中通过 GpioClx 属性返回的值匹配。
选择与您的开发板现有已发布的文档最兼容的编号方案。 例如,Raspberry Pi 使用原生引脚编号,因为许多现有的引脚分布图都使用 BCM2835 引脚编号。 MinnowBoardMax 使用顺序引脚编号,因为现有的引脚分布图很少,而且在 200 多个引脚中,只有 10 个对外引出,因此采用顺序引脚编号可以简化开发人员的使用体验。 决定采用顺序引脚编号还是原生引脚编号时,应以减少开发人员的困惑为目标。
GPIO 驱动程序要求
- 必须使用
GpioClx - 必须映射到 SoC 片上内存
- 必须使用模拟的 ActiveBoth 中断处理
UART
如果 UART 驱动程序使用 SerCx 或 SerCx2,则可以使用 rhproxy 将驱动程序公开到用户模式。 创建类型的 GUID_DEVINTERFACE_COMPORT 设备接口的 UART 驱动程序不需要使用 rhproxy。 收件箱 Serial.sys 驱动程序是其中一种情况。
若要向用户模式公开 SerCx 样式的 UART,请按如下所示声明一个 UARTSerialBus 资源。
// Index 2
UARTSerialBus( // Pin 17, 19 of JP1, for SIO_UART2
115200, // InitialBaudRate: in bits ber second
, // BitsPerByte: default to 8 bits
, // StopBits: Defaults to one bit
0xfc, // LinesInUse: 8 1-bit flags to declare line enabled
, // IsBigEndian: default to LittleEndian
, // Parity: Defaults to no parity
, // FlowControl: Defaults to no flow control
32, // ReceiveBufferSize
32, // TransmitBufferSize
"\\_SB.URT2", // ResourceSource: UART bus controller name
,
,
,
)
只有 ResourceSource 字段是固定的,而所有其他字段都是用户在运行时指定的值的占位符。
随附的友好名称声明为:
Package(2) { "bus-UART-UART2", Package() { 2 }},
这会将友好名称“UART2”分配给控制器,即用户用于从用户模式访问总线的标识符。
运行时引脚复用
引脚复用是指同一个物理引脚可用于不同功能。 多个不同的芯片上外围设备(例如 I2C 控制器、SPI 控制器和 GPIO 控制器)可能会路由到 SOC 上的同一物理引脚。 多路复用器模块控制在任意时刻该引脚上哪种功能处于活动状态。 传统上,固件负责在启动时建立函数分配,并且此分配通过启动会话保持静态。 运行时引脚复用增加了在运行时重新配置引脚函数分配的功能。 允许用户在运行时选择引脚功能,可通过让用户快速重新配置开发板上的引脚来加快开发速度,并使硬件能够支持比静态配置更广泛的应用。
用户无需编写任何其他代码即可使用对 GPIO、I2C、SPI 和 UART 的复用支持。 当用户使用 OpenPin() 或 FromIdAsync() 打开 GPIO 或总线时,基础物理引脚会自动复用到请求的函数。 如果引脚已被其他函数使用,则 OpenPin() 或 FromIdAsync() 调用将失败。 当用户通过释放 GpioPin、I2cDevice、SpiDevice 或 SerialDevice 对象来关闭设备时,这些引脚会被释放,从而可在之后打开并用于其他功能。
Windows 在 GpioClx、SpbCx 和 SerCx 框架中内置了对引脚复用的支持。 这些框架协同工作,在访问 GPIO 引脚或总线时自动将引脚切换到正确的函数。 对引脚的访问通过仲裁机制进行管理,以防止多个客户端之间发生访问冲突。 除了这种内置支持之外,引脚复用的接口和协议具有通用性,并且可以扩展以支持更多设备和场景。
本文档首先介绍了引脚复用所涉及的基础接口和协议,然后介绍如何向 GpioClx、SpbCx 和 SerCx 控制器驱动程序添加对引脚复用的支持。
引脚复用体系结构
本部分介绍引脚复用所涉及的底层接口和协议。 使用 GpioClx/SpbCx/SerCx 驱动程序支持引脚复用,并不一定需要了解底层协议。 有关如何在 GpioClx/SpbCx/SerCx 驱动程序中支持引脚复用的详细信息,请参阅 在 GpioClx 客户端驱动程序中实现引脚复用支持 以及 在 SpbCx 和 SerCx 控制器驱动程序中使用复用支持。
引脚复用是通过多个组件的协同作用实现的。
- 引脚复用服务器 – 这些是控制引脚复用控制块的驱动程序。 引脚复用服务器通过来自客户端的以下请求接收引脚复用请求:用于保留复用资源的请求(通过 IRP_MJ_CREATE),以及用于切换引脚功能的请求(通过 *IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS-)。 引脚复用服务器通常是 GPIO 驱动程序,因为复用块有时是 GPIO 块的一部分。 即使复用模块是一个独立的外设,GPIO 驱动程序也是实现复用功能的合理位置。
- 引脚复用客户端——即使用引脚复用的驱动程序。 引脚复用客户端从 ACPI 固件接收引脚复用资源。 引脚复用资源是一类连接资源,由资源中枢管理。 引脚复用客户端通过打开该资源的句柄来预留引脚复用资源。 若要使硬件更改生效,客户端必须通过发送 IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS 请求来提交配置。 客户端通过关闭该句柄释放引脚复用资源,此时引脚复用配置恢复为默认状态。
- ACPI 固件——通过
MsftFunctionConfig()资源指定复用配置。 MsftFunctionConfig 资源用于指明客户端所需的引脚及其复用配置。 MsftFunctionConfig 资源包含函数号、拉取配置和引脚编号列表。 MsftFunctionConfig 资源作为硬件资源提供给引脚复用客户端,驱动程序会在其 PrepareHardware 回调中接收这些资源,其方式与接收 GPIO 和 SPB 连接资源类似。 客户端会收到一个资源中心 ID,可用其打开指向该资源的句柄。
必须向
asl.exe传递/MsftInternal命令行开关,才能编译包含MsftFunctionConfig()描述符的 ASL 文件,因为这些描述符目前正由 ACPI 工作委员会审议。 例如:asl.exe /MsftInternal dsdt.asl
引脚复用中涉及的操作序列如下所示。
- 客户端在其 EvtDevicePrepareHardware() 回调中从 ACPI 固件接收 MsftFunctionConfig 资源。
- 客户端使用资源中心辅助函数
RESOURCE_HUB_CREATE_PATH_FROM_ID()根据资源 ID 创建路径,然后使用 ZwCreateFile()、IoGetDeviceObjectPointer() 或 WdfIoTargetOpen() 打开该路径的句柄。 - 服务器使用资源中心帮助程序函数
RESOURCE_HUB_ID_FROM_FILE_NAME()从文件路径中提取资源中心 ID,然后查询资源中心以获取资源描述符。 - 服务器对描述符中的每个引脚执行共享仲裁,并完成IRP_MJ_CREATE请求。
- 客户端在所接收的句柄上发送 IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS 请求。
- 为了响应 IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS,服务器通过在每个引脚上激活指定的函数来执行硬件复用操作。
- 客户端将继续执行依赖于所请求的引脚复用配置的操作。
- 当客户端不再需要将引脚配置为复用状态时,它会关闭该句柄。
- 当句柄被关闭时,服务器会将引脚恢复到其初始状态。
引脚复用客户端的协议说明
本部分介绍客户端如何使用引脚复用功能。 这不适用于 SerCx 控制器驱动程序和 SpbCx 控制器驱动程序,因为框架代表控制器驱动程序实现此协议。
分析资源
WDF 驱动程序在其 MsftFunctionConfig()EvtDevicePrepareHardware() 例程中接收资源。 MsftFunctionConfig 资源可以通过以下字段进行标识:
CM_PARTIAL_RESOURCE_DESCRIPTOR::Type = CmResourceTypeConnection
CM_PARTIAL_RESOURCE_DESCRIPTOR::u.Connection.Class = CM_RESOURCE_CONNECTION_CLASS_FUNCTION_CONFIG
CM_PARTIAL_RESOURCE_DESCRIPTOR::u.Connection.Type = CM_RESOURCE_CONNECTION_TYPE_FUNCTION_CONFIG
EvtDevicePrepareHardware()例程可能会提取 MsftFunctionConfig 资源,如下所示:
EVT_WDF_DEVICE_PREPARE_HARDWARE evtDevicePrepareHardware;
_Use_decl_annotations_
NTSTATUS
evtDevicePrepareHardware (
WDFDEVICE WdfDevice,
WDFCMRESLIST ResourcesTranslated
)
{
PAGED_CODE();
LARGE_INTEGER connectionId;
ULONG functionConfigCount = 0;
const ULONG resourceCount = WdfCmResourceListGetCount(ResourcesTranslated);
for (ULONG index = 0; index < resourceCount; ++index) {
const CM_PARTIAL_RESOURCE_DESCRIPTOR* resDescPtr =
WdfCmResourceListGetDescriptor(ResourcesTranslated, index);
switch (resDescPtr->Type) {
case CmResourceTypeConnection:
switch (resDescPtr->u.Connection.Class) {
case CM_RESOURCE_CONNECTION_CLASS_FUNCTION_CONFIG:
switch (resDescPtr->u.Connection.Type) {
case CM_RESOURCE_CONNECTION_TYPE_FUNCTION_CONFIG:
switch (functionConfigCount) {
case 0:
// save the connection ID
connectionId.LowPart = resDescPtr->u.Connection.IdLowPart;
connectionId.HighPart = resDescPtr->u.Connection.IdHighPart;
break;
} // switch (functionConfigCount)
++functionConfigCount;
break; // CM_RESOURCE_CONNECTION_TYPE_FUNCTION_CONFIG
} // switch (resDescPtr->u.Connection.Type)
break; // CM_RESOURCE_CONNECTION_CLASS_FUNCTION_CONFIG
} // switch (resDescPtr->u.Connection.Class)
break;
} // switch
} // for (resource list)
if (functionConfigCount < 1) {
return STATUS_INVALID_DEVICE_CONFIGURATION;
}
// TODO: save connectionId in the device context for later use
return STATUS_SUCCESS;
}
保留和分配资源
当客户端想要复用引脚时,它会保留并提交 MsftFunctionConfig 资源。 以下示例演示客户端如何保留和提交 MsftFunctionConfig 资源。
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS AcquireFunctionConfigResource (
WDFDEVICE WdfDevice,
LARGE_INTEGER ConnectionId,
_Out_ WDFIOTARGET* ResourceHandlePtr
)
{
PAGED_CODE();
//
// Form the resource path from the connection ID
//
DECLARE_UNICODE_STRING_SIZE(resourcePath, RESOURCE_HUB_PATH_CHARS);
NTSTATUS status = RESOURCE_HUB_CREATE_PATH_FROM_ID(
&resourcePath,
ConnectionId.LowPart,
ConnectionId.HighPart);
if (!NT_SUCCESS(status)) {
return status;
}
//
// Create a WDFIOTARGET
//
WDFIOTARGET resourceHandle;
status = WdfIoTargetCreate(WdfDevice, WDF_NO_ATTRIBUTES, &resourceHandle);
if (!NT_SUCCESS(status)) {
return status;
}
//
// Reserve the resource by opening a WDFIOTARGET to the resource
//
WDF_IO_TARGET_OPEN_PARAMS openParams;
WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(
&openParams,
&resourcePath,
FILE_GENERIC_READ | FILE_GENERIC_WRITE);
status = WdfIoTargetOpen(resourceHandle, &openParams);
if (!NT_SUCCESS(status)) {
return status;
}
//
// Commit the resource
//
status = WdfIoTargetSendIoctlSynchronously(
resourceHandle,
WDF_NO_HANDLE, // WdfRequest
IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS,
nullptr, // InputBuffer
nullptr, // OutputBuffer
nullptr, // RequestOptions
nullptr); // BytesReturned
if (!NT_SUCCESS(status)) {
WdfIoTargetClose(resourceHandle);
return status;
}
//
// Pins were successfully muxed, return the handle to the caller
//
*ResourceHandlePtr = resourceHandle;
return STATUS_SUCCESS;
}
驱动程序应在其中一个上下文区域中存储 WDFIOTARGET,以便稍后可以关闭它。 当驱动程序准备释放复用配置时,应通过调用 WdfObjectDelete() 关闭资源句柄;如果打算重用 WDFIOTARGET,则应调用 WdfIoTargetClose()。
WdfObjectDelete(resourceHandle);
当客户端关闭其资源句柄时,引脚将复用回其初始状态,现在可以由其他客户端获取。
用于引脚复用服务器的协议说明
本节介绍引脚复用服务器如何向客户端提供其功能。 这不适用于 GpioClx 微型端口驱动程序,因为框架代表客户端驱动程序实现此协议。 有关如何在 GpioClx 客户端驱动程序中支持引脚复用的详细信息,请参阅 在 GpioClx 客户端驱动程序中实现引脚复用支持。
处理IRP_MJ_CREATE请求
当客户端想要预留引脚复用资源时,会打开该资源的句柄。 引脚复用服务器通过来自资源中心的重解析操作接收 IRP_MJ_CREATE 请求。
IRP_MJ_CREATE请求的尾随路径组件包含资源中心 ID,它是十六进制格式的 64 位整数。 服务器应使用 RESOURCE_HUB_ID_FROM_FILE_NAME() reshub.h 从文件名中提取资源中心 ID,并将 IOCTL_RH_QUERY_CONNECTION_PROPERTIES 发送到资源中心以获取 MsftFunctionConfig() 描述符。
服务器应验证描述符,并从描述符中提取共享模式和 PIN 列表。 然后应对这些引脚进行共享仲裁;如果成功,则应在完成请求之前将这些引脚标记为已预留。
如果引脚列表中的每个引脚的共享仲裁都成功,则共享仲裁整体上成功。 每个引脚应按如下所示进行仲裁:
- 如果 pin 尚未保留,则共享仲裁成功。
- 如果 pin 已保留为独占,则共享仲裁将失败。
- 如果 pin 已保留为共享,
- 并且传入请求是共享的,共享仲裁成功。
- 传入请求是独占的,共享仲裁失败。
如果共享仲裁失败,应使用 STATUS_GPIO_INCOMPATIBLE_CONNECT_MODE完成请求。 如果共享仲裁成功,则请求应使用 STATUS_SUCCESS完成。
请注意,传入请求的共享模式应取自 MsftFunctionConfig 描述符,而不是 IrpSp-Parameters.Create.ShareAccess>。
处理IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS请求
客户端通过打开句柄成功保留 MsftFunctionConfig 资源后,可以发送 IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS 以请求服务器执行实际的硬件复用操作。 当服务器收到IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS时,对于引脚列表中的每个引脚,应
- 将 PNP_FUNCTION_CONFIG_DESCRIPTOR 结构的 PinConfiguration 成员中指定的拉取模式设置为硬件。
- 将引脚复用到由PNP_FUNCTION_CONFIG_DESCRIPTOR结构的 FunctionNumber 成员指定的函数。
然后,服务器应使用 STATUS_SUCCESS完成请求。
FunctionNumber 的含义由服务器定义,据了解,MsftFunctionConfig 描述符是使用服务器如何解释此字段的知识创作的。
请记住,当句柄关闭时,服务器将不得不把引脚恢复到收到 IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS 时的配置,因此服务器可能需要在修改引脚之前先保存其状态。
处理IRP_MJ_CLOSE请求
当客户端不再需要复用资源时,它会关闭其句柄。 当服务器收到 IRP_MJ_CLOSE 请求时,它应将引脚还原到收到 IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS 时它们处于的状态。 如果客户端从未发送 IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS,则无需执行任何操作。 然后,服务器应将这些引脚标记为在共享仲裁方面可用,并以 STATUS_SUCCESS 完成该请求。 请务必将 IRP_MJ_CLOSE 处理与 IRP_MJ_CREATE 处理正确同步。
ACPI 表编写指南
本节介绍如何向客户端驱动程序提供多路复用资源。 请注意,需要Microsoft ASL 编译器内部版本 14327 或更高版本来编译包含 MsftFunctionConfig() 资源的表。
MsftFunctionConfig() 资源作为硬件资源提供给引脚复用客户端。
MsftFunctionConfig() 应向需要更改引脚复用配置的驱动程序提供这些资源,这类驱动程序通常是 SPB 驱动程序和串行控制器驱动程序;但不应向 SPB 和串行外设驱动程序提供这些资源,因为复用配置由控制器驱动程序处理。
该 MsftFunctionConfig() ACPI 宏的定义如下:
MsftFunctionConfig(Shared/Exclusive
PinPullConfig,
FunctionNumber,
ResourceSource,
ResourceSourceIndex,
ResourceConsumer/ResourceProducer,
VendorData) { Pin List }
- 共享/独占 – 若为独占模式,则同一时间此引脚只能被单个客户端获取。 如果共享,则多个共享客户端可以获取资源。 始终将此设置为独占,因为允许多个未协调的客户端访问可变资源可能会导致数据争用,因此无法预测的结果。
- PinPullConfig – 其中之一
- PullDefault – 使用 SoC 定义的上电默认上下拉配置
- PullUp – 启用上拉电阻
- PullDown – 启用下拉电阻
- PullNone – 禁用所有上拉/下拉电阻
- FunctionNumber – 要写入多路复用器的函数编号。
- ResourceSource – 引脚复用服务器的 ACPI 命名空间路径
- ResourceSourceIndex – 将此设置为 0
- ResourceConsumer/ResourceProducer – 将其设置为 ResourceConsumer
- VendorData – 可选的二进制数据,其含义由引脚复用服务器定义。 这通常应留空
- 引脚列表 – 以逗号分隔的引脚编号列表,配置适用于这些引脚。 当引脚复用服务器是 GpioClx 驱动程序时,这些是 GPIO 引脚编号,其含义与 GpioIo 描述符中的引脚号相同。
以下示例演示如何向 I2C 控制器驱动程序提供 MsftFunctionConfig() 资源。
Device(I2C1)
{
Name(_HID, "BCM2841")
Name(_CID, "BCMI2C")
Name(_UID, 0x1)
Method(_STA)
{
Return(0xf)
}
Method(_CRS, 0x0, NotSerialized)
{
Name(RBUF, ResourceTemplate()
{
Memory32Fixed(ReadWrite, 0x3F804000, 0x20)
Interrupt(ResourceConsumer, Level, ActiveHigh, Shared) { 0x55 }
MsftFunctionConfig(Exclusive, PullUp, 4, "\\_SB.GPI0", 0, ResourceConsumer, ) { 2, 3 }
})
Return(RBUF)
}
}
除了控制器驱动程序通常需要的内存和中断资源外,还指定了资源 MsftFunctionConfig() 。 此资源使 I2C 控制器驱动程序能够将引脚 2 和 3(由 \_SB.GPIO0 处的设备节点管理)配置为功能 4,并启用上拉电阻。
支持 GpioClx 客户端驱动程序中的复用支持
GpioClx 具有对引脚复用的内置支持。 GpioClx 微型端口驱动程序(也称为“GpioClx 客户端驱动程序”),驱动 GPIO 控制器硬件。 自 Windows 10 内部版本 14327 起,GpioClx 微型端口驱动程序可以通过实现两个新的 DDI 来增加对引脚复用的支持:
- CLIENT_ConnectFunctionConfigPins – 由
GpioClx调用,以命令微型端口驱动程序应用指定的复用配置。 - CLIENT_DisconnectFunctionConfigPins – 由
GpioClx调用,用于命令微型端口驱动程序还原多路复用配置。
有关这些例程的说明,请参阅 GpioClx 事件回调函数 。
除了这两个新的 DDI 之外,还应针对现有的 DDI 进行审核,以确认其是否与引脚复用兼容:
- CLIENT_ConnectIoPins/CLIENT_ConnectInterrupt – GpioClx 调用 CLIENT_ConnectIoPins,以指示微型端口驱动程序将一组引脚配置为 GPIO 输入或输出。 GPIO 与 MsftFunctionConfig 是互斥的,这意味着同一个引脚绝不会同时连接到 GPIO 和 MsftFunctionConfig。 由于引脚的默认功能不要求必须是 GPIO,因此在调用 ConnectIoPins 时,该引脚未必已复用为 GPIO。 ConnectIoPins 需要执行使引脚做好用于 GPIO 输入/输出所需的所有操作,包括引脚复用操作。 CLIENT_ConnectInterrupt 的行为应该类似,因为中断可以视为 GPIO 输入的特殊情况。
- CLIENT_DisconnectIoPins/CLIENT_DisconnectInterrupt – 除非指定 PreserveConfiguration 标志,否则这些例程应返回调用 CLIENT_ConnectIoPins/CLIENT_ConnectInterrupt 时它们处于的状态。 除了将引脚的方向还原到其默认状态之外,微型端口还应将每个引脚的复用状态还原为调用_Connect例程时的状态。
例如,假设某个引脚的默认复用功能配置为 UART,且该引脚也可用作 GPIO。 当调用CLIENT_ConnectIoPins来连接 GPIO 的引脚时,它应将引脚复用到 GPIO,并在CLIENT_DisconnectIoPins中,将引脚复用回 UART。 通常,Disconnect 例程应撤消 Connect 例程执行的操作。
支持 SpbCx 和 SerCx 控制器驱动程序中的复用
自 Windows 10 内部版本 14327 起,SpbCx 和 SerCx 框架内置了对引脚复用的支持,使 SpbCx 和 SerCx 控制器驱动程序无需对其自身进行任何代码更改即可充当引脚复用客户端。 由此推及,任何连接到启用了引脚复用功能的 SpbCx/SerCx 控制器驱动程序的 SpbCx/SerCx 外设驱动程序,都会触发引脚复用操作。
下图显示了每个组件之间的依赖关系。 可以看到,引脚复用使 SerCx 和 SpbCx 控制器驱动程序依赖于 GPIO 驱动程序,而后者通常负责执行该复用功能。
在设备初始化时, SpbCx 和 SerCx 框架将分析作为硬件资源提供给设备的所有 MsftFunctionConfig() 资源。 然后,SpbCx/SerCx 按需获取并释放引脚复用资源。
SpbCx 在其 IRP_MJ_CREATE 处理程序中应用引脚复用配置,就在调用客户端驱动程序的 EvtSpbTargetConnect() 回调之前。 如果无法应用复用配置,则不会调用控制器驱动程序的 EvtSpbTargetConnect() 回调函数。 因此,SPB 控制器驱动程序可能假定在调用 SPB 函数时 EvtSpbTargetConnect() 将引脚复用到 SPB 函数。
SpbCx 在其 IRP_MJ_CLOSE 处理程序中,在调用控制器驱动程序的 EvtSpbTargetDisconnect() 回调之后立即恢复引脚复用配置。 结果是,每当外围设备驱动程序打开 SPB 控制器驱动程序的句柄时,引脚都会切换为 SPB 功能;而当外围设备驱动程序关闭该句柄时,这些引脚就会切换为非 SPB 功能。
SerCx 行为类似。
SerCx在调用控制器驱动程序的 MsftFunctionConfig() 回调之前,获取其IRP_MJ_CREATE处理程序中的所有资源,并在调用控制器驱动程序的 EvtSerCx2FileClose 回调之后释放其IRP_MJ_CLOSE处理程序中的所有资源。
动态引脚复用对 SerCx 和 SpbCx 控制器驱动程序的影响在于,它们必须能够容忍引脚在某些时候被复用为其他功能,从而不再用于 SPB/UART 功能。 控制器驱动程序需要假定,在调用 EvtSpbTargetConnect() 或 EvtSerCx2FileOpen() 之前,引脚不会被复用。 在以下回调期间,不需要将引脚复用到 SPB/UART 函数。 以下不是完整列表,而是表示控制器驱动程序实现的最常见 PNP 例程。
- DriverEntry
- EvtDriverDeviceAdd
- EvtDevicePrepareHardware/EvtDeviceReleaseHardware
- EvtDeviceD0Entry/EvtDeviceD0Exit
验证
准备好测试 rhproxy 后,使用以下分步过程会很有帮助。
- 验证每个
SpbCx、GpioClx和SerCx控制器驱动程序是否已正确加载并正常运行 - 验证系统中是否存在
rhproxy。 某些版本的Windows没有它。 - 使用
ACPITABL.dat编译并加载你的 rhproxy 节点 -
rhproxy验证设备节点是否存在 - 验证
rhproxy是否正在加载并启动 - 验证预期设备是否公开给用户模式
- 验证是否可以从命令行与每个设备交互
- 验证是否可以从 UWP 应用与每个设备交互
- 运行 HLK 测试
验证控制器驱动程序
由于 rhproxy 向用户模式公开系统上的其他设备,因此仅在这些设备已正常工作时才有效。 第一步是验证这些设备(要公开的 I2C、SPI、GPIO 控制器)是否已正常工作。
在命令提示符下,运行
devcon status *
查看输出并验证是否启动了所有感兴趣的设备。 如果设备有问题代码,则需要排查设备未加载的原因。 所有设备都应在初始平台启动期间启用。 故障排除 SpbCx、 GpioClx或 SerCx 控制器驱动程序超出了本文档的范围。
验证系统上是否存在 rhproxy
rhproxy验证系统上是否存在该服务。
reg query HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\rhproxy
如果 reg 键不存在,则系统上不存在 rhproxy。 Rhproxy 存在于 IoT Core 的所有内部版本和 Windows Enterprise 内部版本 15063 及更高版本中。
使用 ACPITABL.dat 编译和加载 ASL
现在,你已编写了 rhproxy ASL 节点,现在可以编译并加载它了。 可以将 rhproxy 节点编译为可追加到系统 ACPI 表的独立 AML 文件。 或者,如果有权访问系统的 ACPI 源,可以直接将 rhproxy 节点插入平台的 ACPI 表。 但是,在初始调试阶段,使用 ACPITABL.dat 可能更容易。
创建名为 yourboard.asl 的文件,并将 RHPX 设备节点置于 DefinitionBlock 中:
DefinitionBlock ("ACPITABL.dat", "SSDT", 1, "MSFT", "RHPROXY", 1) { Scope (\_SB) { Device(RHPX) { ... } } }下载 WDK,并在
C:\Program Files (x86)\Windows Kits\10\Tools\x64\ACPIVerify查找asl.exe运行以下命令以生成ACPITABL.dat:
asl.exe yourboard.asl将生成的ACPITABL.dat文件复制到受测系统上的 c:\windows\system32。
在受测试的系统上启用测试签名:
bcdedit /set testsigning on重新启动受测试的系统。 系统将ACPITABL.dat中定义的 ACPI 表追加到系统固件表。
验证 rhproxy 设备节点是否存在
运行以下命令来枚举 rhproxy 设备节点。
devcon status *msft8000
devcon 的输出应指示设备存在。 如果设备节点不存在,则 ACPI 表未成功添加到系统。
验证 rhproxy 是否正在加载和启动
检查 rhproxy 的状态:
devcon status *msft8000
如果输出指示已启动 rhproxy,则 rhproxy 已加载并成功启动。 如果看到问题代码,则需要进行调查。 一些常见问题代码包括:
- 问题 51 -
CM_PROB_WAITING_ON_DEPENDENCY系统未启动 rhproxy,因为其中一个依赖项无法加载。 这意味着传递给 rhproxy 的资源指向无效的 ACPI 节点或目标设备未启动。 首先,仔细检查所有设备是否都成功运行(请参阅上面的“验证控制器驱动程序”。 然后,再次仔细检查你的 ASL,并确保所有资源路径(例如\_SB.I2C1)都是正确的,并且指向你的 DSDT 中的有效节点。 - 问题 10 -
CM_PROB_FAILED_STARTRhproxy 无法启动,很可能是因为资源分析问题。 检查 ASL,并再次核对 DSD 中的资源索引,同时确认 GPIO 资源是否按引脚编号递增顺序指定。
验证预期设备是否公开给用户模式
现在,rhproxy 正在运行,它应该已创建可由用户模式访问的设备接口。 我们将使用多个命令行工具来枚举设备并查看它们是否存在。
https://github.com/ms-iot/samples克隆存储库并生成 GpioTestTool、I2cTestTool和SpiTestToolMincomm示例。 将工具复制到受测设备,并使用以下命令枚举设备。
I2cTestTool.exe -list
SpiTestTool.exe -list
GpioTestTool.exe -list
MinComm.exe -list
你应会看到已列出的设备及其易记名称。 如果看不到正确的设备和友好名称,请仔细检查 ASL。
验证命令行上的每个设备
下一步是使用命令行工具打开设备并与之交互。
I2CTestTool 示例:
I2cTestTool.exe 0x55 I2C1
> write {1 2 3}
> read 3
> writeread {1 2 3} 3
SpiTestTool 示例:
SpiTestTool.exe -n SPI1
> write {1 2 3}
> read 3
GpioTestTool 示例:
GpioTestTool.exe 12
> setdrivemode output
> write 0
> write 1
> setdrivemode input
> read
> interrupt on
> interrupt off
MinComm(串行)示例。 在运行之前将 Rx 连接到 Tx:
MinComm "\\?\ACPI#FSCL0007#3#{86e0d1e0-8089-11d0-9ce4-08003e301f73}\0000000000000006"
(type characters and see them echoed back)
验证 UWP 应用中的每个设备
使用以下示例验证设备能否在 UWP 中正常工作。
运行 HLK 测试
下载 硬件实验室工具包 (HLK)。 以下测试可用:
- GPIO WinRT 功能和压力测试
- I2C WinRT 写入测试(需要 EEPROM)
- I2C WinRT 读取测试(需要 EEPROM)
- I2C WinRT 不存在从属地址测试
- I2C WinRT 高级功能测试(需要 mbed LPC1768)
- SPI WinRT 时钟频率验证测试(需要 mbed LPC1768)
- SPI WinRT IO 传输测试(需要 mbed LPC1768)
- SPI WinRT 步幅验证测试
- SPI WinRT 传输间隙检测测试(需要 mbed LPC1768)
在 HLK 管理器中选择 rhproxy 设备节点时,将自动选择适用的测试。
在 HLK 管理器中,选择“资源中心代理设备”:
然后单击“测试”选项卡,然后选择“I2C WinRT”、“Gpio WinRT”和“Spi WinRT”测试。
单击“运行已选中”。 可通过右键单击测试并单击“测试说明”来获取有关每个测试的更多文档。
资源
- ACPI 5.0 规范
- Asl.exe (Microsoft ASL 编译器)
- Windows.Devices.Gpio
- Windows.Devices.I2c
- Windows.Devices.Spi
- Windows.Devices.SerialCommunication
- 测试编写和执行框架 (TAEF)
- SpbCx
- GpioClx
- SerCx
- MITT I2C 测试
- GpioTestTool
- I2cTestTool
- SpiTestTool
- MinComm (Serial)
- 硬件实验室工具包 (HLK)
附录
附录 A - Raspberry Pi ASL 列表
DefinitionBlock ("ACPITABL.dat", "SSDT", 1, "MSFT", "RHPROXY", 1)
{
Scope (\_SB)
{
//
// RHProxy Device Node to enable WinRT API
//
Device(RHPX)
{
Name(_HID, "MSFT8000")
Name(_CID, "MSFT8000")
Name(_UID, 1)
Name(_CRS, ResourceTemplate()
{
// Index 0
SPISerialBus( // SCKL - GPIO 11 - Pin 23
// MOSI - GPIO 10 - Pin 19
// MISO - GPIO 9 - Pin 21
// CE0 - GPIO 8 - Pin 24
0, // Device selection (CE0)
PolarityLow, // Device selection polarity
FourWireMode, // wiremode
0, // databit len: placeholder
ControllerInitiated, // slave mode
0, // connection speed: placeholder
ClockPolarityLow, // clock polarity: placeholder
ClockPhaseFirst, // clock phase: placeholder
"\\_SB.SPI0", // ResourceSource: SPI bus controller name
0, // ResourceSourceIndex
// Resource usage
) // Vendor Data
// Index 1
SPISerialBus( // SCKL - GPIO 11 - Pin 23
// MOSI - GPIO 10 - Pin 19
// MISO - GPIO 9 - Pin 21
// CE1 - GPIO 7 - Pin 26
1, // Device selection (CE1)
PolarityLow, // Device selection polarity
FourWireMode, // wiremode
0, // databit len: placeholder
ControllerInitiated, // slave mode
0, // connection speed: placeholder
ClockPolarityLow, // clock polarity: placeholder
ClockPhaseFirst, // clock phase: placeholder
"\\_SB.SPI0", // ResourceSource: SPI bus controller name
0, // ResourceSourceIndex
// Resource usage
) // Vendor Data
// Index 2
SPISerialBus( // SCKL - GPIO 21 - Pin 40
// MOSI - GPIO 20 - Pin 38
// MISO - GPIO 19 - Pin 35
// CE1 - GPIO 17 - Pin 11
1, // Device selection (CE1)
PolarityLow, // Device selection polarity
FourWireMode, // wiremode
0, // databit len: placeholder
ControllerInitiated, // slave mode
0, // connection speed: placeholder
ClockPolarityLow, // clock polarity: placeholder
ClockPhaseFirst, // clock phase: placeholder
"\\_SB.SPI1", // ResourceSource: SPI bus controller name
0, // ResourceSourceIndex
// Resource usage
) // Vendor Data
// Index 3
I2CSerialBus( // Pin 3 (GPIO2, SDA1), 5 (GPIO3, SCL1)
0xFFFF, // SlaveAddress: placeholder
, // SlaveMode: default to ControllerInitiated
0, // ConnectionSpeed: placeholder
, // Addressing Mode: placeholder
"\\_SB.I2C1", // ResourceSource: I2C bus controller name
,
,
) // VendorData
// Index 4 - GPIO 4 -
GpioIO(Shared, PullUp, , , , "\\_SB.GPI0", , , , ) { 4 }
GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, "\\_SB.GPI0",) { 4 }
// Index 6 - GPIO 5 -
GpioIO(Shared, PullUp, , , , "\\_SB.GPI0", , , , ) { 5 }
GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, "\\_SB.GPI0",) { 5 }
// Index 8 - GPIO 6 -
GpioIO(Shared, PullUp, , , , "\\_SB.GPI0", , , , ) { 6 }
GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, "\\_SB.GPI0",) { 6 }
// Index 10 - GPIO 12 -
GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 12 }
GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 12 }
// Index 12 - GPIO 13 -
GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 13 }
GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 13 }
// Index 14 - GPIO 16 -
GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 16 }
GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 16 }
// Index 16 - GPIO 18 -
GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 18 }
GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 18 }
// Index 18 - GPIO 22 -
GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 22 }
GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 22 }
// Index 20 - GPIO 23 -
GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 23 }
GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 23 }
// Index 22 - GPIO 24 -
GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 24 }
GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 24 }
// Index 24 - GPIO 25 -
GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 25 }
GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 25 }
// Index 26 - GPIO 26 -
GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 26 }
GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 26 }
// Index 28 - GPIO 27 -
GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 27 }
GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 27 }
// Index 30 - GPIO 35 -
GpioIO(Shared, PullUp, , , , "\\_SB.GPI0", , , , ) { 35 }
GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, "\\_SB.GPI0",) { 35 }
// Index 32 - GPIO 47 -
GpioIO(Shared, PullUp, , , , "\\_SB.GPI0", , , , ) { 47 }
GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, "\\_SB.GPI0",) { 47 }
})
Name(_DSD, Package()
{
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package()
{
// Reference http://www.raspberrypi.org/documentation/hardware/raspberrypi/spi/README.md
// SPI 0
Package(2) { "bus-SPI-SPI0", Package() { 0, 1 }}, // Index 0 & 1
Package(2) { "SPI0-MinClockInHz", 7629 }, // 7629 Hz
Package(2) { "SPI0-MaxClockInHz", 125000000 }, // 125 MHz
Package(2) { "SPI0-SupportedDataBitLengths", Package() { 8 }}, // Data Bit Length
// SPI 1
Package(2) { "bus-SPI-SPI1", Package() { 2 }}, // Index 2
Package(2) { "SPI1-MinClockInHz", 30518 }, // 30518 Hz
Package(2) { "SPI1-MaxClockInHz", 125000000 }, // 125 MHz
Package(2) { "SPI1-SupportedDataBitLengths", Package() { 8 }}, // Data Bit Length
// I2C1
Package(2) { "bus-I2C-I2C1", Package() { 3 }},
// GPIO Pin Count and supported drive modes
Package (2) { "GPIO-PinCount", 54 },
Package (2) { "GPIO-UseDescriptorPinNumbers", 1 },
Package (2) { "GPIO-SupportedDriveModes", 0xf }, // InputHighImpedance, InputPullUp, InputPullDown, OutputCmos
}
})
}
}
}
附录 B - MinnowBoardMax ASL 列表
另请参阅 MinnowBoard Max 引脚映射
DefinitionBlock ("ACPITABL.dat", "SSDT", 1, "MSFT", "RHPROXY", 1)
{
Scope (\_SB)
{
Device(RHPX)
{
Name(_HID, "MSFT8000")
Name(_CID, "MSFT8000")
Name(_UID, 1)
Name(_CRS, ResourceTemplate()
{
// Index 0
SPISerialBus( // Pin 5, 7, 9 , 11 of JP1 for SIO_SPI
1, // Device selection
PolarityLow, // Device selection polarity
FourWireMode, // wiremode
8, // databit len
ControllerInitiated, // slave mode
8000000, // Connection speed
ClockPolarityLow, // Clock polarity
ClockPhaseSecond, // clock phase
"\\_SB.SPI1", // ResourceSource: SPI bus controller name
0, // ResourceSourceIndex
ResourceConsumer, // Resource usage
JSPI, // DescriptorName: creates name for offset of resource descriptor
) // Vendor Data
// Index 1
I2CSerialBus( // Pin 13, 15 of JP1, for SIO_I2C5 (signal)
0xFF, // SlaveAddress: bus address
, // SlaveMode: default to ControllerInitiated
400000, // ConnectionSpeed: in Hz
, // Addressing Mode: default to 7 bit
"\\_SB.I2C6", // ResourceSource: I2C bus controller name (For MinnowBoard Max, hardware I2C5(0-based) is reported as ACPI I2C6(1-based))
,
,
JI2C, // Descriptor Name: creates name for offset of resource descriptor
) // VendorData
// Index 2
UARTSerialBus( // Pin 17, 19 of JP1, for SIO_UART2
115200, // InitialBaudRate: in bits ber second
, // BitsPerByte: default to 8 bits
, // StopBits: Defaults to one bit
0xfc, // LinesInUse: 8 1-bit flags to declare line enabled
, // IsBigEndian: default to LittleEndian
, // Parity: Defaults to no parity
, // FlowControl: Defaults to no flow control
32, // ReceiveBufferSize
32, // TransmitBufferSize
"\\_SB.URT2", // ResourceSource: UART bus controller name
,
,
UAR2, // DescriptorName: creates name for offset of resource descriptor
)
// Index 3
GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO2",) {0} // Pin 21 of JP1 (GPIO_S5[00])
// Index 4
GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO2",) {0}
// Index 5
GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO2",) {1} // Pin 23 of JP1 (GPIO_S5[01])
// Index 6
GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO2",) {1}
// Index 7
GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO2",) {2} // Pin 25 of JP1 (GPIO_S5[02])
// Index 8
GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO2",) {2}
// Index 9
UARTSerialBus( // Pin 6, 8, 10, 12 of JP1, for SIO_UART1
115200, // InitialBaudRate: in bits ber second
, // BitsPerByte: default to 8 bits
, // StopBits: Defaults to one bit
0xfc, // LinesInUse: 8 1-bit flags to declare line enabled
, // IsBigEndian: default to LittleEndian
, // Parity: Defaults to no parity
FlowControlHardware, // FlowControl: Defaults to no flow control
32, // ReceiveBufferSize
32, // TransmitBufferSize
"\\_SB.URT1", // ResourceSource: UART bus controller name
,
,
UAR1, // DescriptorName: creates name for offset of resource descriptor
)
// Index 10
GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {62} // Pin 14 of JP1 (GPIO_SC[62])
// Index 11
GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {62}
// Index 12
GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {63} // Pin 16 of JP1 (GPIO_SC[63])
// Index 13
GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {63}
// Index 14
GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {65} // Pin 18 of JP1 (GPIO_SC[65])
// Index 15
GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {65}
// Index 16
GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {64} // Pin 20 of JP1 (GPIO_SC[64])
// Index 17
GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {64}
// Index 18
GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {94} // Pin 22 of JP1 (GPIO_SC[94])
// Index 19
GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {94}
// Index 20
GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {95} // Pin 24 of JP1 (GPIO_SC[95])
// Index 21
GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {95}
// Index 22
GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {54} // Pin 26 of JP1 (GPIO_SC[54])
// Index 23
GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {54}
})
Name(_DSD, Package()
{
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package()
{
// SPI Mapping
Package(2) { "bus-SPI-SPI0", Package() { 0 }},
Package(2) { "SPI0-MinClockInHz", 100000 },
Package(2) { "SPI0-MaxClockInHz", 15000000 },
// SupportedDataBitLengths takes a list of support data bit length
// Example : Package(2) { "SPI0-SupportedDataBitLengths", Package() { 8, 7, 16 }},
Package(2) { "SPI0-SupportedDataBitLengths", Package() { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 }},
// I2C Mapping
Package(2) { "bus-I2C-I2C5", Package() { 1 }},
// UART Mapping
Package(2) { "bus-UART-UART2", Package() { 2 }},
Package(2) { "bus-UART-UART1", Package() { 9 }},
}
})
}
}
}
附录 C - 用于生成 GPIO 资源的示例 Powershell 脚本
以下脚本可用于为 Raspberry Pi 生成 GPIO 资源声明:
$pins = @(
@{PinNumber=4;PullConfig='PullUp'},
@{PinNumber=5;PullConfig='PullUp'},
@{PinNumber=6;PullConfig='PullUp'},
@{PinNumber=12;PullConfig='PullDown'},
@{PinNumber=13;PullConfig='PullDown'},
@{PinNumber=16;PullConfig='PullDown'},
@{PinNumber=18;PullConfig='PullDown'},
@{PinNumber=22;PullConfig='PullDown'},
@{PinNumber=23;PullConfig='PullDown'},
@{PinNumber=24;PullConfig='PullDown'},
@{PinNumber=25;PullConfig='PullDown'},
@{PinNumber=26;PullConfig='PullDown'},
@{PinNumber=27;PullConfig='PullDown'},
@{PinNumber=35;PullConfig='PullUp'},
@{PinNumber=47;PullConfig='PullUp'})
# generate the resources
$FIRST_RESOURCE_INDEX = 4
$resourceIndex = $FIRST_RESOURCE_INDEX
$pins | % {
$a = @"
// Index $resourceIndex - GPIO $($_.PinNumber) - $($_.Name)
GpioIO(Shared, $($_.PullConfig), , , , "\\_SB.GPI0", , , , ) { $($_.PinNumber) }
GpioInt(Edge, ActiveBoth, Shared, $($_.PullConfig), 0, "\\_SB.GPI0",) { $($_.PinNumber) }
"@
Write-Host $a
$resourceIndex += 2;
}