注
この記事は、デバイス ドライバー開発者向けです。 USB デバイスで問題が発生している場合は、「Windows で USB-C の問題を解決する」を参照してください。
USB 選択的一時停止機能を使用すると、ハブ ドライバーは、ハブ上の他のポートの操作に影響を与えることなく、個々のポートを中断できます。 この機能は、バッテリの電力を節約するのに役立つポータブル コンピューターで役立ちます。 生体認証スキャナーなどの多くのデバイスでは、断続的に電力のみが必要です。 このようなデバイスを使用していない場合に一時停止すると、全体的な電力消費量が削減されます。 さらに重要なのは、選択的に中断されていないデバイスでは、USB ホスト コントローラーがシステム メモリに存在する転送スケジュールを無効にできない可能性があります。 ホスト コントローラーからスケジューラへの直接メモリ アクセス (DMA) 転送により、システムのプロセッサが C3 などのより深いスリープ状態に入るのを防ぐことができます。
選択的中断は既定で有効になっています。 マイクロソフトは選択的な中断を無効にしないよう強くお勧めします。
クライアント ドライバーは、アイドル状態の要求を送信する前に選択的な中断が有効になっているかどうかを判断しないでください。 デバイスがアイドル状態のときに、アイドル状態の要求を送信する必要があります。 アイドル要求が失敗した場合、クライアント ドライバーはアイドル タイマーをリセットして再試行する必要があります。
USB デバイスを選択的に中断するには、アイドル要求 IRP (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) と電源 IRP (IRP_MN_SET_POWER) の 2 つの異なるメカニズムが存在します。 使用するメカニズムは、複合デバイスまたは非複合デバイスの種類によって異なります。
選択的中断メカニズムの選択
待機ウェイク アップ IRP (IRP_MN_WAIT_WAKE) を使用してリモート ウェイクアップのインターフェイスを有効にする複合デバイス上のインターフェイスのクライアント ドライバーは、アイドル状態の要求 IRP (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) メカニズムを使用して、デバイスを選択的に中断する必要があります。
リモート ウェイクアップの詳細については、次を参照してください。
このセクションでは、Windows の選択的一時停止メカニズムについて説明します。
USB アイドル要求 IRP の送信
デバイスがアイドル状態になると、クライアント ドライバーはアイドル状態の要求 IRP (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) を送信してバス ドライバーに通知します。 バス ドライバーは、デバイスを低電力状態にしても安全であると判断した後、クライアント デバイス ドライバーがアイドル状態の要求 IRP でスタックを渡したコールバック ルーチンを呼び出します。
コールバック ルーチンでは、クライアント ドライバーは保留中のすべての I/O 操作を取り消し、すべての USB I/O Irp が完了するまで待機する必要があります。 その後、WDM デバイスの電源状態を D2 に変更するIRP_MN_SET_POWER要求を発行できます。 コールバック ルーチンは、 D2 要求が完了するまで待機してから戻る必要があります。 アイドル通知コールバック ルーチンの詳細については、「 USB アイドル要求 IRP コールバック ルーチンの実装」を参照してください。
バス ドライバーは、アイドル通知コールバックルーチンを呼び出した後、アイドル要求IRPを完了しません。 代わりに、バス ドライバーは、次のいずれかの条件が満たされるまでアイドル要求 IRP を保留します。
- IRP_MN_SURPRISE_REMOVALまたはIRP_MN_REMOVE_DEVICE IRP が受信されます。 これらの IRP のいずれかが受信されると、アイドル状態の要求 IRP はSTATUS_CANCELLEDで完了します。
- バス ドライバーは、デバイスを動作中の電源状態 (D0) にする要求を受け取ります。 この要求を受信すると、バス ドライバーは IRP を STATUS_SUCCESS で完了し、保留中のアイドル状態の要求を処理します。
アイドル状態の要求 IRP の使用には、次の制限が適用されます。
- ドライバーは、アイドル状態の要求 IRP を送信するときに、デバイスの電源状態 D0 である必要があります。
- ドライバーは、デバイス スタックごとに 1 つのアイドル状態の要求 IRP のみを送信する必要があります。
次の WDM コード例は、デバイス ドライバーが USB アイドル要求 IRP を送信するために実行する手順を示しています。 次のコード例では、エラー チェックは省略されています。
IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION IRP を割り当てて初期化する
irp = IoAllocateIrp (DeviceContext->TopOfStackDeviceObject->StackSize, FALSE); nextStack = IoGetNextIrpStackLocation (irp); nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION; nextStack->Parameters.DeviceIoControl.InputBufferLength = sizeof(struct _USB_IDLE_CALLBACK_INFO);
アイドル状態の要求情報構造 (USB_IDLE_CALLBACK_INFO) を割り当てて初期化します。
idleCallbackInfo = ExAllocatePool (NonPagedPool, sizeof(struct _USB_IDLE_CALLBACK_INFO)); idleCallbackInfo->IdleCallback = IdleNotificationCallback; // Put a pointer to the device extension in member IdleContext idleCallbackInfo->IdleContext = (PVOID) DeviceExtension; nextStack->Parameters.DeviceIoControl.Type3InputBuffer = idleCallbackInfo;
完了ルーチンを設定します。
クライアント ドライバーは、アイドル状態の要求 IRP に完了ルーチンを関連付ける必要があります。 アイドル状態の通知完了ルーチンとコード例の詳細については、「 USB アイドル要求 IRP 完了ルーチンの実装」を参照してください。
IoSetCompletionRoutine (irp, IdleNotificationRequestComplete, DeviceContext, TRUE, TRUE, TRUE);
アイドル状態の要求をデバイス拡張機能に格納します。
deviceExtension->PendingIdleIrp = irp;
親ドライバーに Idle 要求を送信します。
ntStatus = IoCallDriver (DeviceContext->TopOfStackDeviceObject, irp);
USB アイドル要求の取り消し
特定の状況では、デバイス ドライバーは、バス ドライバーに送信されたアイドル状態の要求 IRP を取り消す必要があります。 この状況は、デバイスが削除された場合、アイドル状態になった後にアクティブ化されてアイドル状態の要求を送信した場合、またはシステム全体が低いシステム電源状態に移行している場合に発生する可能性があります。
クライアント ドライバーは、 IoCancelIrp を呼び出すことによってアイドル状態の IRP を取り消します。 次の表では、アイドル状態の IRP を取り消す 3 つのシナリオについて説明し、ドライバーが実行する必要があるアクションを指定します。
シナリオ | 非稼働状態のリクエストキャンセル機構 |
---|---|
クライアント ドライバーは、アイドル状態の IRP をキャンセルし、USB アイドル通知コールバック ルーチンが呼び出されていません。 | USB ドライバー スタックは、アイドル状態の IRP を完了します。 デバイスが D0 を離れることがないため、ドライバーはデバイスの状態を変更しません。 |
クライアント ドライバーはアイドル状態の IRP を取り消し、USB ドライバー スタックは USB アイドル通知コールバック ルーチンを呼び出し、まだ返されていません。 | クライアント ドライバーが IRP のキャンセルを呼び出した場合でも、USB アイドル通知コールバック ルーチンが呼び出される可能性があります。 この場合でも、クライアント ドライバーのコールバック ルーチンは、デバイスを下位の電源状態に同期的に送信することによって、デバイスの電源を切る必要があります。 デバイスの電源状態が低い場合、クライアント ドライバーは D0 要求を送信できます。 または、ドライバーは、アイドル状態の IRP を完了し、 D0 IRP を送信する USB ドライバー スタックを待機できます。 電源 IRP を割り当てるためにメモリが不足しているために、コールバック ルーチンがデバイスを低電力状態にできない場合は、アイドル状態の IRP を取り消してすぐに終了する必要があります。 アイドル状態のIRPは、コールバックルーチンが戻り値を返すまで完了しません。 そのため、コールバック ルーチンは、キャンセルされたアイドル状態の IRP が完了するまで待機をブロックしないでください。 |
デバイスは既に低電力状態です。 | デバイスが既に低電力状態にある場合、クライアント ドライバーは D0 IRP を送信できます。 USB ドライバー スタックは、STATUS_SUCCESSでアイドル状態の要求 IRP を完了します。 または、ドライバーは、アイドル状態の IRP を取り消し、USB ドライバー スタックがアイドル IRP を完了するまで待ってから、 D0 IRP を送信できます。 |
USB アイドル処理要求 IRP 完了ルーチン
多くの場合、バス ドライバーは、ドライバーのアイドル状態の要求 IRP 完了ルーチンを呼び出す可能性があります。 このような状況が発生した場合、クライアント ドライバーは、バス ドライバーが IRP を完了した理由を検出する必要があります。 返される状態コードは、この情報を提供できます。 状態コードがSTATUS_POWER_STATE_INVALIDされていない場合、デバイスがまだ D0 に存在しない場合、ドライバーはデバイスを D0 に配置する必要があります。 デバイスがまだアイドル状態の場合、ドライバーは別のアイドル状態の要求 IRP を送信できます。
注
アイドル状態の要求 IRP 完了ルーチンは、 D0 電源要求の完了を待機することをブロックしないでください。 完了ルーチンは、ハブ ドライバーによって電源 IRP のコンテキストで呼び出すことができます。また、完了ルーチン内の別の電源 IRP でブロックすると、デッドロックが発生する可能性があります。
次の一覧は、アイドル状態要求の完了ルーチンで一般的な状態コードを解釈する方法を示しています。
状態コード | 説明 |
---|---|
ステータス_成功 | デバイスをこれ以上中断しないようにすることを示します。 ただし、ドライバーはデバイスの電源が入っていることを確認し、 まだ D0 に含まれていない場合は D0 に配置する必要があります。 |
状態_キャンセル済み | バス ドライバーは、次のいずれかの状況でアイドル要求 IRP をSTATUS_CANCELLEDとして終了します。
|
STATUS_POWER_STATE_INVALID (無効な電源状態) | デバイス ドライバーがデバイスの D3 電源状態を要求したことを示します。 この要求が発生すると、バス ドライバーは、保留中のすべてのアイドル状態の IRP をSTATUS_POWER_STATE_INVALIDで完了します。 |
デバイスが使用中です | バス ドライバーがデバイスのために保留中のアイドル状態の要求 IRP を既に保持しているということを示しています。 特定のデバイスに対して一度に保留中にできるアイドル状態の IRP は 1 つだけです。 複数のアイドル状態の要求 IRP を送信すると、電源ポリシー所有者側でエラーが発生します。 ドライバー ライターは、エラーに対処します。 |
次のコード例は、アイドル状態の要求完了ルーチンの実装例を示しています。
/*
Routine Description:
Completion routine for idle notification IRP
Arguments:
DeviceObject - pointer to device object
Irp - I/O request packet
DeviceExtension - pointer to device extension
Return Value:
NT status value
--*/
NTSTATUS
IdleNotificationRequestComplete(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PDEVICE_EXTENSION DeviceExtension
)
{
NTSTATUS ntStatus;
POWER_STATE powerState;
PUSB_IDLE_CALLBACK_INFO idleCallbackInfo;
ntStatus = Irp->IoStatus.Status;
if(!NT_SUCCESS(ntStatus) && ntStatus != STATUS_NOT_SUPPORTED)
{
//Idle IRP completes with error.
switch(ntStatus)
{
case STATUS_INVALID_DEVICE_REQUEST:
//Invalid request.
break;
case STATUS_CANCELLED:
//1. The device driver canceled the IRP.
//2. A system power state change is required.
break;
case STATUS_POWER_STATE_INVALID:
// Device driver requested a D3 power state for its device
// Release the allocated resources.
goto IdleNotificationRequestComplete_Exit;
case STATUS_DEVICE_BUSY:
//The bus driver already holds an idle IRP pending for the device.
break;
default:
break;
}
// If IRP completes with error, issue a SetD0
//Increment the I/O count because
//a new IRP is dispatched for the driver.
//This call is not shown.
powerState.DeviceState = PowerDeviceD0;
// Issue a new IRP
PoRequestPowerIrp (
DeviceExtension->PhysicalDeviceObject,
IRP_MN_SET_POWER,
powerState,
(PREQUEST_POWER_COMPLETE) PoIrpCompletionFunc,
DeviceExtension,
NULL);
}
IdleNotificationRequestComplete_Exit:
idleCallbackInfo = DeviceExtension->IdleCallbackInfo;
DeviceExtension->IdleCallbackInfo = NULL;
DeviceExtension->PendingIdleIrp = NULL;
InterlockedExchange(&DeviceExtension->IdleReqPend, 0);
if(idleCallbackInfo)
{
ExFreePool(idleCallbackInfo);
}
DeviceExtension->IdleState = IdleComplete;
// Because the IRP was created using IoAllocateIrp,
// the IRP needs to be released by calling IoFreeIrp.
// Also return STATUS_MORE_PROCESSING_REQUIRED so that
// the kernel does not reference this.
IoFreeIrp(Irp);
KeSetEvent(&DeviceExtension->IdleIrpCompleteEvent, IO_NO_INCREMENT, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
USB 待機通知コールバックルーチン
バス ドライバー (ハブ ドライバーのインスタンスまたは汎用親ドライバー) は、デバイスの子を中断しても安全かどうかを判断するタイミングを決定します。 その場合は、各子デバイスのクライアント ドライバーによって提供される待機状態通知コールバック ルーチンを呼び出します。
USB_IDLE_CALLBACKの関数プロトタイプは次のとおりです。
typedef VOID (*USB_IDLE_CALLBACK)(__in PVOID Context);
デバイス ドライバーは、アイドル状態の通知コールバック ルーチンで次のアクションを実行する必要があります。
- デバイスをリモート ウェイクアップ用に武装する必要がある場合は、デバイスの IRP_MN_WAIT_WAKE IRP を要求します。
- すべての I/O を取り消し、デバイスの電源が低い状態に戻る準備をします。
- PowerState パラメーターを列挙子値 PowerDeviceD2 (wdm.h; ntddk.h で定義) に設定して PoRequestPowerIrp を呼び出して、デバイスを WDM スリープ状態にします。
ハブ ドライバーと USB 汎用親ドライバー (Usbccgp.sys) の両方が、IRQL = PASSIVE_LEVEL でアイドル状態の通知コールバック ルーチンを呼び出します。 コールバックルーチンは、電源状態変更要求が完了するまでの間、動作を停止することができます。
コールバック ルーチンは、システムが S0 にあり、デバイスが D0 にある間にのみ呼び出されます。
アイドル状態の要求通知コールバック ルーチンには、次の制限が適用されます。
- デバイス ドライバーは、アイドル状態の通知コールバック ルーチンで D0 から D2 へのデバイスの電源状態の遷移を開始できますが、他の電源状態の遷移は許可されません。 特に、ドライバーは、コールバック ルーチンの実行中に、デバイスを D0 に変更しようとしないでください。
- デバイス ドライバーは、アイドル状態の通知コールバック ルーチン内から複数の電源 IRP を要求することはできません。
アイドル状態の通知コールバック ルーチンでのウェイクアップ用のデバイスの準備
アイドル状態の通知コールバック ルーチンは、そのデバイスに保留中の IRP_MN_WAIT_WAKE 要求があるかどうかを判断する必要があります。 IRP_MN_WAIT_WAKEリクエストが保留中でない場合、コールバックルーチンはデバイスを中断する前にIRP_MN_WAIT_WAKEリクエストを提出する必要があります。 待機ウェイク メカニズムの詳細については、「 WakeUp 機能を備えているデバイスのサポート」を参照してください。
USB グローバルサスペンド
USB 2.0 仕様では、フレームの開始パケットを含め、バス上のすべての USB トラフィックを停止することで、USB ホスト コントローラーの背後にあるバス全体の中断としてグローバル サスペンドを定義します。 まだ中断されていないダウンストリーム デバイスは、アップストリーム ポートのアイドル状態を検出し、自身で中断状態に入ります。 この方法では、Windows はグローバルな中断を実装しません。 Windows は、バス上のすべての USB トラフィックを停止する前に、USB ホスト コントローラーの背後にある各 USB デバイスを常に選択的に中断します。
グローバル中断の条件
USB ハブ ドライバーは、接続されているすべてのデバイスが D1、 D2、または D3 デバイスの電源状態にあるハブを選択的に中断します。 すべての USB ハブが選択的に中断されると、バス全体がグローバルに中断されます。 USB ドライバー スタックは、デバイスが D1、 D2、または D3 の WDM デバイスの状態になると、デバイスをアイドル状態として扱います。