USB KMDF 機能ドライバーにおけるセレクティブ サスペンド

この記事では、KMDF 関数ドライバーが USB 選択的一時停止をサポートする方法について説明します。

USB ドライバーに、ユーザー モードで使用できない機能またはリソースが必要な場合は、KMDF 関数ドライバーを指定する必要があります。 KMDF ドライバーは、KMDF 初期化構造体に関連する値を設定し、適切なコールバック関数を指定することによって、選択的中断を実装します。 KMDF は、デバイスを一時停止および再開するために、下位ドライバーとの通信の詳細を処理します。

KMDF ドライバーでの選択的一時停止のガイドライン

選択的一時停止をサポートする KMDF ドライバーは、次のガイドラインに従う必要があります。

  • KMDF 関数ドライバーは、デバイス スタックの PPO である必要があります。 既定では、KMDF 関数ドライバーは PPO です。
  • 選択的一時停止をサポートする KMDF 関数ドライバーでは、電源管理されているキューまたは電源管理されていないキューを使用できます。 既定では、GPIO のキュー オブジェクトは電源管理されます。

電源ポリシーの所有権と KMDF USB ドライバー

既定では、USB デバイスの KMDF ファンクション ドライバーは、デバイス スタックの PPO です。 KMDF は、このドライバーの代わりに選択的な中断と再開を管理します。

KMDF ドライバーでの I/O キューの構成

選択的一時停止をサポートする KMDF 関数ドライバーでは、電源管理されているキューまたは電源管理されていないキューを使用できます。 通常、ドライバーは、受信デバイスの I/O 制御要求を受信するために電源管理されていないキューを構成し、読み取り、書き込み、およびその他の電源依存の要求を受信するように 1 つ以上の電源管理キューを構成します。 要求が電源管理キューに到着すると、KMDF は、ドライバーに要求を提示する前に、デバイスが D0 にあることを確認します。

デバイス スタックの PPO の上に階層化された KMDF フィルター ドライバーを作成する場合は、電源管理キューを使用しないでください。 その理由は、UMDF ドライバーの場合と同じです。 デバイスが中断されている間、フレームワークは電源管理キューからの要求を提示しないため、このようなキューを使用すると、デバイス スタックがストールする可能性があります。

KMDF 関数ドライバーの選択的一時停止メカニズム

KMDF は、USB 選択的一時停止をサポートするために必要なほとんどの作業を処理します。 I/O アクティビティを追跡し、アイドル タイマーを管理し、親ドライバー (Usbhub.sys または Usbccgp.sys) がデバイスを中断して再開するデバイス I/O 制御要求を送信します。

KMDF 関数ドライバーが選択的な中断をサポートしている場合、KMDF は、各デバイス オブジェクトが所有するすべての電源管理キューの I/O アクティビティを追跡します。 I/O カウントが 0 に達するたびに、フレームワークはアイドル タイマーを開始します。 既定のタイムアウト値は 5 秒です。

I/O 要求が、アイドルタイムアウト期間が経過する前にデバイス オブジェクトに属する電源管理キューに到着した場合、フレームワークはアイドル タイマーを取り消し、デバイスを中断しません。

アイドル タイマーの有効期限が切れると、KMDF は USB デバイスを中断状態にするために必要な要求を発行します。 関数ドライバーが USB エンドポイントで連続リーダーを使用する場合、リーダーの繰り返しポーリングは KMDF アイドル タイマーに対するアクティビティとしてカウントされません。 ただし、 EvtDeviceD0Exit コールバック関数では、USB ドライバーは、デバイスが動作状態でない間にドライバーが I/O 要求を送信しないようにするために、電源管理されていないキューによって供給される継続的リーダーとその他の I/O ターゲットを手動で停止する必要があります。 ターゲットを停止するために、ドライバーは WdfIoTargetStop を呼び出し、ターゲット アクションとして WdfIoTargetWaitForSentIoToComplete を指定します。 応答として、フレームワークは、ターゲットの I/O キュー内のすべての I/O 要求が完了し、関連付けられている I/O 完了コールバックが実行された後にのみ、I/O ターゲットを停止します。

既定では、KMDF はデバイスを D0 から移行し、アイドル設定でドライバーが指定したデバイスの電源状態に切り替えます。 KMDF は、移行の一環として、他の電源ダウン シーケンスの場合と同じ方法でドライバーの電源コールバック関数を呼び出します。

デバイスが中断されると、次のいずれかのイベントが発生すると、フレームワークによってデバイスが自動的に再開されます。

  • ドライバーの電源管理キューのいずれかに対して I/O 要求が到着します。
  • ユーザーは、デバイス マネージャーを使用して USB 選択的な中断を無効にします。
  • ドライバーは、「USB デバイスの中断の防止」の説明に従って、WdfDeviceStopIdle を呼び出します。

デバイスを再開するために、KMDF はデバイス スタックに電源投入要求を送信し、他の電源投入シーケンスの場合と同じ方法でドライバーのコールバック関数を呼び出します。

電源ダウンシーケンスと電源投入シーケンスに関連するコールバックの詳細については、「 WDF ドライバーのプラグ アンド プレイと電源管理 」ホワイト ペーパーを参照してください。

KMDF ファンクション ドライバーでの USB 選択的一時停止のサポート

KMDF 関数ドライバーで USB 選択的一時停止を実装するには:

  • アイドルタイムアウトなど、アイドル状態に関連する電源ポリシー設定を初期化します。
  • 必要に応じて、ドライバーが開いているハンドルまたはデバイスの I/O キューに関連しないその他の理由により、デバイスを中断してはならないと判断したときに、中断または再開操作を一時的に防止するロジックを含めます。
  • ヒューマン インターフェイス デバイス (HID) の USB ドライバーで、選択的な中断をサポートしていることを INF で示します。

KMDF 関数ドライバーでの電源ポリシー設定の初期化

USB 選択的一時停止のサポートを構成するには、KMDF ドライバーは 、WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS 構造体を使用します。 ドライバーは、最初に構造体を初期化する必要がありますし、ドライバーとそのデバイスの機能に関する詳細を提供するフィールドを設定できます。 通常、ドライバーは、 EvtDriverDeviceAdd または EvtDevicePrepareHardware 関数でこの構造体を埋めます。

WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS構造体を初期化するには

ドライバーは、デバイス オブジェクトを作成した後、 WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT 関数を使用して構造体を初期化します。 この関数は、次の 2 つの引数を受け取ります。

  • WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS 構造体を初期化するためのポインター。
  • 選択的中断のサポートを示す列挙値。 ドライバーは IdleUsbSelectiveSuspend を指定する必要があります。

ドライバーが IdleUsbSelectiveSuspend を指定した場合、関数は構造体のメンバーを次のように初期化します。

  • IdleTimeoutIdleTimeoutDefaultValue (現在 5000 ミリ秒または 5 秒) に設定されています。
  • UserControlOfIdleSettingsIdleAllowUserControl に設定されています。
  • EnabledWdfUseDefault に設定されています。これは、選択的一時停止が有効になっていることを示しますが、 UserControlOfIdleSettings メンバーが許可している場合、ユーザーはそれを無効にすることができます。
  • DxStatePowerDeviceMaximum に設定され、デバイスの報告された電源機能を使用して、アイドル状態のデバイスを移行する状態を決定します。

USB 選択的一時停止を構成するには

ドライバーが WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS 構造体を初期化した後、ドライバーは構造体内の他のフィールドを設定し、 WdfDeviceAssignS0IdleSettings を呼び出して、これらの設定をフレームワークに渡すことができます。 USB ファンクション ドライバーには、次のフィールドが適用されます。

  • IdleTimeout— フレームワークがデバイスをアイドル状態と見なす前に I/O 要求を受信せずに経過する必要がある間隔 (ミリ秒単位)。 ドライバーは ULONG 値を指定することも、既定値をそのまま使用することもできます。

  • UserControlOfIdleSettings - ユーザーがデバイスのアイドル設定を変更できるかどうか。 指定できる値は IdleDoNotAllowUserControl と IdleAllowUserControl です。

  • DxState— フレームワークがデバイスを中断するデバイスの電源状態。 指定できる値は、PowerDeviceD1、PowerDeviceD2、および PowerDeviceD3 です。

    USB ドライバーでは、この値の初期設定を変更しないでください。 WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT関数は、この値を PowerDeviceMaximum に設定します。これにより、フレームワークがデバイスの機能に基づいて正しい値を選択できるようになります。

次のコード スニペットは、Osrusbfx2 サンプル ドライバーの Device.c ファイルからのものです。

WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS idleSettings;
NTSTATUS    status = STATUS_SUCCESS;
//
// Initialize the idle policy structure.
//
WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&idleSettings, 
     IdleUsbSelectiveSuspend);
idleSettings.IdleTimeout = 10000; // 10 sec

status = WdfDeviceAssignS0IdleSettings(Device, &idleSettings);
if ( !NT_SUCCESS(status)) {
     TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
                 "WdfDeviceSetPowerPolicyS0IdlePolicy failed %x\n", 
                 status);
    return status;
}

この例では、ドライバーは IdleUsbSelectiveSuspend を指定してWDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INITを呼び出します。 ドライバーは IdleTimeout を 10,000 ミリ秒 (10 秒) に設定し、 DxStateUserControlOfIdleSettings のフレームワークの既定値を受け入れます。 その結果、フレームワークは、デバイスがアイドル状態のときに D3 状態に移行し、管理者特権を持つユーザーがデバイスのアイドル状態のサポートを有効または無効にできるようにする Device Manager プロパティ ページを作成します。 その後、ドライバーは WdfDeviceAssignS0IdleSettings を呼び出してアイドル状態のサポートを有効にし、これらの設定をフレームワークに登録します。

ドライバーは、デバイス オブジェクトを作成した後、いつでも WdfDeviceAssignS0IdleSettings を呼び出すことができます。 ほとんどのドライバーは 、最初は EvtDriverDeviceAdd コールバックからこのメソッドを呼び出しますが、これは常に可能であるとは限りませんが、望ましい場合もあります。 ドライバーが複数のデバイスまたはデバイスのバージョンをサポートしている場合、ドライバーは、ハードウェアを照会するまで、すべてのデバイス機能を認識していない可能性があります。 このようなドライバーは、EvtDevicePrepareHardware コールバックまで WdfDeviceAssignS0IdleSettings の呼び出しを延期できます。

WdfDeviceAssignS0IdleSettings への最初の呼び出し後、ドライバーは、アイドルタイムアウト値とデバイスがアイドル状態のデバイスの状態を変更できます。 1 つ以上の設定を変更するには、ドライバーは、前述のように別 のWDF_DEVICE_POWER_POLICY_IDLE_SETTINGS 構造体を初期化し、 WdfDeviceAssignS0IdleSettings をもう一度呼び出します。

USB デバイスの中断の防止

タイムアウト期間内に I/O 要求が存在しない場合 (通常、デバイスにハンドルが開いている場合やデバイスが充電されている場合) は、USB デバイスの電源を切らなくてもかまいません。 USB ドライバーは、 WdfDeviceStopIdle を呼び出し、デバイスの中断が再び許容される場合に WdfDeviceResumeIdle を呼び出すことによって、このような状況でアイドル状態のデバイスを中断するフレームワークを防ぐことができます。

WdfDeviceStopIdle はアイドル タイマーを停止します。 IdleTimeout 期間の有効期限が切れていない場合、デバイスがまだ中断されていない場合、フレームワークはアイドル タイマーを取り消し、デバイスを中断しません。 デバイスが既に中断されている場合、フレームワークはデバイスを動作状態に戻します。 WdfDeviceStopIdleでは、システムが Sx スリープ状態に変わると、フレームワークによってデバイスが中断されるのを防ぐことはありません。 その唯一の効果は、システムが S0 動作状態にある間にデバイスの中断を防ぐことです。 WdfDeviceResumeIdle がアイドル タイマーを再起動します。 これら 2 つのメソッドはデバイスの参照カウントを管理するため、ドライバーが WdfDeviceStopIdle を 複数回呼び出した場合、フレームワークは、ドライバーが WdfDeviceResumeIdle を同じ回数呼び出すまでデバイスを中断しません。 ドライバーは、最初に WdfDeviceStopIdle を呼び出さずに WdfDeviceResumeIdle を呼び出してはなりません。

レジストリ キーを含む (HID ドライバーのみ)

USB HID デバイスの KMDF 上位フィルタードライバーは、Microsoft が提供する HIDClass.sys ポートドライバーが HID スタックに対して選択的な中断を有効にできるよう、INF 内で選択的な中断をサポートしていることを示す必要があります。 INF には、次の文字列に示すように、SelectiveSuspendEnabled キーを追加し、その値を 1 に設定する AddReg ディレクティブを含める必要があります。

HKR,,"SelectiveSuspendEnabled",0x00000001,0x1

例については、WDK の Hidusbfx2.inx (\BuildNumber\Src\Hid\ Hidusbfx2\sys %WinDDK%) を参照してください。

KMDF ドライバーのリモート ウェイク サポート

選択的一時停止と同様に、KMDF にはウェイクアップのサポートが組み込まれているため、USB デバイスは、デバイスがアイドル状態で、システムが動作状態 (S0) またはスリープ状態 (S1 ~ S4) の間にスリープ解除信号をトリガーできます。 KMDF の用語では、これら 2 つの機能をそれぞれ "Wake from S0" と "wake from Sx" と呼ばれます。

USB デバイスの場合、ウェイクアップは、デバイス自体が低電力状態から動作状態への移行を開始できることを示すだけです。 したがって、USB の用語では、S0 からのウェイクと Sx からのウェイクは同じであり、"リモート ウェイク" と呼ばれます。

KMDF USB 関数ドライバーは、選択的な中断メカニズムの一部としてこの機能を提供するため、S0 からのウェイクをサポートするコードは必要ありません。 ただし、システムが Sx 内にある場合にリモート ウェイクをサポートするには、ファンクション ドライバーで次の操作を行う必要があります。

KMDF ドライバーは通常、 EvtDriverDeviceAdd または EvtDevicePrepareHardware 関数で USB 選択的一時停止のサポートを構成すると同時にウェイク サポートを構成します。

デバイスの機能の確認

KMDF USB 関数ドライバーは、アイドル状態とスリープ解除の電源ポリシー設定を初期化する前に、デバイスがリモート ウェイクをサポートしていることを確認する必要があります。 デバイスハードウェア機能に関する情報を取得するために、ドライバーは WDF_USB_DEVICE_INFORMATION 構造体を初期化し、 WdfUsbTargetDeviceRetrieveInformation を呼び出します。通常は、 EvtDriverDeviceAdd または EvtDevicePrepareHardware コールバックで呼び出します。

WdfUsbTargetDeviceRetrieveInformation の呼び出しでは、ドライバーは、デバイス オブジェクトにハンドルと初期化されたWDF_USB_DEVICE_INFORMATION構造体へのポインターを渡します。 関数から正常に戻ると、構造体の Traits フィールドには、デバイスがセルフパワーで動作し、高速で動作し、リモート ウェイクをサポートするかどうかを示すフラグが含まれます。

Osrusbfx2 KMDF サンプルの次の例は、このメソッドを呼び出して、デバイスがリモート ウェイクをサポートしているかどうかを判断する方法を示しています。 これらのコード行が実行された後、waitWakeEnable 変数には、デバイスがリモート ウェイクをサポートしている場合は TRUE、サポートされていない場合は FALSE が含まれます。

    WDF_USB_DEVICE_INFORMATION          deviceInfo;
// Retrieve USBD version information, port driver capabilities and device
// capabilities such as speed, power, etc.
//

WDF_USB_DEVICE_INFORMATION_INIT(&deviceInfo);

status = WdfUsbTargetDeviceRetrieveInformation(
                            pDeviceContext->UsbDevice,
                            &deviceInfo);
waitWakeEnable = deviceInfo.Traits & WDF_USB_DEVICE_TRAIT_REMOTE_WAKE_CAPABLE;

リモート ウェイクアップの有効化

USB 用語では、USB デバイスのDEVICE_REMOTE_WAKEUP機能が設定されている場合、リモート ウェイクアップに対して USB デバイスが有効になります。 USB 仕様に従って、ホスト ソフトウェアは、デバイスをスリープ状態にする "直前" にデバイスにリモート ウェイクアップ機能を設定する必要があります。 KMDF 関数ドライバーは、ウェイク設定を初期化するためにのみ必要です。 KMDF と Microsoft が提供する USB バス ドライバーは、I/O 要求を発行し、リモート ウェイクアップを有効にするために必要なハードウェア操作を処理します。

ウェイク設定を初期化するには

  1. WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INITを呼び出して、WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS構造体を初期化します。 この関数は、構造体の Enabled メンバーを WdfUseDefault に設定し、 DxState メンバーを PowerDeviceMaximum に設定し、 UserControlOfWakeSettings メンバーを WakeAllowUserControl に設定します。
  2. 初期化された構造体を使用して WdfDeviceAssignSxWakeSettings を呼び出します。 その結果、デバイスは D3 状態から復帰でき、ユーザーはデバイス マネージャーのデバイス プロパティ ページからウェイク シグナルを有効または無効にできます。

Osrusbfx2 サンプルの次のコード スニペットは、ウェイク設定を既定値に初期化する方法を示しています。

WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS wakeSettings;

WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT(&wakeSettings);
status = WdfDeviceAssignSxWakeSettings(Device, &wakeSettings);
if (!NT_SUCCESS(status)) {
    return status;
}

選択的一時停止をサポートする USB デバイスの場合、基になるバス ドライバーは、スリープ解除するデバイス ハードウェアを準備します。 そのため、USB 関数ドライバーで EvtDeviceArmWakeFromS0 コールバックが必要になることはほとんどありません。 フレームワークは、アイドルタイムアウトの有効期限が切れると、USB バス ドライバーに選択的な一時停止要求を送信します。

同じ理由から、USB 関数ドライバーでEvtDeviceWakeFromS0TriggeredまたはEvtDeviceWakeFromSxTriggeredコールバックが必要になることはほとんどありません。 代わりに、フレームワークと基になるバス ドライバーは、デバイスを動作状態に戻すすべての要件を処理します。