在Windows 10及之後的版本中,API 可直接從使用者模式存取通用輸入/輸出(GPIO)、Inter-Integrated 電路(I2C)、序列周邊介面(SPI)及通用非同步接收-發射器(UART)。 像 Raspberry Pi 2 這類開發板會提供這些連接的子集,讓你能用自訂電路擴展基礎計算模組,以滿足特定應用需求。 這些低階匯流排通常與其他關鍵的內建功能共用,只有部分 GPIO 腳位和匯流排暴露在標頭上。 為了維持系統穩定性,必須指定哪些接腳和匯流排可由使用者模式應用程式安全地修改。
本文件說明如何在進階配置與電源介面(ACPI)中指定此配置,並提供驗證設定正確指定的工具。
Important
本文件的讀者為統一可擴充韌體介面(UEFI)及 ACPI 開發者。 假設你對 ACPI、ACPI 原始語言(ASL)撰寫以及 SpbCx/GpioClx 有一定的熟悉。
Windows 上低階匯流排的使用者模式存取,是透過現有的 GpioClx 與 SpbCx 架構來提供的。 名為 RhProxy 的新驅動程式可在 Windows IoT Core 和 Windows Enterprise 上使用,並向使用者模式公開 GpioClx 和 SpbCx 資源。 若要啟用這些 API,您必須在 ACPI 資料表中宣告 rhproxy 的裝置節點,並在其中宣告每一個要公開給使用者模式的 GPIO 和 SPB 資源。 這份文件說明了如何撰寫並驗證 ASL。
以範例說明 ASL
讓我們來看看 Raspberry Pi 2 上的 rhproxy 裝置節點宣告。 首先,在 \_SB 範圍內建立 ACPI 裝置宣告。
Device(RHPX)
{
Name(_HID, "MSFT8000")
Name(_CID, "MSFT8000")
Name(_UID, 1)
}
- _HID – 硬體識別碼。將此設定為廠商專屬的硬體識別碼。
- _CID – 相容身份證。必須是「MSFT8000」。
- _UID – 唯一識別碼,設定為 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 連線描述符的這個欄位。
Note
本文提及了「從屬」一詞——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
- 資源來源
以下欄位為使用者執行時指定的值的佔位符:
- DataBitLength
- 連接速度
- 時鐘極性
- 時鐘相位
由於 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
- 資源來源
以下欄位為執行時使用者指定的值的佔位符。
- 從屬地址
- 連接速度
- 定址模式
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結構中對 CLIENT_QueryControllerBasicInformation 回調設定 MemoryMappedController 旗標,則稱為記憶體映射控制器。
- 每個腳位都需要 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 輸出外,還支援內建上拉與下拉電阻,您必須以選用的 SupportedDriveMode 屬性來指定此功能。
Package (2) { “GPIO-SupportedDriveModes”, 0xf },
SupportedDriveMode 屬性顯示 GPIO 控制器支援哪些驅動模式。 在上述範例中,以下所有驅動模式皆被支援。 該屬性是由以下值組成的位元遮罩:
| 旗幟價值 | 駕駛模式 | Description |
|---|---|---|
| 0x1 | 輸入高阻抗 | 腳位支援高阻抗輸入,對應於 ACPI 中的「PullNone」值。 |
| 0x2 | InputPullUp | 該腳位支援內建的上拉電阻,對應於 ACPI 中的「上拉」值。 |
| 0x4 | 輸入下拉式選單 | 該腳位支援內建的下拉電阻,對應於 ACPI 中的「下拉」值。 |
| 0x8 | OutputCMOS | 此引腳支援產生強高電位與強低電位輸出(不同於開漏)。 |
幾乎所有 GPIO 控制器都支援 InputHighImpedance 與 OutputCmos。 若未指定 SupportedDriveMode 屬性,則預設為此。
如果 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 控制器,可能會路由到同一物理腳位。 多工器區塊控制該腳位在任一時刻啟用的是哪一種功能。 傳統上,韌體負責在開機時建立函式指派,且此指派在開機會話中保持靜態。 執行時腳位多工技術則增加了在執行時重新配置腳位功能指派的能力。 讓使用者能在執行時選擇腳位功能,透過快速重新配置板子腳位,加速開發,並使硬體能支援比靜態配置更廣泛的應用範圍。
使用者無需撰寫額外程式碼,即可使用對 GPIO、I2C、SPI 與 UART 的多工支援。 當使用者使用 OpenPin() 或 FromIdAsync() 開啟 GPIO 或匯流排時,底層的實體腳位會自動切換為所要求的功能。 如果這些腳位已被其他函式使用,OpenPin() 或 FromIdAsync() 呼叫將會失敗。 當使用者透過丟棄 GpioPin、 I2cDevice、 SpiDevice 或 SerialDevice 物件來關閉裝置時,腳位會被釋放,之後可開啟以執行其他功能。
Windows 內含對 GpioClx、SpbCx 和 SerCx 架構中腳位多工的內建支援。 這些框架會共同運作,在存取 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 請求來提交設定。 用戶端會關閉 handle 以釋放腳位多工資源,此時多工設定會回復到預設狀態。
- ACPI 韌體——指定多路復用設定與
MsftFunctionConfig()資源。 MsftFunctionConfig 資源用於表達用戶端需要哪些腳位,以及這些腳位所需的多工配置。 MsftFunctionConfig 資源包含函式編號、拉取設定及腳位編號列表。 MsftFunctionConfig 資源會作為硬體資源提供給針腳多工用戶端,並由驅動程式在其 PrepareHardware 回呼中接收,方式類似於 GPIO 和 SPB 連線資源。 用戶端會收到一個資源集線器 ID,可用來開啟該資源的控制代碼。
您必須將
/MsftInternal命令列參數傳遞給asl.exe,才能編譯包含MsftFunctionConfig()描述符的 ASL 檔案,因為這些描述符目前仍在 ACPI 工作委員會的審查中。 例如:asl.exe /MsftInternal dsdt.asl
腳位多工的操作順序如下所示。
- 用戶端會從 ACPI 韌體的 EvtDevicePrepareHardware() 回調中接收 MsftFunctionConfig 資源。
- 用戶端會使用 resource hub 輔助函式
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,伺服器會執行硬體多工切換作業,使每個腳位上的指定功能生效。
- 用戶端會依據所請求的腳位多工配置進行操作。
- 當用戶端不再需要腳位多工時,會關閉把手。
- 當 handle 被關閉時,伺服器會將腳位恢復到初始狀態。
腳位多工用戶端的協定說明
本節說明用戶端如何使用腳位多工功能。 這不適用於 SerCx 控制器 SpbCx 驅動程式,因為框架是代表控制器驅動程式實作此協定。
剖析資源
WDF 驅動程式透過 MsftFunctionConfig() 其 EvtDevicePrepareHardware() 例程接收資源。 MsftFunctionConfig 資源可透過以下欄位識別:
CM_PARTIAL_RESOURCE_DESCRIPTOR::Type = CmResourceTypeConnection
CM_PARTIAL_RESOURCE_DESCRIPTOR::u.Connection.Class = CM_RESOURCE_CONNECTION_CLASS_FUNCTION_CONFIG
CM_PARTIAL_RESOURCE_DESCRIPTOR::u.Connection.Type = CM_RESOURCE_CONNECTION_TYPE_FUNCTION_CONFIG
一個 EvtDevicePrepareHardware() 常式可能會如下擷取 MsftFunctionConfig 資源:
EVT_WDF_DEVICE_PREPARE_HARDWARE evtDevicePrepareHardware;
_Use_decl_annotations_
NTSTATUS
evtDevicePrepareHardware (
WDFDEVICE WdfDevice,
WDFCMRESLIST ResourcesTranslated
)
{
PAGED_CODE();
LARGE_INTEGER connectionId;
ULONG functionConfigCount = 0;
const ULONG resourceCount = WdfCmResourceListGetCount(ResourcesTranslated);
for (ULONG index = 0; index < resourceCount; ++index) {
const CM_PARTIAL_RESOURCE_DESCRIPTOR* resDescPtr =
WdfCmResourceListGetDescriptor(ResourcesTranslated, index);
switch (resDescPtr->Type) {
case CmResourceTypeConnection:
switch (resDescPtr->u.Connection.Class) {
case CM_RESOURCE_CONNECTION_CLASS_FUNCTION_CONFIG:
switch (resDescPtr->u.Connection.Type) {
case CM_RESOURCE_CONNECTION_TYPE_FUNCTION_CONFIG:
switch (functionConfigCount) {
case 0:
// save the connection ID
connectionId.LowPart = resDescPtr->u.Connection.IdLowPart;
connectionId.HighPart = resDescPtr->u.Connection.IdHighPart;
break;
} // switch (functionConfigCount)
++functionConfigCount;
break; // CM_RESOURCE_CONNECTION_TYPE_FUNCTION_CONFIG
} // switch (resDescPtr->u.Connection.Type)
break; // CM_RESOURCE_CONNECTION_CLASS_FUNCTION_CONFIG
} // switch (resDescPtr->u.Connection.Class)
break;
} // switch
} // for (resource list)
if (functionConfigCount < 1) {
return STATUS_INVALID_DEVICE_CONFIGURATION;
}
// TODO: save connectionId in the device context for later use
return STATUS_SUCCESS;
}
資源預留與投入
當用戶端想要多路復用腳位時,會保留並提交 MsftFunctionConfig 資源。 以下範例說明客戶端如何保留並提交 MsftFunctionConfig 資源。
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS AcquireFunctionConfigResource (
WDFDEVICE WdfDevice,
LARGE_INTEGER ConnectionId,
_Out_ WDFIOTARGET* ResourceHandlePtr
)
{
PAGED_CODE();
//
// Form the resource path from the connection ID
//
DECLARE_UNICODE_STRING_SIZE(resourcePath, RESOURCE_HUB_PATH_CHARS);
NTSTATUS status = RESOURCE_HUB_CREATE_PATH_FROM_ID(
&resourcePath,
ConnectionId.LowPart,
ConnectionId.HighPart);
if (!NT_SUCCESS(status)) {
return status;
}
//
// Create a WDFIOTARGET
//
WDFIOTARGET resourceHandle;
status = WdfIoTargetCreate(WdfDevice, WDF_NO_ATTRIBUTES, &resourceHandle);
if (!NT_SUCCESS(status)) {
return status;
}
//
// Reserve the resource by opening a WDFIOTARGET to the resource
//
WDF_IO_TARGET_OPEN_PARAMS openParams;
WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(
&openParams,
&resourcePath,
FILE_GENERIC_READ | FILE_GENERIC_WRITE);
status = WdfIoTargetOpen(resourceHandle, &openParams);
if (!NT_SUCCESS(status)) {
return status;
}
//
// Commit the resource
//
status = WdfIoTargetSendIoctlSynchronously(
resourceHandle,
WDF_NO_HANDLE, // WdfRequest
IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS,
nullptr, // InputBuffer
nullptr, // OutputBuffer
nullptr, // RequestOptions
nullptr); // BytesReturned
if (!NT_SUCCESS(status)) {
WdfIoTargetClose(resourceHandle);
return status;
}
//
// Pins were successfully muxed, return the handle to the caller
//
*ResourceHandlePtr = resourceHandle;
return STATUS_SUCCESS;
}
驅動程式應將 WDFIOTARGET 儲存在其其中一個上下文區域,以便之後關閉。 當驅動程式準備釋放多工設定時,應呼叫 WdfObjectDelete() 來關閉資源控制代碼;如果打算重複使用 WDFIOTARGET,則應呼叫 WdfIoTargetClose()。
WdfObjectDelete(resourceHandle);
當用戶端關閉其資源控制代碼時,腳位會切換回其初始狀態,之後即可由其他用戶端取得。
引腳多工伺服器的協定描述
本節說明腳位多工伺服器如何向用戶端揭露其功能。 這不適用於 GpioClx 迷你埠驅動程式,因為框架是代表用戶端驅動程式實作此協定。 關於如何在用戶端驅動程式中支援腳位多工的 GpioClx 詳細資訊,請參見 「在 GpioClx 用戶端驅動程式中實作多工支援」。
處理 IRP_MJ_CREATE 要求
當用戶端想要預留腳位多工資源時,會開啟該資源的一個控制代碼。 腳位多工伺服器透過資源樞紐的解析操作接收 IRP_MJ_CREATE 請求。
IRP_MJ_CREATE請求的後續路徑部分包含資源樞紐 ID,這是一個十六進位格式的 64 位整數。 伺服器應使用 RESOURCE_HUB_ID_FROM_FILE_NAME() reshub.h 從檔名中擷取資源集線器的 ID,然後將 IOCTL_RH_QUERY_CONNECTION_PROPERTIES 傳送給資源集線器取得 MsftFunctionConfig() 描述符。
伺服器應驗證描述元,並從描述元中擷取共享模式和引腳清單。 接著應針對腳位執行共享仲裁,若成功,則在完成請求前將腳位標示為已保留。
如果共享仲裁對接腳清單中的每個接腳都成功,則共享仲裁整體上即為成功。 每個腳位應按如下方式仲裁:
- 如果該密碼尚未被保留,共享仲裁即為成功。
- 如果密碼已經被保留為專屬,共享仲裁將失敗。
- 如果該密碼已經被保留為共享,
- 而且傳入的請求已被共享,因此共享仲裁成功。
- 而且收到的請求是排他性的,共享仲裁失敗。
若共享仲裁失敗,申請應以 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 描述符是在了解伺服器如何解讀此欄位後所撰寫的。
請記得當 handle 關閉時,伺服器必須將腳位還原到收到 IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS 時的配置,因此伺服器可能需要先儲存腳位狀態,才能修改它們。
處理 IRP_MJ_CLOSE 要求
當用戶端不再需要多工資源時,會關閉其 handle。 當伺服器收到 IRP_MJ_CLOSE 請求時,應該會將腳位回復到收到 IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS 時的狀態。 如果客戶從未寄出 IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS,則無需採取行動。 伺服器應將腳位標記為可用於共享仲裁,並完成請求 STATUS_SUCCESS。 務必正確同步 IRP_MJ_CLOSE 與 IRP_MJ_CREATE 的操作。
ACPI 資料表的撰寫指引
本節說明如何提供多工處理資源給用戶端驅動程式。 請注意,若要編譯包含 MsftFunctionConfig() 資源的資料表,您需要使用 Microsoft ASL 編譯器組建 14327 或更新版本。
MsftFunctionConfig() 資源作為硬體資源提供給腳位多工用戶端。
MsftFunctionConfig() 應將資源提供給需要變更腳位多工設定的驅動程式,通常是 SPB 和序列控制器驅動程式;但不應提供給 SPB 和序列周邊驅動程式,因為腳位多工設定是由控制器驅動程式負責處理。
MsftFunctionConfig() ACPI 巨集的定義如下:
MsftFunctionConfig(Shared/Exclusive
PinPullConfig,
FunctionNumber,
ResourceSource,
ResourceSourceIndex,
ResourceConsumer/ResourceProducer,
VendorData) { Pin List }
- 共享/獨家 – 若為獨家,此 PIN 可由單一客戶一次取得。 若共享,多個共享客戶端即可取得該資源。 請始終將此設定為排他,因為允許多個未協調的客戶端存取可變資源,可能導致資料競賽,進而產生不可預測的結果。
- PinPullConfig – 其中之一
- PullDefault – 使用 SOC 定義的上電預設 pull 配置
- 上拉 – 啟用上拉電阻
- 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 控制器驅動程式能夠將第 2 與第 3 腳位(由 \_SB.GPIO0 的裝置節點管理)設為功能 4,並啟用上拉電阻。
支援 GpioClx 用戶端驅動程式中的多工支援
GpioClx 內建支援腳位多工。 GpioClx 迷你埠驅動程式(亦稱為「GpioClx 用戶端驅動程式」),驅動 GPIO 控制器硬體。 自 Windows 10 14327 版本起,GpioClx 迷你埠驅動程式可透過實作兩個新 DDI 來新增腳位多工(pin muxing)支援:
- CLIENT_ConnectFunctionConfigPins – 由
GpioClx呼叫,用以指示迷你埠驅動程式套用指定的多工組態。 - CLIENT_DisconnectFunctionConfigPins – 由
GpioClx呼叫,以要求 miniport 驅動程式還原多工組態。
有關這些例程的說明,請參見 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。 一般而言,斷開例程應撤銷 Connect 例程所執行的操作。
在 SpbCx 與 SerCx 控制器驅動程式中支援多工處理
自 Windows 10 組建 14327 起,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 功能;而當周邊驅動程式關閉其控制代碼時,腳位就會切換離開 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
如果沒有註冊金鑰,那你的系統裡就不存在 rhproxy。 Rhproxy 存在於所有 IoT Core 及 Windows Enterprise 版本 15063 及以後版本中。
使用 ACPITABL.dat 編譯並載入 ASL
既然你已經寫出 rhproxy ASL 節點,就該編譯並載入它了。 你可以將 rhproxy 節點編譯成獨立的 AML 檔案,並附加到系統的 ACPI 資料表中。 或者,如果你能存取系統的 ACPI 來源,也可以直接將 rhproxy 節點插入平台的 ACPI 資料表。 不過,在初始啟用階段,使用 ACPITABL.dat 可能會比較容易。
建立一個名為 yourboard.asl 的檔案,並將 RHPX 裝置節點放入 DefinitionBlock:
DefinitionBlock ("ACPITABL.dat", "SSDT", 1, "MSFT", "RHPROXY", 1) { Scope (\_SB) { Device(RHPX) { ... } } }下載 WDK,並在
C:\Program Files (x86)\Windows Kits\10\Tools\x64\ACPIVerify找到asl.exe執行以下指令以產生ACPITABL.dat:
asl.exe yourboard.asl將產生的 ACPITABL.dat 檔案複製到 你正在測試的系統上的 c:\windows\system32。
在受測系統上開啟測試簽署模式:
bcdedit /set testsigning on重新啟動被測系統。 系統會將ACPITABL.dat中定義的 ACPI 表格附加到系統韌體表格中。
確認 rhproxy 裝置節點是否存在
執行以下指令以枚舉 rhproxy 裝置節點。
devcon status *msft8000
devcon 的輸出應該會顯示該裝置存在。 若裝置節點不存在,則表示 ACPI 資料表未成功加入系統。
確認 rhproxy 是否正在載入並啟動
請查看 rhproxy 的狀態:
devcon status *msft8000
如果輸出顯示 rhproxy 已啟動,則表示 rhproxy 已載入並成功啟動。 如果你發現有問題的程式碼,就需要調查。 一些常見的問題代碼包括:
- 問題 51 -
CM_PROB_WAITING_ON_DEPENDENCY- 系統未啟動 rhproxy,因為其其中一個相依元件載入失敗。 這表示傳給 rhproxy 的資源要麼指向無效的 ACPI 節點,要麼目標裝置無法啟動。 首先,請再次確認所有裝置是否都能正常運作(詳見上文「驗證控制器驅動程式」)。 然後,再三確認你的 ASL,確保所有資源路徑(例如\_SB.I2C1)都正確,並且指向 DSDT 中的有效節點。 - 問題 10 -
CM_PROB_FAILED_STARTRhproxy 無法啟動,很可能是因為資源解析問題。 檢查你的 ASL 並再次確認 DSD 中的資源索引,並確認 GPIO 資源的指定順序是否依針位遞增順序排列。
確認預期的裝置是否可供使用者模式存取
現在 rhproxy 已經運行,應該已經建立了使用者模式可以存取的裝置介面。 我們會使用多個命令列工具來枚舉裝置並確認它們是否存在。
複製 https://github.com/ms-iot/samples 存放庫,並建置 GpioTestTool、I2cTestTool、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測驗
下載Hardware Lab Kit(HLK)。 以下檢測可供使用:
- GPIO WinRT 功能與壓力測試
- I2C WinRT 寫入測試(需 EEPROM)
- I2C WinRT 讀取測試(需 EEPROM)
- I2C WinRT 不存在從屬位址測試
- I2C WinRT 進階功能測試(需要 mbed LPC1768)
- SPI WinRT 時脈頻率驗證測試(需要 mbed LPC1768)
- SPI WinRT IO 傳輸測試(必須具備 mbed LPC1768)
- SPI WinRT 步距驗證測試
- SPI WinRT 傳輸間隙偵測測試(需 MBED LPC1768)
當你在 HLK 管理器中選擇 rhproxy 裝置節點時,適用的測試會自動被選中。
在 HLK 管理器中,選擇「資源集線器代理裝置」:
然後點選測試標籤,選擇 I2C WinRT、Gpio WinRT 和 Spi WinRT 測試。
按一下「執行所選項目」。 每項測試的詳細文件可透過右鍵點擊測試並點擊「測試說明」來取得。
資源
- ACPI 5.0 規範
- Asl.exe(Microsoft ASL 編譯器)
- Windows.Devices.Gpio
- Windows.Devices.I2c
- Windows.Devices.Spi
- Windows.Devices.SerialCommunication
- 測試撰寫與執行架構 (TAEF)
- SpbCx
- GpioClx
- SerCx
- MITT I2C 測試
- GpioTestTool
- I2cTestTool
- SpiTestTool
- MinComm(串列)
- Hardware Lab Kit (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 清單
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;
}