在 Windows 10 及更高版本中,API 提供了用户模式直接访问通用输入/输出(GPIO)、Inter-Integrated 电路(I2C)、串行外围接口(SPI)和通用异步收发器(UART)的功能。 开发板(如 Raspberry Pi 2)公开了这些连接的子集,使你能够扩展具有自定义线路的基本计算模块,以解决特定应用程序的问题。 这些低级别总线通常与其他关键板载功能共享,只有一部分 GPIO 引脚和总线露出在连接器上。 为了保持系统稳定性,必须指定哪些引脚和总线可以安全地由用户模式应用程序进行修改。
本文档介绍如何在高级配置和 Power Interface(ACPI)中指定此配置,并提供用于验证配置是否正确指定的工具。
重要
本文档的受众是统一可扩展固件接口(UEFI)和 ACPI 开发人员。 假定您对 ACPI、ACPI 源语言(ASL)的编写和 SpbCx/GpioClx 有一定的了解。
Windows 上对低级别总线的用户模式访问通过 GpioClx
和 SpbCx
框架实现。 在 Windows IoT Core 和 Windows Enterprise 上提供名为 RhProxy 的新驱动程序,在用户模式中公开 GpioClx
和 SpbCx
资源。 若要启用 API,必须在 ACPI 表中声明 rhproxy 的设备节点,其中包含应公开到用户模式的每个 GPIO 和 SPB 资源。 本文档逐步讲解如何创作和验证 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 总线,则第一个声明的总线被视为该类型的“默认”总线,并且将由 GetDefaultAsync()
方法返回的实例是 Windows.Devices.I2c.I2cController 和 Windows.Devices.Spi.SpiController。
SPI
Raspberry Pi 有两辆公开的 SPI 巴士。 SPI0 有两个硬件芯片选择线,SPI1 有一个硬件芯片选择线。 每个总线的每个芯片选择行都需要一个 SPISerialBus() 资源声明。 以下两个 SPISerialBus 资源声明适用于 SPI0 上的两个芯片选择行。 DeviceSelection 字段包含一个唯一值,驱动程序将其解释为硬件芯片选择行标识符。 放入 DeviceSelection 字段的确切值取决于驱动程序如何解释 ACPI 连接描述符的此字段。
注释
本文包含对从属术语的引用,该术语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 测试
- 必须支持 4 MHz 时钟速度
- 必须支持 8 位数据长度
- 必须支持所有 SPI 模式:0、1、2、3
I2C
接下来,声明 I2C 资源。 Raspberry Pi 在引脚 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 | 输入下拉菜单 | 该引脚支持内置的下拉电阻,它对应于 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,该 ID 可用于打开资源的句柄。
您必须向
/MsftInternal
传递asl.exe
命令行开关,以编译包含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()
例程中接收 资源。 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,它是 16 进制格式的 64 位整数。 服务器应使用 RESOURCE_HUB_ID_FROM_FILE_NAME()
reshub.h 从文件名中提取资源中心 ID,并将 IOCTL_RH_QUERY_CONNECTION_PROPERTIES 发送到资源中心以获取 MsftFunctionConfig()
描述符。
服务器应验证描述符,并从描述符中提取共享模式和引脚列表。 然后,它应执行共享引脚的仲裁,如果成功,请在完成请求之前将引脚标记为已保留。
如果对引脚列表中的每个引脚共享仲裁均成功,则共享仲裁整体成功。 每个引脚都应按以下方式进行仲裁:
- 如果 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 - 启用上拉电阻
- 下拉 - 启用下拉电阻
- 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 控制器驱动程序能够将由设备节点 \_SB.GPIO0 管理的引脚 2 和 3 设置为功能 4,并启用上拉电阻。
支持在 GpioClx 客户端驱动程序中实现复用功能
GpioClx
内置了引脚复用功能支持。 GpioClx 微型端口驱动程序(也称为“GpioClx 客户端驱动程序”),驱动 GPIO 控制器硬件。 从 Windows 10 内部版本 14327 开始,GpioClx 微型端口驱动程序可以通过实现两个新的 DDI 来添加对引脚复用的支持:
- CLIENT_ConnectFunctionConfigPins – 由
GpioClx
调用,以命令微型端口驱动程序应用指定的复用配置。 - CLIENT_DisconnectFunctionConfigPins – 由
GpioClx
调用,以命令微型端口驱动程序还原复用配置。
有关这些例程的说明,请参阅 GpioClx 事件回调函数 。
除了这两个新的 DDI 之外,还应审核现有的 DDI,以确保引脚复用的兼容性。
- GpioClx 调用 CLIENT_ConnectIoPins,用于指示微型端口驱动程序配置一组引脚以进行 GPIO 输入或输出。 GPIO 和 MsftFunctionConfig 是互斥的,这意味着引脚不可能同时连接到 GPIO 和 MsftFunctionConfig。 由于 PIN 的默认功能不需要是 GPIO,因此在调用 ConnectIoPins 时,引脚不一定会复用到 GPIO。 ConnectIoPins 需要执行所有必需的操作,以使引脚适用于 GPIO IO,包括多路复用操作。 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 控制器驱动程序可能假设在调用 EvtSpbTargetConnect()
之前,引脚已经被复用到 SPB 功能。
SpbCx
在调用控制器驱动程序的 EvtSpbTargetDisconnect() 回调后,立即在其 IRP_MJ_CLOSE 处理程序中还原引脚复用配置。 当外围设备驱动程序打开与 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 并在
asl.exe
找到C:\Program Files (x86)\Windows Kits\10\Tools\x64\ACPIVerify
运行以下命令以生成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_START
Rhproxy 无法启动,很可能是因为资源分析问题。 再次确认 ASL,并仔细检查 DSD 中的资源索引,然后验证 GPIO 资源是否按引脚编号递增顺序指定。
验证预期设备是否在用户模式下可用
现在,rhproxy 正在运行,它应该已创建可由用户模式访问的设备接口。 我们将使用多个命令行工具来枚举设备并查看它们是否存在。
https://github.com/ms-iot/samples克隆存储库并生成 GpioTestTool
、I2cTestTool
和SpiTestTool
Mincomm
示例。 将工具复制到受测设备,并使用以下命令枚举设备。
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 测试
- Gpio测试工具
- I2cTestTool
- SpiTestTool
- MinComm (串行)
- 硬件实验室工具包 (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 最大引脚映射
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;
}