GPIO、I2C、SPI へのユーザー モード アクセスの有効化

Windows 10 以降では、API は、ユーザー モードから汎用入出力 (GPIO)、集積回路間通信 (I2C)、シリアル周辺機器インターフェイス (SPI)、およびユニバーサル非同期レシーバー送信機 (UART) への直接アクセスを提供します。 Raspberry Pi 2 のような開発ボードは、特定のアプリケーションに対処するためにカスタム回路を使って基本計算モジュールを拡張できるようにする、これらの接続のサブセットを公開しています。 これらの低レベルのバスは、通常、他の重要なオンボード機能と共有され、GPIO ピンとバスのサブセットのみがヘッダーに公開されます。 システムの安定性を維持するために、どのピンとバスの変更が安全かをユーザー モード アプリケーションで指定する必要があります。

このドキュメントでは、Advanced Configuration and Power Interface (ACPI) でこの構成を指定する方法を説明し、構成が正しく指定されていることを検証するためのツールを提供します。

重要

このドキュメントの対象ユーザーは、Unified Extensible Firmware Interface (UEFI) と ACPI の開発者です。 ACPI、ACPI ソース言語 (ASL) 作成、SpbCx/GpioClx についてある程度の知識があることを前提としています。

Windows での低レベル バスへのユーザー モード アクセスは、既存の GpioClx および SpbCx フレームワークを通じて組み込まれています。 Windows IoT Core と Windows Enterprise で使用可能な RhProxy という新しいドライバーが、GpioClx リソースと SpbCx リソースをユーザー モードに公開します。 API を有効にするには、ユーザー モードに公開する GPIO リソースと SPB リソースのそれぞれで、ACPI テーブル内で rhproxy 用のデバイス ノードが宣言されている必要があります。 このドキュメントでは、ASL の作成と検証について説明します。

例による ASL

Raspberry Pi 2 の rhproxy デバイス ノード宣言について説明します。 最初に、\_SB スコープで ACPI デバイス宣言を作成します。

Device(RHPX)
{
    Name(_HID, "MSFT8000")
    Name(_CID, "MSFT8000")
    Name(_UID, 1)
}
  • _HID – ハードウェア ID。これをベンダー固有のハードウェア ID に設定します。
  • _CID – 互換性のある ID。"MSFT8000" である必要があります。
  • _UID – 一意の ID。1 に設定します。

次に、ユーザー モードに公開する GPIO リソースと SPB リソースをそれぞれ宣言します。 リソースインデックスはプロパティをリソースに関連付けるために使用されるため、リソースが宣言される順序は重要です。 複数の I2C バスまたは SPI バスが公開されている場合、最初に宣言されたバスはその型の ‘既定’ のバスと見なされ、 Windows.Devices.I2c.I2cController および windows.Devices.Spi.SpiControllerGetDefaultAsync() メソッドによって返されるインスタンスになります。

SPI

Raspberry Pi には、2 つの公開 SPI バスがあります。 SPI0 には 2 つのハードウェア チップ選択ラインがあり、SPI1 には 1 つのハードウェア チップ選択ラインがあります。 各バスのチップ選択行ごとに 1 つの SPISerialBus() リソース宣言が必要です。 次の 2 つの SPISerialBus リソース宣言は、SPI0 上の 2 つのチップ選択ライン用です。 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

ソフトウェアは、これら 2 つのリソースを同じバスに関連付ける必要があることをどのように認識していますか? バスフレンドリ名とリソース インデックスの間のマッピングは、DSD で指定されます:

Package(2) { "bus-SPI-SPI0", Package() { 0, 1 }},

これにより、"SPI0" という名前のバスが作成され、2 つのチップ選択ライン (リソース インデックス 0 と 1) が作成されます。 SPI バスの機能を宣言するには、さらにいくつかのプロパティが必要です。

Package(2) { "SPI0-MinClockInHz", 7629 },
Package(2) { "SPI0-MaxClockInHz", 125000000 },

MinClockInHz プロパティと MaxClockInHz プロパティは、コントローラーでサポートされる最小および最大クロック速度を指定します。 この API では、ユーザーがこの範囲外の値を指定できなくなります。 クロック速度は、接続記述子 (ACPI セクション 6.4.3.8.2.2)の _SPE フィールドで SPB ドライバーに渡されます。

Package(2) { "SPI0-SupportedDataBitLengths", Package() { 8 }},

SupportedDataBitLengths プロパティには、コントローラーでサポートされているデータ ビットの長さが一覧表示されます。 コンマ区切りリストで複数の値を指定できます。 この API を使用すると、ユーザーがこのリスト以外の値を指定できなくなります。 データ ビットの長さは、接続記述子 (ACPI セクション 6.4.3.8.2.2) の _LEN フィールドで SPB ドライバーに渡されます。

これらのリソース宣言は、「テンプレート」と考えることができます。 一部のフィールドはシステム起動時に固定され、他のフィールドは実行時に動的に指定されます。 SPISerialBus 記述子の次のフィールドは固定されています:

  • DeviceSelection
  • DeviceSelectionPolarity
  • WireMode
  • SlaveMode
  • ResourceSource

次のフィールドは、実行時にユーザーによって指定された値のプレースホルダーです:

  • DataBitLength
  • ConnectionSpeed
  • ClockPolarity
  • ClockPhase

SPI1 には 1 つのチップ選択行のみが含まれるため、1 つの 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 に 1 つの 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 }},

これにより、上記で宣言した I2CSerialBus() リソースのインデックスであるリソース インデックス 3 を参照するフレンドリ名 "I2C1" を持つ I2C バスが宣言されます。

I2CSerialBus() 記述子の次のフィールドは固定されています:

  • SlaveMode
  • ResourceSource

次のフィールドは、実行時にユーザーによって指定された値のプレースホルダーです。

  • SlaveAddress
  • ConnectionSpeed
  • AddressingMode

I2C ドライバーの要件

  • SpbCx を使用するか、SpbCx と互換性がある必要があります
  • MITT I2C テストに合格している必要があります
  • 7 ビット アドレス指定をサポートする必要があります
  • 100kHz クロック速度をサポートする必要があります
  • 400kHz クロック速度をサポートする必要があります

GPIO

次に、ユーザー モードに公開されるすべての GPIO ピンを宣言します。 公開するピンを決定する際には、次のガイダンスを提供します:

  • 公開されているヘッダーのすべてのピンを宣言します。
  • ボタンや LED などの便利なオンボード機能に接続されているピンを宣言します。
  • システム機能用に予約されているか、何にも接続されていないピンは宣言しないでください。

ASL の次のブロックでは、GPIO4 と GPIO5 の 2 つのピンが宣言されています。 簡潔にするために、他のピンはここには示されていません。 付録 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 コールバックに応答して、CLIENT_CONTROLLER_BASIC_INFORMATION 構造体で MemoryMappedController フラグを設定する場合、メモリ マップコントローラーです。
  • 各ピンには、GpioIO リソースと GpioInt リソースの両方が必要です。 GpioInt リソースは、GpioIOリソースの直後にあり、同じピン番号を参照する必要があります。
  • GPIO リソースは、ピン番号を増やすことで順序付けする必要があります。
  • 各 GpioIO リソースと GpioInt リソースには、ピン リストにピン番号を 1 つだけ含む必要があります。
  • 両方の記述子の 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 ピンは、(オープンドレインではなく)強い高値と強い安値の両方を生成することをサポートしています。

InputHighImpedance と OutputCmos は、ほぼすべての GPIO コントローラーでサポートされています。 SupportedDriveModes プロパティが指定されていない場合は、これが既定値です。

GPIO 信号が公開されたヘッダーに到達する前にレベル シフターを通過する場合は、外部ヘッダーでドライブ モードを監視できない場合でも、SOC でサポートされるドライブ モードを宣言します。 たとえば、ピンが双方向レベルシフターを通過し、抵抗プルアップでピンがオープンドレインとして表示される場合、ピンが高インピーダンス入力として構成されている場合でも、露出したヘッダーで高インピーダンス状態が観察されることはありません。 ピンが高インピーダンス入力をサポートしていることを宣言する必要があります。

ピン番号付け

Windows では、次の 2 つのピン番号付けスキームがサポートされています:

  • シーケンシャル ピン番号付け – ユーザーには露出したピンの数まで 0、1、2... などの番号が表示されます。 0 は ASL で宣言された最初の GpioIo リソース、1 は ASL で宣言された 2 番目の GpioIo リソースなどです。
  • ネイティブのピンの番号付け – 4、5、12、13 … などの、GpioIo 記述子で指定されたピン番号がユーザーに表示されます。
Package (2) { “GPIO-UseDescriptorPinNumbers”, 1 },

UseDescriptorPinNumbers プロパティは、連続するピン番号ではなく、ネイティブのピン番号を使用するように Windows に指示します。 UseDescriptorPinNumbers プロパティが指定されていない場合、またはその値が 0 の場合、Windows は既定でシーケンシャル ピン番号付けになります。

ネイティブのピン番号を使用する場合は、PinCount プロパティも指定する必要があります。

Package (2) { “GPIO-PinCount”, 54 },

PinCount プロパティは、 GpioClx ドライバーの CLIENT_QueryControllerBasicInformation コールバックの TotalPins プロパティから返される値と一致する必要があります。

ボードの既存の発行済みドキュメントと最も互換性のある番号付けスキームを選択します。 たとえば、Raspberry Pi では、既存のピン配置図の多くが BCM2835 ピン番号を使用するため、ネイティブピン番号が使用されます。 MinnowBoardMax では、既存のピン配置図がほとんどないため、シーケンシャル ピン番号が使用されます。また、10 個のピンのみが 200 を超えるピンから公開されるため、シーケンシャル ピンの番号付けによって開発者エクスペリエンスが簡略化されます。 シーケンシャルまたはネイティブのピン番号を使用する決定は、開発者の混乱を減らすことを目的とする必要があります。

GPIO ドライバーの要件

  • GpioClx を使う必要があります
  • SOC 上のメモリ がマップされている必要があります
  • エミュレートされた ActiveBoth 割り込み処理を使用する必要があります

UART

UART ドライバーが SerCx または SerCx2 を使用する場合、rhproxy を使用してこのドライバーをユーザー モードに公開することができます。 タイプ GUID_DEVINTERFACE_COMPORT のデバイス インターフェイスを作成する UART ドライバーでは、rhproxy を使用する必要はありません。 受信トレイ Serial.sys ドライバーは、このようなケースの 1 つです。

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 上の同じ物理ピンにルーティングされる場合があります。 mux ブロックは、任意の時点でピンでアクティブな機能を制御します。 従来、ファームウェアは起動時に関数の割り当てを確立する役割を担い、この割り当てはブート セッションを通じて静的なままです。 実行時のピンの多重化により、実行時にピン関数の割り当てを再構成する機能が追加されます。 実行時にユーザーがピンの機能を選択できるようにすることで、ユーザーがボードのピンをすばやく再構成できるようになるため、開発が高速化され、ハードウェアは静的な構成よりも広範なアプリケーションをサポートできるようになります。

ユーザーは、追加のコードを記述することなく、GPIO、I2C、SPI、UART の多重化サポートを使用します。 ユーザーが OpenPin() または FromIdAsync() を使用して GPIO またはバスを開くと、基になる物理ピンが要求された関数に自動的に多重化されます。 ピンが既に別の関数で使用されている場合、OpenPin() または FromIdAsync() の呼び出しは失敗します。 ユーザーが GpioPinI2cDeviceSpiDevice、または SerialDevice オブジェクトを破棄してデバイスを閉じると、ピンが解放され、後で別の機能で開くことができるようになります。

Windows には、GpioClxSpbCx、および 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 要求を送信して構成をコミットする 必要があります。 クライアントはハンドルを閉じてピンの多重化リソースを解放します。その時点で、多重化構成は既定の状態に戻ります。
  • ACPI ファームウェア – MsftFunctionConfig() リソースを使用した多重化構成を指定します。 MsftFunctionConfig リソースは、クライアントで多重化構成が必要なピンを表します。 MsftFunctionConfig リソースには、関数番号、プル構成、およびピン番号の一覧が含まれています。 MsftFunctionConfig リソースは、GPIO および SPB 接続リソースと同様に、ドライバーが PrepareHardware コールバックで受信するハードウェア リソースとして多重化クライアントをピン留めするために提供されます。 クライアントは、リソースへのハンドルを開くために使用できるリソース ハブ ID を受け取ります。

/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() を使用してリソース ID からパスを作成し、パスへのハンドルを開きます (ZwCreateFile()IoGetDeviceObjectPointer()、または WdfIoTargetOpen() を使用します)。
  3. サーバーは、リソース ハブ ヘルパー関数 RESOURCE_HUB_ID_FROM_FILE_NAME() を使用してファイル パスからリソース ハブ ID を抽出し、リソース ハブにクエリを実行してリソース記述子を取得します。
  4. サーバーは、記述子内の各ピンに対して共有の判別を実行し、IRP_MJ_CREATE 要求を完了します。
  5. クライアントは、受信したハンドルに対して IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS 要求を発行します。
  6. IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS に応答して、サーバーは、指定された機能を各ピンでアクティブにすることで、ハードウェアの多重化操作を実行します。
  7. クライアントは、要求されたピンの多重化構成に依存する操作を続行します。
  8. クライアントがピンを多重化する必要がなくなったら、ハンドルを閉じます。
  9. 閉じているハンドルに応答して、サーバーはピンを初期状態に戻します。

ピンの多重化クライアントのプロトコルの説明

このセクションでは、クライアントがピンの多重化機能を使用する方法について説明します。 フレームワークはコントローラー ドライバーの代わりにこのプロトコルを実装するため、これは、SerCx および SpbCx コントローラー ドライバーには適用されません。

リソースの解析

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() 呼び出してリソース ハンドルを閉じるか、WDFIOTARGET を再利用する場合は WdfIoTargetClose() を呼び出す必要があります。

    WdfObjectDelete(resourceHandle);

クライアントがリソース ハンドルを閉じると、ピンは初期状態に戻され、別のクライアントによって取得できるようになります。

ピンの多重化サーバーのプロトコルの説明

このセクションでは、ピンの多重化サーバーがその機能をクライアントに公開する方法について説明します。 フレームワークはクライアント ドライバーの代わりにこのプロトコルを実装するため、これは GpioClx ミニポート ドライバーには適用されません。 GpioClx クライアント ドライバーでピンの多重化をサポートする方法の詳細については、「GpioClx クライアント ドライバーでの多重化サポートの実装」を参照してください。

IRP_MJ_CREATE 要求の処理

クライアントは、ピンの多重化リソースを予約するときに、リソースへのハンドルを開きます。 ピンの多重化サーバーは、リソース ハブからの再解析操作を使用して、IRP_MJ_CREATE 要求を受け取ります。 IRP_MJ_CREATE 要求の末尾のパス コンポーネントには、16 進数形式の 64 ビット整数であるリソース ハブ ID が含まれています。 サーバーは reshub.h から RESOURCE_HUB_ID_FROM_FILE_NAME() を使用してファイル名からリソース ハブ ID を抽出し、リソース ハブに IOCTL_RH_QUERY_CONNECTION_PROPERTIES を送信して MsftFunctionConfig() 記述子を取得する必要があります。

サーバーは記述子を検証し、記述子から共有モードとピンリストを抽出する必要があります。 その後、ピンの共有判定を実行し、成功した場合は、要求を完了する前にピンを予約済みとしてマークします。

ピンリスト内の各ピンに対して共有判定が成功した場合、共有判定が全体的に成功します。 各ピンは、次のように裁定する必要があります:

  • ピンがまだ予約されていない場合、共有の判定は成功します。
  • ピンが既に排他的として予約されている場合、共有の判定は失敗します。
  • ピンが既に共有として予約されている場合は、
    • 受信要求が共有されると、共有の判定が成功します。
    • 受信要求が排他的である場合、共有の判定は失敗します。

共有の判定が失敗した場合は、STATUS_GPIO_INCOMPATIBLE_CONNECT_MODE を使用して要求を完了する必要があります。 共有の判定が成功した場合、要求は STATUS_SUCCESS で完了する必要があります。

着信要求の共有モードは、IrpSp->Parameters.Create.ShareAccess ではなく MsftFunctionConfig 記述子から取得する必要があることに注意してください。

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 テーブルの作成ガイドライン

このセクションでは、多重化リソースをクライアント ドライバーに提供する方法について説明します。 MsftFunctionConfig() リソースを含むテーブルをコンパイルするには、Microsoft ASL コンパイラ ビルド 14327 以降が必要であることに注意してください。 MsftFunctionConfig() リソースは、ピンの多重化のクライアントにハードウェア リソースとして提供されます。 MsftFunctionConfig() リソースは、ピンの多重化の変更が必要なドライバーに提供する必要があります。これは通常 SPB およびシリアル コントローラー ドライバーですが、コントローラー ドライバーが多重化構成を扱うため、SPB およびシリアル周辺機器ドライバーには提供しません。 MsftFunctionConfig() ACPI マクロは次のように定義されます:

  MsftFunctionConfig(Shared/Exclusive
                PinPullConfig,
                FunctionNumber,
                ResourceSource,
                ResourceSourceIndex,
                ResourceConsumer/ResourceProducer,
                VendorData) { Pin List }

  • 共有/排他 – 排他的な場合、このピンは一度に 1 つのクライアントによって取得できます。 共有されている場合、複数の共有クライアントがリソースを取得できます。 複数の調整されていないクライアントが変更可能なリソースにアクセスできるようにすると、データ競合が発生し、予測できない結果になる可能性があるため、常にこれを排他的に設定してください。
  • PinPullConfig – 次のいずれか
    • PullDefault – SOC で定義された電源オンの既定のプル構成を使用
    • プルアップ – プルアップ抵抗を有効にする
    • プルダウン - プルダウン抵抗を有効にする
    • PullNone – すべてのプル抵抗を無効にする
  • FunctionNumber – 多重化にプログラムする関数番号。
  • ResourceSource – ピンの多重化サーバーの ACPI 名前空間パス
  • ResourceSourceIndex – これを 0 に設定する
  • ResourceConsumer/ResourceProducer – これを ResourceConsumer に設定する
  • VendorData – ピンの多重化サーバーによって意味が定義されている省略可能なバイナリ データ。 通常、これは空白のままにする必要があります
  • Pin List – 構成が適用されるピン番号のコンマ区切りリスト。 ピンの多重化サーバーが 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 ミニポート ドライバーは、次の 2 つの新しい DDI を実装することで、ピンの多重化のサポートを追加できます:

  • CLIENT_ConnectFunctionConfigPins – 指定した多重化構成を適用するミニポート ドライバーをコマンドするために GpioClx によって呼び出されます。
  • CLIENT_DisconnectFunctionConfigPins – ミニポート ドライバーに多重化構成を元に戻すようコマンドするために GpioClx によって呼び出されます。

これらのルーチンの説明については、「GpioClx イベント コールバック関数」を参照してください。

これら 2 つの新しい DDI に加えて、ピンの多重化の互換性のために既存の DDI を監査する必要があります:

  • CLIENT_ConnectIoPins/CLIENT_ConnectInterrupt – CLIENT_ConnectIoPins は、ミニポート ドライバーに GPIO 入力または出力のセット ピンを構成するように命令するために GpioClx によって呼び出されます。 GPIO は MsftFunctionConfig と相互に排他的であり、GPIO と MsftFunctionConfig に対してピンが同時に接続されることはありません。 ピンの既定の関数は GPIO である必要がないため、ConnectIoPins が呼び出されたときにピンが GPIO に多重化されないとは限りません。 ConnectIoPins は、多重化操作を含め、ピンを GPIO IO に対応させるために必要なすべての操作を実行する必要があります。 割り込みは GPIO 入力の特殊なケースと考えることができるため、CLIENT_ConnectInterrupt も同様に動作する必要があります。
  • 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 ドライバーへの依存関係が導入されます。これは通常、多重化を担当します。

Pin muxing dependency

デバイスの初期化時に、SpbCx フレームワークと SerCx フレームワークは、デバイスにハードウェア リソースとして提供されるすべての 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 ハンドラー内のすべてのリソースを解放します。

SerCx および SpbCx コントローラー ドライバーの動的ピン多重化の影響は、特定の時点で SPB/UART 機能からピンが多重化されることを許容できる必要があることです。 コントローラー ドライバーは、EvtSpbTargetConnect() または EvtSerCx2FileOpen() が呼び出されるまでピンが多重化されないことを想定する必要があります。 ピンは、次のコールバック中に SPB/UART 関数に多重化する必要はありません。 以下は完全なリストではありませんが、コントローラー ドライバーによって実装される最も一般的な PNP ルーチンを表しています。

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

検証

rhproxy をテストする準備ができたら、次の手順を使用すると便利です。

  1. SpbCxGpioClx、および SerCx コントローラー ドライバーが正しく読み込んで動作していることを確認します
  2. rhproxy がシステムに存在することを確認します。 Windows の一部のエディションとビルドには含まれていません。
  3. ACPITABL.dat を使用して rhproxy ノードをコンパイルして読み込みます。
  4. rhproxy デバイス ノードが存在することを確認する
  5. rhproxy が読み込まれて開始されていることを確認する
  6. 想定されているデバイスがユーザー モードに公開されていることを確認します。
  7. コマンド ラインから各デバイスと対話できることを確認する
  8. UWP アプリから各デバイスと対話できることを確認する
  9. HLK テストを実行する

コントローラー ドライバーを確認する

rhproxy は、システム上の他のデバイスもユーザー モードに公開するため、それらのデバイスが既に動作している場合にのみ動作します。 最初の手順では、公開する I2C、SPI、GPIO コントローラーのデバイスが既に動作していることを確認します。

コマンド プロンプトで

devcon status *

出力を確認し、対象のすべてのデバイスが開始されていることを確認します。 デバイスに問題のあるコードがある場合は、そのデバイスが読み込まれていない理由をトラブルシューティングする必要があります。 すべてのデバイスは、最初のプラットフォームの起動中に有効になっている必要があります。 SpbCxGpioClx、または SerCx コントローラー ドライバーのトラブルシューティングは、このドキュメントの範囲外です。

rhproxy がシステムに存在することを確認する

rhproxy サービスがシステムに存在することを確認します。

reg query HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\rhproxy

reg キーが存在しない場合、rhproxy はシステムに存在しません。 Rhproxy は、IoT Core および Windows Enterprise ビルド 15063 以降のすべてのビルドに存在します。

ACPITABL.dat を使用して ASL をコンパイルして読み込む

rhproxy ASL ノードを作成したので、コンパイルして読み込みます。 rhproxy ノードは、システム ACPI テーブルに追加できるスタンドアロン AML ファイルにコンパイルできます。 または、システムの ACPI ソースにアクセスできる場合は、rhproxy ノードをプラットフォームの ACPI テーブルに直接挿入できます。 しかし、最初の持ち込み中に ACPITABL.dat を使用する方が簡単な場合があります。

  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\ACPIVerifyasl.exe を検索します。

  3. 次のコマンドを実行して AC ACPITABL.dat を生成します:

    asl.exe yourboard.asl
    
  4. テスト対象のシステム上の c:\windows\system32 に、結果として得られる ACPITABL.dat ファイルをコピーします。

  5. テスト対象のシステムで testsigning を有効にします:

    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 - 依存関係の 1 つが読み込みに失敗したため、システムが 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 テストを実行する

Hardware Lab Kit (HLK) をダウンロードします。 次のテストを使用できます:

HLK マネージャーで rhproxy デバイス ノードを選択すると、該当するテストが自動的に選択されます。

HLK マネージャーで、[リソース ハブ プロキシ デバイス] を選択します:

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 Max のピン マッピング」も参照してください。

DefinitionBlock ("ACPITABL.dat", "SSDT", 1, "MSFT", "RHPROXY", 1)
{
    Scope (\_SB)
    {
        Device(RHPX)
        {
            Name(_HID, "MSFT8000")
            Name(_CID, "MSFT8000")
            Name(_UID, 1)

            Name(_CRS, ResourceTemplate()
            {
                // Index 0
                SPISerialBus(            // Pin 5, 7, 9 , 11 of JP1 for SIO_SPI
                    1,                     // Device selection
                    PolarityLow,           // Device selection polarity
                    FourWireMode,          // wiremode
                    8,                     // databit len
                    ControllerInitiated,   // slave mode
                    8000000,               // Connection speed
                    ClockPolarityLow,      // Clock polarity
                    ClockPhaseSecond,      // clock phase
                    "\\_SB.SPI1",          // ResourceSource: SPI bus controller name
                    0,                     // ResourceSourceIndex
                    ResourceConsumer,      // Resource usage
                    JSPI,                  // DescriptorName: creates name for offset of resource descriptor
                    )                      // Vendor Data

                // Index 1
                I2CSerialBus(            // Pin 13, 15 of JP1, for SIO_I2C5 (signal)
                    0xFF,                  // SlaveAddress: bus address
                    ,                      // SlaveMode: default to ControllerInitiated
                    400000,                // ConnectionSpeed: in Hz
                    ,                      // Addressing Mode: default to 7 bit
                    "\\_SB.I2C6",          // ResourceSource: I2C bus controller name (For MinnowBoard Max, hardware I2C5(0-based) is reported as ACPI I2C6(1-based))
                    ,
                    ,
                    JI2C,                  // Descriptor Name: creates name for offset of resource descriptor
                    )                      // VendorData

                // Index 2
                UARTSerialBus(           // Pin 17, 19 of JP1, for SIO_UART2
                    115200,                // InitialBaudRate: in bits ber second
                    ,                      // BitsPerByte: default to 8 bits
                    ,                      // StopBits: Defaults to one bit
                    0xfc,                  // LinesInUse: 8 1-bit flags to declare line enabled
                    ,                      // IsBigEndian: default to LittleEndian
                    ,                      // Parity: Defaults to no parity
                    ,                      // FlowControl: Defaults to no flow control
                    32,                    // ReceiveBufferSize
                    32,                    // TransmitBufferSize
                    "\\_SB.URT2",          // ResourceSource: UART bus controller name
                    ,
                    ,
                    UAR2,                  // DescriptorName: creates name for offset of resource descriptor
                    )

                // Index 3
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO2",) {0}  // Pin 21 of JP1 (GPIO_S5[00])
                // Index 4
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO2",) {0}

                // Index 5
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO2",) {1}  // Pin 23 of JP1 (GPIO_S5[01])
                // Index 6
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO2",) {1}

                // Index 7
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO2",) {2}  // Pin 25 of JP1 (GPIO_S5[02])
                // Index 8
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO2",) {2}

                // Index 9
                UARTSerialBus(           // Pin 6, 8, 10, 12 of JP1, for SIO_UART1
                    115200,                // InitialBaudRate: in bits ber second
                    ,                      // BitsPerByte: default to 8 bits
                    ,                      // StopBits: Defaults to one bit
                    0xfc,                  // LinesInUse: 8 1-bit flags to declare line enabled
                    ,                      // IsBigEndian: default to LittleEndian
                    ,                      // Parity: Defaults to no parity
                    FlowControlHardware,   // FlowControl: Defaults to no flow control
                    32,                    // ReceiveBufferSize
                    32,                    // TransmitBufferSize
                    "\\_SB.URT1",          // ResourceSource: UART bus controller name
                    ,
                    ,
                    UAR1,              // DescriptorName: creates name for offset of resource descriptor
                    )

                // Index 10
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {62}  // Pin 14 of JP1 (GPIO_SC[62])
                // Index 11
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {62}

                // Index 12
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {63}  // Pin 16 of JP1 (GPIO_SC[63])
                // Index 13
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {63}

                // Index 14
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {65}  // Pin 18 of JP1 (GPIO_SC[65])
                // Index 15
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {65}

                // Index 16
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {64}  // Pin 20 of JP1 (GPIO_SC[64])
                // Index 17
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {64}

                // Index 18
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {94}  // Pin 22 of JP1 (GPIO_SC[94])
                // Index 19
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {94}

                // Index 20
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {95}  // Pin 24 of JP1 (GPIO_SC[95])
                // Index 21
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {95}

                // Index 22
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {54}  // Pin 26 of JP1 (GPIO_SC[54])
                // Index 23
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {54}
            })

            Name(_DSD, Package()
            {
                ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
                Package()
                {
                    // SPI Mapping
                    Package(2) { "bus-SPI-SPI0", Package() { 0 }},

                    Package(2) { "SPI0-MinClockInHz", 100000 },
                    Package(2) { "SPI0-MaxClockInHz", 15000000 },
                    // SupportedDataBitLengths takes a list of support data bit length
                    // Example : Package(2) { "SPI0-SupportedDataBitLengths", Package() { 8, 7, 16 }},
                    Package(2) { "SPI0-SupportedDataBitLengths", Package() { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 }},
                     // I2C Mapping
                    Package(2) { "bus-I2C-I2C5", Package() { 1 }},
                    // UART Mapping
                    Package(2) { "bus-UART-UART2", Package() { 2 }},
                    Package(2) { "bus-UART-UART1", Package() { 9 }},
                }
            })
        }
    }
}

付録 C - GPIO リソースを生成するための PowerShell スクリプトのサンプル

次のスクリプトを使用して、Raspberry Pi の GPIO リソース宣言を生成できます:

$pins = @(
    @{PinNumber=4;PullConfig='PullUp'},
    @{PinNumber=5;PullConfig='PullUp'},
    @{PinNumber=6;PullConfig='PullUp'},
    @{PinNumber=12;PullConfig='PullDown'},
    @{PinNumber=13;PullConfig='PullDown'},
    @{PinNumber=16;PullConfig='PullDown'},
    @{PinNumber=18;PullConfig='PullDown'},
    @{PinNumber=22;PullConfig='PullDown'},
    @{PinNumber=23;PullConfig='PullDown'},
    @{PinNumber=24;PullConfig='PullDown'},
    @{PinNumber=25;PullConfig='PullDown'},
    @{PinNumber=26;PullConfig='PullDown'},
    @{PinNumber=27;PullConfig='PullDown'},
    @{PinNumber=35;PullConfig='PullUp'},
    @{PinNumber=47;PullConfig='PullUp'})

# generate the resources
$FIRST_RESOURCE_INDEX = 4
$resourceIndex = $FIRST_RESOURCE_INDEX
$pins | % {
    $a = @"
// Index $resourceIndex - GPIO $($_.PinNumber) - $($_.Name)
GpioIO(Shared, $($_.PullConfig), , , , "\\_SB.GPI0", , , , ) { $($_.PinNumber) }
GpioInt(Edge, ActiveBoth, Shared, $($_.PullConfig), 0, "\\_SB.GPI0",) { $($_.PinNumber) }
"@
    Write-Host $a
    $resourceIndex += 2;
}