共用方式為


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

在 Windows 10 和更新版本中,API 提供從使用者模式直接存取至一般用途輸入/輸出 (GPIO)、Inter-Integrated 線路 (I2C)、序列周邊介面 (SPI)和通用異步接收器傳輸器 (UART)。 Raspberry Pi 2 等開發面板會公開這些連線的子集,可讓您擴充具有自定義線路的基底計算模組,以解決特定應用程式。 這些低階總線通常與其他重要的車載功能共用,只有一部分 GPIO 針腳和總線在接頭上公開。 若要維持系統穩定,必須指定哪些針腳和總線可以安全地由使用者模式應用程式修改。

本文件說明如何在進階設定和 Power Interface (ACPI) 中指定此組態,並提供工具來驗證已正確指定組態。

這很重要

本文件的目標對象是統一擴展韌體介面(UEFI)和 ACPI 開發人員。 假設對 ACPI、ACPI 原始語言 (ASL) 撰寫,以及 SpbCx/GpioClx 有基本的了解。

Windows 上低階總線的使用者模式存取會透過現有的 GpioClxSpbCx 架構。 名為 RhProxy的新驅動程式,可在 Windows IoT 核心版和 Windows 企業版上使用,將 GpioClxSpbCx 資源公開至使用者模式。 若要啟用 API,您必須在 ACPI 數據表中宣告 rhproxy 的設備節點,且應將每個 GPIO 和 SPB 資源公開至使用者模式。 本文件會詳細解說如何編寫和驗證 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 總線,則第一個宣告的總線會被視為該類型的「預設」總線,而且會由 GetDefaultAsync()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 },

MinClockInHzMaxClockInHz 屬性會指定控制器支援的最小和最大時鐘速度。 API 會防止使用者指定超出此範圍的值。 時鐘速度會傳遞至SPB驅動程式,在連線描述元_SPE欄位中(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 資源。 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_QueryControllerBasicInformation 回呼時,將 MemoryMappedController 旗標設定在 CLIENT_CONTROLLER_BASIC_INFORMATION 結構中,則為記憶體對應控制器。
  • 每個針腳都需要 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 控制器支援哪些驅動模式。 在上述範例中,支援下列所有驅動模式。 屬性是下列值的位元掩碼:

旗標值 駕駛模式 說明
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 使用循序針腳編號,因為現有的針腳圖很少,而循序針腳編號可簡化開發人員體驗,因為只有 10 個針腳會從超過 200 個針腳公開。 使用循序或原生針腳編號的選擇應該旨在減少開發人員的困惑。

GPIO 驅動程式需求

  • 必須使用 GpioClx
  • 必須在 SoC 記憶體中映射
  • 必須使用模擬的 ActiveBoth 中斷處理

UART

如果您的 UART 驅動程式使用 SerCxSerCx2,您可以使用 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 呼叫將會失敗。 當使用者透過處置 GpioPinI2cDeviceSpiDeviceSerialDevice 物件來關閉設備時,針腳會被釋放,以便之後可以用於不同的功能。

Windows 包含在 GpioClxSpbCx以及 SerCx 架構中的內建的支援功能。 這些架構會一起運作,以在存取 GPIO 針腳或總線時,自動將 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 連接資源。 用戶端會收到資源中心的標識碼,可用來開啟資源的控制碼。

您必須將 /MsftInternal 命令列開關傳遞至 asl.exe,以編譯包含 MsftFunctionConfig() 描述符的 ASL 檔案,因為這些描述符目前正在由 ACPI 工作委員會審查。 例如:asl.exe /MsftInternal dsdt.asl

針腳多路復用所涉及的操作順序如下所示。

腳位多工功能客戶端與伺服器的互動

  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 驅動程式會在其 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 要求的尾端路徑元件包含資源中樞標識碼,這是十六進位格式的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 }

  • 共享/獨佔 – 如果獨佔,一次可由單一用戶端取得此 PIN。 如果共用,多個共用用戶端可以取得資源。 一律將此設定為獨佔,因為允許多個未協調的用戶端存取可變資源可能會導致數據競爭,因此無法預測的結果。
  • PinPullConfig – 其中之一
    • PullDefault – 使用 SOC 定義的開機默認下拉配置
    • PullUp – 啟用拉式壓接器
    • PullDown – 啟用下拉式電阻
    • PullNone – 關閉所有拉電阻
  • FunctionNumber – 要編程到多工器的函式編號。
  • ResourceSource – 針腳多工伺服器的 ACPI 命名空間路徑
  • ResourceSourceIndex – 將此設定為 0
  • ResourceConsumer/ResourceProducer – 將此設定為 ResourceConsumer
  • VendorData – 選擇性的二進位數據,其意義由引腳多工伺服器定義。 這通常應該保留空白
  • 引腳清單 – 逗號分隔的引腳編號清單,適用於組態。 當腳位多工伺服器是 GpioClx 驅動程式時,這些是 GPIO 針腳號碼,且與 GpioIo 描述元中的針腳號碼具有相同的意義。

下列範例示範如何將 MsftFunctionConfig() 資源提供給 I2C 控制器驅動程式。

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 迷你埠驅動程式可以藉由實作兩個新的 DIS 來新增針腳多任務處理的支援:

  • CLIENT_ConnectFunctionConfigPins – 由 GpioClx 呼叫,以命令迷你埠驅動程式套用指定的多任務處理組態。
  • CLIENT_DisconnectFunctionConfigPins – 由 GpioClx 呼叫,指示迷你埠驅動程式還原多工組態。

如需這些例程的描述,請參閱 GpioClx 事件回呼函式

除了這兩個新的 DDIs 之外,應檢查現有的 DDIs 以確保針腳多工配置相容性:

  • CLIENT_ConnectIoPins/CLIENT_ConnectInterrupt – GpioClx 會呼叫 CLIENT_ConnectIoPins,藉此命令小型埠驅動程式配置一組引腳,作為 GPIO 的輸入或輸出用途。 GPIO 與 MsftFunctionConfig 互斥,這表示不會同時將引腳連接至 GPIO 和 MsftFunctionConfig。 由於針腳的預設功能不一定必須是 GPIO,因此當呼叫 ConnectIoPins 時,針腳可能不會被復用至 GPIO。 需要 ConnectIoPins 才能執行所有必要作業,讓 PIN 準備好供 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 開始,SpbCxSerCx 架構包含針腳多任務處理內建支援,可讓 SpbCxSerCx 控制器驅動程式成為針腳多任務處理用戶端,而不需要對控制器驅動程式本身進行任何程式代碼變更。 依延伸模組,任何連線到已啟用多任務處理之SpbCx/SerCx控制器驅動程式的SpbCx/SerCx周邊驅動程式都會觸發針腳多任務處理活動。

下圖顯示每個元件之間的相依性。 如您所見,針腳多工建立了 SerCx 和 SpbCx 控制器驅動程式對 GPIO 驅動程式的相依性,而 GPIO 驅動程式通常負責針腳多工。

針腳複用相依性

在裝置初始化時,SpbCxSerCx 架構會剖析所有以硬體資源的形式提供給裝置的所有 MsftFunctionConfig() 資源。 SpbCx/SerCx 接著會視需要取得並釋放針腳多任務處理資源。

SpbCx 在其 IRP_MJ_CREATE 處理常式中套用針腳複用組態,就在呼叫客戶端驅動程式的 EvtSpbTargetConnect() 回呼函式之前。 如果無法套用多路複用配置,則不會呼叫控制器驅動程式的 EvtSpbTargetConnect() 回調函式。 因此,SPB 控制器驅動程式可能會假設在呼叫 EvtSpbTargetConnect() 時,針腳已經切換至 SPB 功能。

SpbCx 在其 IRP_MJ_CLOSE 處理程式中,在叫用控制器驅動程式的 EvtSpbTargetDisconnect() 回呼之後,還原針腳複用組態。 結果是每當周邊驅動程式開啟 SPB 控制器驅動程式的句柄時,針腳會多工用於 SPB 功能,而當周邊驅動程式關閉句柄時,針腳會取消多工。

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

動態針腳多工對於 SerCxSpbCx 控制器驅動程式的影響在於,它們必須能夠容忍在特定時間將針腳功能從 SPB/UART 切換開。 在呼叫 EvtSpbTargetConnect()EvtSerCx2FileOpen() 之前,控制器驅動程序必須假設針腳不會被複用。 在下列回呼期間,引腳不需要多工至SPB/UART功能。 下列不是完整的清單,但代表控制器驅動程式所實作的最常見 PNP 例程。

  • 驅動程式入口
  • 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 核心版和 Windows 企業版 15063 和更新版本的所有組建上。

使用 ACPITABL.dat 編譯和載入 ASL

現在您已撰寫 rhproxy ASL 節點,現在可以進行編譯和載入。 您可以將 rhproxy 節點編譯成可附加至系統 ACPI 數據表的獨立 AML 檔案。 或者,如果您有系統的 ACPI 來源存取權,您可以將 rhproxy 節點直接插入平臺的 ACPI 數據表。 不過,在初始啟動期間,使用 ACPITABL.dat可能會比較容易。

  1. 建立名為 yourboard.asl 的檔案,並將 RHPX 裝置節點放在 DefinitionBlock 內:

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

  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 裝置]:

Windows 硬體實驗室套件的螢幕快照,其中顯示已選取 [資源中樞代理裝置] 選項的 [選取] 索引標籤。

然後按兩下 [測試] 索引標籤,然後選取 [I2C WinRT]、[Gpio WinRT] 和 [Spi WinRT 測試]。

Windows 硬體實驗室套件的螢幕截圖,顯示 [測試] 索引標籤上已選取 [G P I O Win R T 功能和壓力測試] 選項。

點擊「執行選取」。 以滑鼠右鍵點選測試,然後選擇「測試描述」,即可取得每個測試的進一步文件。

資源

附錄

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