Registrieren eines zusammengesetzten Geräts

In diesem Thema wird beschrieben, wie ein Treiber eines USB-Mehrfunktionsgeräts, der als zusammengesetzter Treiber bezeichnet wird, das zusammengesetzte Gerät mit dem zugrunde liegenden USB-Treiberstapel registrieren und aufheben kann. Der von Microsoft bereitgestellte Treiber, Usbccgp.sys, ist der standardmäßige zusammengesetzte Treiber, der von Windows geladen wird. Das Verfahren in diesem Thema gilt für einen benutzerdefinierten WDM-basierten Verbundtreiber (Windows Driver Model), der Usbccgp.sys ersetzt.

Ein USB-Gerät (Universal Serial Bus) kann mehrere Funktionen bereitstellen, die gleichzeitig aktiv sind. Solche Multifunktionsgeräte werden auch als zusammengesetzte Geräte bezeichnet. Beispielsweise kann ein zusammengesetztes Gerät eine Funktion für die Tastaturfunktion und eine andere Funktion für die Maus definieren. Die Funktionen des Geräts werden vom zusammengesetzten Treiber aufgelistet. Der zusammengesetzte Treiber kann diese Funktionen selbst in einem monolithischen Modell verwalten oder physische Geräteobjekte (PDOs) für jede der Funktionen erstellen. Diese einzelnen PDOs werden von ihren jeweiligen USB-Funktionstreibern, dem Tastaturtreiber und dem Maustreiber verwaltet.

Die USB 3.0-Spezifikation definiert die Funktion zum Anhalten und Remote-Reaktivieren der Funktion , die es einzelnen Funktionen ermöglicht, in Zustände mit niedriger Leistung zu gelangen und diese zu beenden, ohne den Leistungszustand anderer Funktionen oder des gesamten Geräts zu beeinträchtigen. Weitere Informationen zum Feature finden Sie unter Implementieren der Funktionssperre in einem zusammengesetzten Treiber.

Um das Feature zu verwenden, muss der zusammengesetzte Treiber das Gerät beim zugrunde liegenden USB-Treiberstapel registrieren. Da das Feature für USB 3.0-Geräte gilt, muss der zusammengesetzte Treiber sicherstellen, dass der zugrunde liegende Stapel version USBD_INTERFACE_VERSION_602 unterstützt. Über die Registrierungsanforderung kann der zusammengesetzte Treiber Folgendes ausführen:

  • Informiert den zugrunde liegenden USB-Treiberstapel, dass der Treiber für das Senden einer Anforderung zum Ausrüsten einer Funktion für das Remotereaktivieren verantwortlich ist. Die Remotereaktivierungsanforderung wird vom USB-Treiberstapel verarbeitet, der die erforderlichen Protokollanforderungen an das Gerät sendet.
  • Ruft eine Liste von Funktionshandles (einen pro Funktion) ab, die vom USB-Treiberstapel zugewiesen werden. Der zusammengesetzte Treiber kann dann ein Funktionshandle in der Anforderung der Remoteaktivierung der Funktion verwenden, die dem Handle zugeordnet ist.

In der Regel sendet ein zusammengesetzter Treiber die Registrierungsanforderung im AddDevice des Treibers oder in der Startgeräteroutine, um IRP_MN_START_DEVICE zu verarbeiten. Folglich gibt der zusammengesetzte Treiber die Ressourcen frei, die für die Registrierung in den Entladeroutinen des Treibers wie Stoppgerät (IRP_MN_STOP_DEVICE) oder Remove-Device-Routine (IRP_MN_REMOVE_DEVICE) zugewiesen werden.

Voraussetzungen

Stellen Sie vor dem Senden der Registrierungsanforderung Folgendes sicher:

  • Sie haben die Anzahl der Funktionen im Gerät. Diese Zahl kann von den Deskriptoren abgeleitet werden, die von der get-configuration-Anforderung abgerufen werden.
  • Sie haben in einem vorherigen Aufruf von USBD_CreateHandle ein USBD-Handle erhalten.
  • Der zugrunde liegende USB-Treiberstapel unterstützt USB 3.0-Geräte. Rufen Sie dazu USBD_IsInterfaceVersionSupported auf, und übergeben Sie USBD_INTERFACE_VERSION_602 als zu überprüfende Version.

Ein Codebeispiel finden Sie unter Implementieren der Funktionssperre in einem zusammengesetzten Treiber.

Anweisungen

Registrieren eines zusammengesetzten Geräts

Im folgenden Verfahren wird beschrieben, wie Sie eine Registrierungsanforderung erstellen und senden sollten, um dem USB-Treiberstapel einen zusammengesetzten Treiber zuzuordnen.

  1. Ordnen Sie eine COMPOSITE_DEVICE_CAPABILITIES-Struktur zu, und initialisieren Sie sie, indem Sie das makro COMPOSITE_DEVICE_CAPABILITIES_INIT aufrufen.

  2. Legen Sie das CapabilityFunctionSuspend-Element von COMPOSITE_DEVICE_CAPABILITIES auf 1 fest.

  3. Ordnen Sie eine REGISTER_COMPOSITE_DEVICE-Struktur zu, und initialisieren Sie die Struktur, indem Sie die USBD_BuildRegisterCompositeDevice-Routine aufrufen. Geben Sie im Aufruf das USBD-Handle, die initialisierte COMPOSITE_DEVICE_CAPABILITIES-Struktur und die Anzahl der Funktionen an.

  4. Ordnen Sie ein E/A-Anforderungspaket (IRP) zu, indem Sie IoAllocateIrp aufrufen und einen Zeiger auf den ersten Stapelspeicherort des IRP (IO_STACK_LOCATION) abrufen, indem Sie IoGetNextIrpStackLocation aufrufen.

  5. Ordnen Sie Arbeitsspeicher für einen Puffer zu, der groß genug ist, um ein Array von Funktionshandles (USBD_FUNCTION_HANDLE) aufzunehmen. Die Anzahl der Elemente im Array muss die Anzahl der PDOs sein.

  6. Erstellen Sie die Anforderung, indem Sie die folgenden Member des IO_STACK_LOCATION festlegen:

    • Geben Sie den Typ der Anforderung an, indem Sie Parameters.DeviceIoControl.IoControlCode auf IOCTL_INTERNAL_USB_REGISTER_COMPOSITE_DEVICE festlegen.
    • Geben Sie den Eingabeparameter an, indem Sie Parameters.Others.Argument1 auf die Adresse der initialisierten REGISTER_COMPOSITE_DEVICE-Struktur festlegen.
    • Geben Sie den Ausgabeparameter an, indem Sie AssociatedIrp.SystemBuffer auf den Puffer festlegen, der in Schritt 5 zugewiesen wurde.
  7. Rufen Sie IoCallDriver auf, um die Anforderung zu senden, indem Sie den IRP an den nächsten Stapelspeicherort übergeben.

Überprüfen Sie nach Abschluss das Array von Funktionshandles, das vom USB-Treiberstapel zurückgegeben wird. Sie können das Array für die zukünftige Verwendung im Gerätekontext des Treibers speichern.

Im folgenden Codebeispiel wird veranschaulicht, wie eine Registrierungsanforderung erstellt und gesendet wird. Im Beispiel wird davon ausgegangen, dass der zusammengesetzte Treiber die zuvor abgerufene Anzahl von Funktionen und das USBD-Handle im Gerätekontext des Treibers speichert.

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;  
}

Aufheben der Registrierung des zusammengesetzten Geräts

  1. Ordnen Sie eine IRP zu, indem Sie IoAllocateIrp aufrufen, und rufen Sie einen Zeiger auf den ersten Stapelspeicherort (IO_STACK_LOCATION) des IRP ab, indem Sie IoGetNextIrpStackLocation aufrufen.
  2. Erstellen Sie die Anforderung, indem Sie den Parameter.DeviceIoControl.IoControlCode-Member von IO_STACK_LOCATION auf IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DEVICE festlegen.
  3. Rufen Sie IoCallDriver auf, um die Anforderung zu senden, indem Sie den IRP an den nächsten Stapelspeicherort übergeben.

Die IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DEVICE Anforderung wird einmal vom zusammengesetzten Treiber im Kontext der Remove-Device-Routine gesendet. Der Zweck der Anforderung besteht darin, die Zuordnung zwischen dem USB-Treiberstapel und dem zusammengesetzten Treiber und seiner aufgezählten Funktion zu entfernen. Die Anforderung bereinigt auch alle Ressourcen, die erstellt wurden, um diese Zuordnung und alle Funktionshandles zu verwalten, die in der vorherigen Registrierungsanforderung zurückgegeben wurden.

Das folgende Codebeispiel zeigt, wie Sie eine Anforderung erstellen und senden, um die Registrierung des zusammengesetzten Geräts aufzuheben. Im Beispiel wird davon ausgegangen, dass der zusammengesetzte Treiber zuvor über eine Registrierungsanforderung registriert wurde, wie weiter oben in diesem Thema beschrieben.

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;  
}

IOCTL_INTERNAL_USB_REGISTER_COMPOSITE_DEVICE
IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DEVICE