次の方法で共有


複合デバイスの登録方法

この記事では、USB 多機能デバイスのドライバー (複合ドライバー) が基盤となる USB ドライバー スタックに複合デバイスを登録および登録解除する方法について説明しています。 Windows では、Microsoft 提供のドライバーである Usbccgp.sys を既定の複合ドライバーとして読み込みます。 この記事の手順は、Usbccgp.sys を置き換えるカスタムの Windows Driver Model (WDM) ベースの複合ドライバーに適用されます。

ユニバーサル シリアル バス (USB) デバイスは、同時にアクティブな複数の機能を提供できます。 このような多機能デバイスは、複合デバイスとも呼ばれます。 たとえば、複合デバイスでは、キーボードの機能とマウスの別の関数を定義できます。 複合ドライバーは、デバイスの機能を列挙します。 複合ドライバーは、モノリシック モデルでこれらの機能自体を管理したり、各機能の物理デバイス オブジェクト (PDO) を作成したりできます。 USB 機能ドライバー (キーボード ドライバーやマウス ドライバーなど) は、それぞれ個別の PDO を管理します。

USB 3.0 仕様では、機能の一時停止およびリモート ウェイクアップ機能が定義されており、これにより、個々の機能が、他の機能またはデバイス全体の電源状態に影響を与えずに低電力状態に入ったり終了したりできるようになります。 この機能について詳しくは、「複合ドライバーで機能の一時停止を実装する方法」をご覧ください。

この機能を使用するには、複合ドライバーは、基になる USB ドライバー スタックにデバイスを登録する必要があります。 この機能は USB 3.0 デバイスに適用されるため、複合ドライバーは、基になるスタックがバージョン USBD_INTERFACE_VERSION_602 をサポートしていることを確認する必要があります。 登録要求を通じて、複合ドライバーは以下の手順を実行します。

  • リモート ウェイクアップ機能の装備要求の送信をドライバーが担当していることを、基になる USB ドライバー スタックに通知します。 USB ドライバー スタックは、リモート ウェイクアップ要求を処理します。この要求は、デバイスに必要なプロトコル要求を送信します。
  • USB ドライバー スタックによって割り当てられた機能ハンドル (機能ごとに 1 つ) のリストを取得します。 複合ドライバーは、ドライバーのハンドルに関連付けられている機能のリモート ウェイク アップ要求で機能ハンドルを使用できます。

通常、複合ドライバーは、ドライバーの AddDevice またはデバイス起動ルーチンで登録要求を送信して、IRP_MN_START_DEVICE を処理します。 その結果、複合ドライバーは、デバイス停止ルーチン (IRP_MN_STOP_DEVICE) やデバイス削除ルーチン (IRP_MN_REMOVE_DEVICE) など、ドライバーのアンロード ルーチンの登録に割り当てられているリソースを解放します。

前提条件

登録要求を送信する前に、以下の点を確認してください。

  • デバイス内の機能の数があります。 この数は、構成取得要求によって取得された記述子から派生することができます。
  • USBD_CreateHandle の前の呼び出しで USBD ハンドルを取得しました。
  • 基になる USB ドライバー スタックは、USB 3.0 デバイスをサポートしています。 これを行うには、USBD_IsInterfaceVersionSupported を呼び出し、バージョンとして USBD_INTERFACE_VERSION_602 を渡してチェックします。

コード例については、「複合ドライバーに機能サスペンドを実装する方法」を参照してください。

複合デバイスの登録

次の手順では、登録要求を構築して送信し、複合ドライバーを USB ドライバー スタックに関連付ける方法について説明します。

  1. COMPOSITE_DEVICE_CAPABILITIES 構造を割り当て、COMPOSITE_DEVICE_CAPABILITIES_INIT マクロを呼び出して初期化します。

  2. COMPOSITE_DEVICE_CAPABILITIESCapabilityFunctionSuspend メンバーを 1 に設定します。

  3. REGISTER_COMPOSITE_DEVICE 構造を割り当て、USBD_BuildRegisterCompositeDevice ルーチンを呼び出して構造を初期化します。 呼び出しで、USBD ハンドル、初期化された COMPOSITE_DEVICE_CAPABILITIES 構造、および機能の数を指定します。

  4. IoAllocateIrp を呼び出して I/O 要求パケット (IRP) を割り当て、IoGetNextIrpStackLocation を呼び出して IRP の最初のスタック位置 (IO_STACK_LOCATION) へのポインターを取得します。

  5. 機能ハンドルの配列 (USBD_FUNCTION_HANDLE) を保持するのに十分な大きさのバッファーにメモリを割り当てます。 配列内の要素の数は、PDO の数でなければなりません。

  6. IO_STACK_LOCATION の次のメンバーを設定して、要求を構築します。

    • Parameters.DeviceIoControl.IoControlCodeIOCTL_INTERNAL_USB_REGISTER_COMPOSITE_DEVICE に設定して、要求の種類を指定します。
    • Parameters.Others.Argument1 を初期化された REGISTER_COMPOSITE_DEVICE 構造のアドレスに設定して、入力パラメーターを指定します。
    • AssociatedIrp.SystemBuffer を、手順 5 で割り当てられたバッファーに設定して、出力パラメーターを指定します。
  7. 次のスタックの場所に IRP を渡すことにより、IoCallDriver を呼び出して要求を送信します。

完了したら、USB ドライバー スタックによって返された機能ハンドルの配列を調べます。 後で使用できるよう、ドライバーのデバイス コンテキストに配列を格納できます。

次のコード例は、登録要求を構築して送信する方法を示しています。 この例では、複合ドライバーが以前に取得した機能の数と USBD ハンドルをドライバーのデバイス コンテキストに格納することを前提としています。

VOID  RegisterCompositeDriver(PPARENT_FDO_EXT parentFdoExt)
{
    PIRP                            irp;
    REGISTER_COMPOSITE_DRIVER       registerInfo;
    COMPOSITE_DRIVER_CAPABILITIES   capabilities;
    NTSTATUS                        status;
    PVOID                           buffer;
    ULONG                           bufSize;
    PIO_STACK_LOCATION              nextSp;

    buffer = NULL;

    COMPOSITE_DRIVER_CAPABILITIES_INIT(&capabilities);
    capabilities.CapabilityFunctionSuspend = 1;

    USBD_BuildRegisterCompositeDriver(parentFdoExt->usbdHandle,
        capabilities,
        parentFdoExt->numFunctions,
        &registerInfo);

    irp = IoAllocateIrp(parentFdoExt->topDevObj->StackSize, FALSE);

    if (irp == NULL)
    {
        //IoAllocateIrp failed.
        status = STATUS_INSUFFICIENT_RESOURCES;
        goto ExitRegisterCompositeDriver;
    }

    nextSp = IoGetNextIrpStackLocation(irp);

    bufSize = parentFdoExt->numFunctions * sizeof(USBD_FUNCTION_HANDLE);

    buffer = ExAllocatePoolWithTag (NonPagedPool, bufSize, POOL_TAG);

    if (buffer == NULL)
    {
        // Memory alloc for function-handles failed.
        status = STATUS_INSUFFICIENT_RESOURCES;
        goto ExitRegisterCompositeDriver;
    }

    nextSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
    nextSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_REGISTER_COMPOSITE_DRIVER;

    //Set the input buffer in Argument1
    nextSp->Parameters.Others.Argument1 = &registerInfo;

    //Set the output buffer in SystemBuffer field for USBD_FUNCTION_HANDLE.
    irp->AssociatedIrp.SystemBuffer = buffer;

    // Pass the IRP down to the next device object in the stack. Not shown.
    status = CallNextDriverSync(parentFdoExt, irp, FALSE);

    if (!NT_SUCCESS(status))
    {
        //Failed to register the composite driver.
        goto ExitRegisterCompositeDriver;
    }

    parentFdoExt->compositeDriverRegistered = TRUE;

    parentFdoExt->functionHandleArray = (PUSBD_FUNCTION_HANDLE) buffer;

End:
    if (!NT_SUCCESS(status))
    {
        if (buffer != NULL)
        {
            ExFreePoolWithTag (buffer, POOL_TAG);
            buffer = NULL;
        }
    }

    if (irp != NULL)
    {
        IoFreeIrp(irp);
        irp = NULL;
    }

    return;
}

複合デバイスの登録解除

  1. IoAllocateIrp を呼び出して IRP を割り当て、IoGetNextIrpStackLocation を呼び出して IRP の最初のスタック位置 (IO_STACK_LOCATION) へのポインターを取得します。
  2. IO_STACK_LOCATIONParameters.DeviceIoControl.IoControlCode メンバーを IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DEVICE に設定して、要求を構築します。
  3. 次のスタックの場所に IRP を渡すことにより、IoCallDriver を呼び出して要求を送信します。

IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DEVICE 要求は、デバイス削除ルーチンのコンテキストで複合ドライバーによって 1 回送信されます。 要求の目的は、USB ドライバー スタックと複合ドライバーとその列挙された機能の間の関連付けを削除することです。 要求は、その関連付けと、前の登録要求で返されたすべての機能ハンドルを維持するために作成されたすべてのリソースをクリーンアップします。

次のコード例は、複合デバイスの登録を解除する要求を構築して送信する方法を示しています。 この例では、この記事で前述したとおり、複合ドライバーが登録要求を通じて以前に登録されていることを前提としています。

VOID  UnregisterCompositeDriver(
    PPARENT_FDO_EXT parentFdoExt )
{
    PIRP                irp;
    PIO_STACK_LOCATION  nextSp;
    NTSTATUS            status;

    PAGED_CODE();

    irp = IoAllocateIrp(parentFdoExt->topDevObj->StackSize, FALSE);

    if (irp == NULL)
    {
        //IoAllocateIrp failed.
        status = STATUS_INSUFFICIENT_RESOURCES;
        return;
    }

    nextSp = IoGetNextIrpStackLocation(irp);

    nextSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
    nextSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DRIVER;

    // Pass the IRP down to the next device object in the stack. Not shown.
    status = CallNextDriverSync(parentFdoExt, irp, FALSE);

    if (NT_SUCCESS(status))
    {
        parentFdoExt->compositeDriverRegistered = FALSE;
    }

    IoFreeIrp(irp);

    return;
}