啟用使用者模式存取 GPIO、I2C 和 SPI

在 Windows 10 及更新版本中,API 提供從使用者模式到通用輸入/輸出(GPIO)、內部整合電路 (I2C)、序列周邊介面 (SPI) 和通用非同步收發傳輸器 (UART) 的直接存取權。 Raspberry Pi 2 等開發板公開了這些連接的子集,使您能夠使用自訂電路擴充基本運算模組,以解決特定應用程式的問題。 這些低階匯流排通常與其他關鍵板載函式共用,只有一部分 GPIO 針腳和匯流排公開在接頭板上。 為了保持系統穩定性,有必要指定哪些針腳和匯流排可以安全地由使用者模式應用程式修改。

本文件說明如何在進階設定和電源介面 (ACPI) 中指定此組態,並提供用於驗證是否正確指定組態的工具。

重要

本文件的讀者是統一可擴充韌體介面 (UEFI) 和 ACPI 開發人員。 假設您熟悉 ACPI、ACPI 來源語言 (ASL) 撰寫和 SpbCx/GpioClx。

使用者模式對 Windows 上低階匯流排的存取是透過現有 GpioClxSpbCx 架構進行的。 Windows IoT Core 和 Windows Enterprise 上提供了名為 RhProxy 的新驅動程式,可向使用者模式公開 GpioClxSpbCx 資源。 若要啟用 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 – 硬體識別碼將其設定為特定廠商的硬體識別碼。
  • _CID – 相容識別碼。必須是「MSFT8000」。
  • _UID – 唯一識別碼。設為 1。

接下來,我們宣告應向使用者模式公開的每個 GPIO 和 SPB 資源。 宣告資源的順序很重要,因為資源索參考於將屬性與資源關聯起來。 如果公開了多個 I2C 或 SPI 匯流排,則第一個宣告的匯流排被視為該類型的「預設」匯流排,並且將是 Windows.Devices.I2c.I2cControllerWindows.Devices.Spi.SpiControllerGetDefaultAsync() 方法傳回的執行個體。

SPI

Raspberry Pi 有兩個公開的 SPI 匯流排。 SPI0 有 2 條硬體片選線,SPI1 有 1 條硬體片選線。 每條匯流排的每個晶片選線都需要一個 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 },

MinClockInHzMaxClockInHz 屬性指定控制器支援的最小和最大時脈速度。 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 描述項的以下欄位是固定的:

  • DeviceSelection
  • DeviceSelectionPolarity
  • WireMode
  • SlaveMode
  • ResourceSource

以下欄位是使用者在執行時指定值的預留位置:

  • DataBitLength
  • ConnectionSpeed
  • ClockPolarity
  • 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 資源。 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 }},

這宣告了一個易記名稱為「I2C1」的 I2C 匯流排,其參考資源索引 3,即我們上面宣告的 I2CSerialBus() 資源的索引。

I2CSerialBus() 描述項的以下欄位是固定的:

  • SlaveMode
  • ResourceSource

以下欄位是使用者在執行時指定值的預留位置。

  • SlaveAddress
  • ConnectionSpeed
  • AddressingMode

I2C 驅動程式需求

  • 必須使用 SpbCx 或與 SpbCx 相容
  • 必須通過 MITT I2C 測試
  • 必須支援 7 位元尋址
  • 必須支援 100kHz 時脈速度
  • 必須支援 400kHz 時脈速度

GPIO

接下來,我們會宣告所有公開給使用者模式的 GPIO 針腳。 我們提供以下指南來決定要公開哪些針腳:

  • 在公開標頭上宣告所有針腳。
  • 宣告連接到有用的板載功能 (如按鈕和 LED) 的針腳。
  • 請勿宣告為系統函式保留的針腳,或未連接到任何東西的針腳。

以下 ASL 區塊會宣告兩個針腳 – GPIO4 和 GPIO5。 為了簡潔起見,此處不會顯示其他針腳。 附錄 C 包含一個範例 powershell 指令碼,可用於產生 GPIO 資源。

// 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 欄位必須是 Shared
  • 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 控制器支援哪些驅動模式。 在上述範例中,支援以下所有驅動模式。 此屬性是以下值的位元遮罩:

旗標值 驅動模式 描述
0x1 InputHighImpedance 此針腳支援高阻抗輸入,對應 ACPI 中的「PullNone」值。
0x2 InputPullUp 此針腳支援內建上拉電阻,對應 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 屬性應與透過 GpioClx 驅動程式的 CLIENT_QueryControllerBasicInformation 回呼中的 TotalPins 屬性傳回的值相符。

選擇與您的電路板現有已發佈文件最相容的編號配置。 例如,Raspberry Pi 會使用原生針腳編號,因為許多現有針腳分配圖使用 BCM2835 針腳編號。 MinnowBoardMax 使用循序針腳編號,因為現有的針腳排列圖很少,而且循序針腳編號簡化了開發人員的體驗,因為 200 多個針腳中只公開了 10 個針腳。 使用循序或原生針腳編號的決定應該要能降低開發人員的混淆。

GPIO 驅動程式需求

  • 必須使用 GpioClx
  • 必須是 SOC 記憶體對應
  • 必須使用模擬的 ActiveBoth 中斷處理

UART

如果您的 UART 驅動程式使用 SerCxSerCx2,則可以使用 rhproxy 將驅動程式公開給使用者模式。 建立 GUID_DEVINTERFACE_COMPORT 類型裝置介面的 UART 驅動程式不需要使用 rhproxy。 收件匣 Serial.sys 驅動程式就是其中之一。

若要將 SerCx-style 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() 呼叫將會失敗。 當使用者透過處理 GpioPinI2cDeviceSpiDeviceSerialDevice 物件來關閉裝置時,將會釋放針腳,從而允許稍後打開它們以執行不同的函式。

Windows 包含 GpioClxSpbCxSerCx 架構中針腳多工處理的內建支援。 這些架構會協同工作,在存取 GPIO 針腳或匯流排時自動將針腳切換到正確的函式。 對針腳的存取進行仲裁,以防止多個用戶端之間的衝突。 除了這種內建支援之外,針腳多工處理的介面和通訊協定都是通用的,可以擴充以支援其他裝置和場景。

本文首先介紹針腳多工處理所涉及的底層介面和通訊通訊協定,然後介紹如何在 GpioClx、SpbCx 和 SerCx 控制器驅動程式中新增對針腳多工處理的支援。

針腳多工處理架構

本節介紹針腳多工處理中涉及的底層介面和通訊協定。 支援 GpioClx/SpbCx/SerCx 驅動程式的針腳多工處理不一定需要了解底層通訊協定。 有關如何使用 GpioCls/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 連接資源。 用戶端會收到資源中樞標識碼,可用來開啟資源的控制代碼。

您必須將 /MsftInternal 命令列開關傳遞給 asl.exe 來編譯包含 MsftFunctionConfig() 描述項的 ASL 文件,因為這些描述項目前正在接受 ACPI 工作委員會的審查。 例如:asl.exe /MsftInternal dsdt.asl

針腳多工處理涉及的作業順序如下所示。

Pin muxing client server interaction

  1. 用戶端在其 EvtDevicePrepareHardware() 回呼中,從 ACPI 韌體接收 MsftFunctionConfig 資源。
  2. 用戶端會使用資源中樞協助程式函式 RESOURCE_HUB_CREATE_PATH_FROM_ID() 根據資源識別碼建立路徑,然後開啟該路徑的控制代碼 (使用 ZwCreateFile()IoGetDeviceObjectPointer()WdfIoTargetOpen())。
  3. 伺服器使用資源中樞輔助函式 RESOURCE_HUB_ID_FROM_FILE_NAME() 從檔案路徑中提取資源中樞識別碼,然後查詢資源中樞以取得資源描述項。
  4. 伺服器對描述項中的每個針腳執行共用仲裁,並完成 IRP_MJ_CREATE 要求。
  5. 用戶端在收到的控制代碼上發出 IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS 要求。
  6. 為了回應 IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS,伺服器透過在每個針腳上啟動指定的功能來執行硬體多工處理作業。
  7. 用戶端繼續執行取決於要求的針腳多工處理設定的作業。
  8. 當用戶端不再需要多工作處理針腳時,它會關閉控制代碼。
  9. 為了回應關閉的控制代碼,伺服器會將針腳還原回其初始狀態。

針腳多工處理用戶端的通訊協定描述

本節說明用戶端如何使用針腳多工作處理功能。 這不適用於 SerCxSpbCx 控制器驅動程式,因為架構代表控制器驅動程式實作此通訊協定。

剖析資源

WDF 驅動程式在其 EvtDevicePrepareHardware() 例程中接收 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()WdfIoTargetClose() (如果您打算重複使用 WDFIOTARGET) 來關閉資源控制代碼。

    WdfObjectDelete(resourceHandle);

當用戶端關閉其資源控制代碼時,針腳將會進行多工處理回到其初始狀態,並且現在可以由不同的用戶端取得。

針腳多工處理伺服器的通訊協定描述

本節介紹針腳多工處理伺服器如何向用戶端公開其功能。 這不適用於 GpioClx 迷你埠驅動程式,因為架構代表用戶端驅動程式實作此通訊協定。 有關如何在 GpioClx 用戶端驅動程式中支援針腳多工處理的詳細資訊,請參閱在 GpioClx 用戶端驅動程式中實作多工處理支援

處理 IRP_MJ_CREATE 要求

當用戶端想要保留針腳多工處理資源時,其會開啟資源的控制代碼。 針腳多工處理伺服器透過來自資源中樞的重新剖析作業接收 IRP_MJ_CREATE 要求。 IRP_MJ_CREATE 要求的尾部路徑元件包含資源中樞識別碼,它是十六進位格式的 64 位元整數。 伺服器應使用 reshub.h 中的 RESOURCE_HUB_ID_FROM_FILE_NAME() 從檔案名稱中提取資源中樞識別碼,並將 IOCTL_RH_QUERY_CONNECTION_PROPERTIES 傳送到資源中樞以取得 MsftFunctionConfig() 描述項。

伺服器應驗證描述項,並從描述項中提取共用模式和針腳清單。 然後,它應該對針腳執行共用仲裁,如果成功,則在完成要求之前將針腳標記為保留。

如果針腳清單中每個針腳的共用仲裁都成功,則共用仲裁總體成功。 各個針腳應以以下方式仲裁:

  • 如果該針腳尚未保留,則共用仲裁成功。
  • 如果該針腳已保留為專屬,則共用仲裁失敗。
  • 如果針腳已保留為共用,
    • 並且已共用傳入的要求,則共用仲裁成功。
    • 而傳入的要求是專屬的,共用仲裁失敗。

如果共用仲裁失敗,則應使用 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 build 14327 開始,GpioClx 迷你埠驅動程式可以透過實作兩個新的 DDI 來新增對針腳多工處理的支援:

  • CLIENT_ConnectFunctionConfigPins – 由 GpioClx 呼叫,以命令迷你埠驅動程式應用指定的多路多工處理組態。
  • CLIENT_DisconnectFunctionConfigPins – 由 GpioClx 呼叫,以命令迷你埠驅動程式恢復多路多工處理組態。

有關這些例程的說明,請參閱 GpioClx 事件回呼函式

除了這兩個新的 DDI 之外,還應審核現有 DDI 的針腳多工處理相容性:

  • CLIENT_ConnectIoPins/CLIENT_ConnectInterrupt – CLIENT_ConnectIoPins 由 GpioClx 呼叫,以命令迷你埠驅動程式為 GPIO 輸入或輸出設定一組針腳。 GPIO 與 MsftFunctionConfig 是互斥的,這代表針腳永遠不會同時連接 GPIO 和 MsftFunctionConfig。 由於針腳的預設功能不需要是 GPIO,因此當呼叫 ConnectIoPins 時,針腳不一定不會多工處理到 GPIO。 ConnectIoPins 需要執行使針腳為 GPIO IO 做好準備所需的所有作業,包括多工處理作業。 CLIENT_ConnectInterrupt 的行為應該類似,因為中斷可以視為 GPIO 輸入的特殊情況。
  • CLIENT_DisconnectIoPins/CLIENT_DisconnectInterrupt – 這些例程應將針腳傳回至呼叫 CLIENT_ConnectIoPins/CLIENT_ConnectInterrupt 時的狀態,除非指定了 PreserveConfiguration 旗標。 除了將針腳的方向恢復為其預設狀態之外,迷你埠還應將每個針腳的多路多工處理狀態恢復為呼叫 _Connect 例程時的狀態。

例如,假設針腳的預設多路多工處理組態是 UART,並且該針腳也可以作為 GPIO 使用。 當呼叫 CLIENT_ConnectIoPins 來連接 GPIO 的針腳時,它應該將針腳多工處理到 GPIO,並且在 CLIENT_DisconnectIoPins 中,它應該將針腳多工處理回 UART。 一般來說,中斷例程應該會復原連接例程所執行的作業。

支援 SpbCx 和 SerCx 控制器驅動程式中的多工處理

從Windows 10 版本14327 開始,SpbCxSerCx 架構包含對針腳多工處理的內建支持,使 SpbCxSerCx 控制器驅動程式能夠成為針腳多工處理用戶端,而無需對控制器驅動程式本身進行任何程式碼變更。 透過擴充,任何連接到啟用多工處理的 SpbCx/SerCx 控制器驅動程式的 SpbCx/SerCx 周邊驅動程式都會觸發針腳多工處理活動。

下圖顯示了每個元件之間的相依性。 如您所見,針腳多工處理引入了從 SerCx 和 SpbCx 控制器驅動程式到 GPIO 驅動程式的相依性,該驅動程式通常負責多工處理。

Pin muxing dependency

在裝置初始化時,SpbCxSerCx 架構會剖析作為硬體資源提供給裝置的所有 MsftFunctionConfig() 資源。 然後,SpbCx/SerCx 根據需要取得和釋放針腳多工處理資源。

SpbCx 在呼叫用戶端驅動程式的 EvtSpbTargetConnect() 回呼之前,在其 IRP_MJ_CREATE 處理常式中套用針腳多工處理設定。 如果無法套用多路多工處理組態,則不會呼叫控制器驅動程式的 EvtSpbTargetConnect() 回呼。 因此,SPB 控制器驅動程式可能會假設在呼叫 EvtSpbTargetConnect() 時,針腳會多工處理到 SPB 函式。

SpbCx 在叫用控制器驅動程式的 EvtSpbTargetDisconnect() 回呼後,立即在其 IRP_MJ_CLOSE 處理常式中恢復針腳多工處理組態。 結果是,只要周邊驅動程式開啟 SPB 控制器驅動程式的控制代碼,針腳就會多工處理到 SPB 函式,而當周邊驅動程式關閉其控制代碼時,針腳就會進行多工處理。

SerCx 的行為類似。 SerCx 在呼叫控制器驅動程式的 EvtSerCx2FileOpen() 回呼之前取得其 IRP_MJ_CREATE 處理常式中的所有 MsftFunctionConfig() 資源,並在呼叫控制器驅動程式的 EvtSerCx2FileClose 回呼之後釋放其 IRP_MJ_CLOSE 處理常式中的所有資源。

SerCxSpbCx 控制器驅動程式的動態針腳多工處理的意思是,它們必須能夠容許在某些時候將針腳與 SPB/UART 函式多工處理。 控制器驅動程式需要假設在呼叫 EvtSpbTargetConnect()EvtSerCx2FileOpen() 之前,針腳不會進行多工處理。 在以下回呼期間,針腳無需多工處理到 SPB/UART 函式。 以下不是完整清單,但代表了控制器驅動程式實作的最常見 PNP 例程。

  • DriverEntry
  • EvtDriverDeviceAdd
  • EvtDevicePrepareHardware/EvtDeviceReleaseHardware
  • EvtDeviceD0Entry/EvtDeviceD0Exit

驗證

當您準備好測試 rhproxy 時,使用下列逐步程序會很有幫助。

  1. 確認每個 SpbCxGpioClxSerCx 控制器驅動程式是否已正確載入和執行
  2. 確認系統上是否存在 rhproxy。 Windows 的某些版本和組建上並不具備它。
  3. 使用 ACPITABL.dat 編譯並載入您的 rhproxy 節點
  4. 確認 rhproxy 裝置節點是否存在
  5. 確認 rhproxy 是否正在載入並啟動
  6. 確認預期的裝置是否公開至使用者模式
  7. 確認您可以從命令行與每個裝置互動
  8. 確認您可以從 UWP 應用程式與每個裝置互動
  9. 執行 HLK 測試

確認控制器驅動程式

由於 rhproxy 將系統上的其他裝置公開給使用者模式,因此它僅在這些裝置已經運作時才能運作。 第一步是確認這些裝置 (您希望公開的 I2C、SPI、GPIO 控制器) 是否已經正常運作。

在命令提示字元中,執行

devcon status *

查看輸出並驗證所有感興趣的裝置都已啟動。 如果裝置有問題代碼,您需要排除該裝置未載入的原因。 所有裝置都應在初始平台啟動期間啟用。 對 SpbCxGpioClxSerCx 控制器驅動程式進行疑難排解不在本文的探討範圍。

確認系統上是否存在 rhproxy

確認系統上是否存在 rhproxy 服務。

reg query HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\rhproxy

如果 reg 金鑰不存在,則您的系統上不存在 rhproxy。 Rhproxy 存在於 IoT Core 和 Windows Enterprise build 15063 及更新版本的所有版本中。

使用 ACPITABL.dat 編譯並載入 ASL

現在您已經撰寫 rhproxy ASL 節點,是時候編譯並載入它了。 您可以將 rhproxy 節點編譯為獨立的 AML 檔案,該檔案可以附加到系統 ACPI 資料表中。 或者,如果您有權存取系統的 ACPI 來源,則可以將 rhproxy 節點直接插入到平台的 ACPI 資料表中。 然而,在初始啟動期間,使用 ACPITABL.dat 可能會比較容易。

  1. 建立一個名為 yourboard.asl 的檔案,並將 RHPX 裝置節點放入 DefinitionBlock 中:

    DefinitionBlock ("ACPITABL.dat", "SSDT", 1, "MSFT", "RHPROXY", 1)
    {
        Scope (\_SB)
        {
            Device(RHPX)
            {
            ...
            }
        }
    }
    
  2. 下載 WDK 並在 C:\Program Files (x86)\Windows Kits\10\Tools\x64\ACPIVerify 找到 asl.exe

  3. 執行以下命令產生 ACPITABL.dat:

    asl.exe yourboard.asl
    
  4. 將產生的 ACPITABL.dat 檔案複製到測試系統上的 c:\windows\system32。

  5. 在受測系統上開啟測試簽章:

    bcdedit /set testsigning on
    
  6. 重新啟動受測系統。 系統會將 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 存放庫,然後建置 GpioTestToolI2cTestToolSpiTestToolMincomm 範例。 將工具複製到受測裝置,並使用以下命令列舉裝置。

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)。 可進行以下測試:

當您在 HLK 管理員中選擇 rhproxy 裝置節點時,將自動選擇適用的測試。

在 HLK 管理員中,選取 [資源中樞 Proxy 裝置]:

Screenshot of the Windows Hardware Lab Kit showing the Selection tab with the Resource Hub proxy device option selected.

然後點擊 [測試] 索引標籤,並選擇 I2C WinRT、Gpio WinRT 和 Spi WinRT 測試。

Screenshot of the Windows Hardware Lab Kit showing the Tests tab with the G P I O Win R T Functional and Stress Tests option selected.

按一下 [執行所選項目]。 以滑鼠右鍵點擊測試並按一下 [測試描述],即可取得每個測試的進一步文件。

資源

附錄

附錄 A - Raspberry Pi ASL 清單

另請參閱 Raspberry Pi 2 & 3 針腳對應

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;
}