So registrieren Sie ein Verbundgerät
In diesem Artikel wird beschrieben, wie ein Treiber eines USB-Multifunktionsgeräts (ein sogenannter Verbundtreiber) das Verbundgerät beim zugrunde liegenden USB-Treiberstapel registrieren und die Registrierung aufheben kann. Windows lädt den von Microsoft bereitgestellten Treiber, Usbccgp.sys als Standard-Verbundtreiber. Das Verfahren in diesem Artikel bezieht sich auf einen benutzerdefinierten Verbundtreiber mit Windows-Treibermodell (WDM), der Usbccgp.sys ersetzt.
Ein USB-Gerät (Universal Serial Bus) kann mehrere Funktionen bereitstellen, die gleichzeitig aktiv sind. Solche multifunktionsbasierten Geräte werden auch als Verbundgeräte bezeichnet. Beispielsweise kann ein Verbundgerät eine Funktion für die Tastaturfunktion und eine andere Funktion für die Maus definieren. Der Verbundtreiber listet die Funktionen des Geräts auf. Der Verbundtreiber kann diese Funktionen selbst in einem monolithischen Modell verwalten oder physische Geräteobjekte (PDOs) für jede der Funktionen erstellen. USB-Funktionstreiber, z. B. der Tastaturtreiber und der Maustreiber, verwalten ihre jeweiligen einzelnen PDOs.
Die USB 3.0-Spezifikation definiert die Funktion zum Anhalten und Remote-Aktivieren von Funktionen, wodurch einzelne Funktionen in den Energiesparzustand versetzt und wieder verlassen werden können, ohne den Energiezustand anderer Funktionen oder des gesamten Geräts zu beeinträchtigen. Weitere Informationen zum Feature finden Sie unter Implementieren der Funktionssperre in einem Verbundtreiber.
Um das Feature zu verwenden, muss der Verbundtreiber das Gerät mit dem zugrunde liegenden USB-Treiberstapel registrieren. Da die Funktion für USB 3.0-Geräte gilt, muss der Verbundtreiber sicherstellen, dass der zugrunde liegende Stapel die Version USBD_INTERFACE_VERSION_602 unterstützt. Durch die Registrierungsanforderung kann der Verbundtreiber Folgendes ausführen:
- Informiert den zugrunde liegenden USB-Treiberstapel darüber, dass der Treiber für das Senden einer Anforderung zum Aktivieren einer Funktion für die Remote-Aktivierung verantwortlich ist. Der USB-Treiberstapel verarbeitet die Remote-Wiederaufnahmefunktion, die die erforderlichen Protokollanforderungen an das Gerät sendet.
- Ruft eine Liste der vom USB-Treiberstapel zugewiesenen Funktionshandles (eins pro Funktion) ab. Der Verbundtreiber kann dann einen Funktions-Handle in der Anforderung des Treibers zum Remote-Aktivieren der mit dem Handle verknüpften Funktion verwenden.
In der Regel sendet ein Verbundtreiber die Registrierungsanforderung im AddDevice des Treibers oder der Startgeräteroutine, um IRP_MN_START_DEVICE zu verarbeiten. Der Verbundtreiber gibt also die Ressourcen frei, die für die Registrierung in den Entladeroutinen des Treibers zugewiesen sind, z. B. stop-device (IRP_MN_STOP_DEVICE) oder remove-device routine (IRP_MN_REMOVE_DEVICE).
Voraussetzungen
Stellen Sie vor dem Senden der Registrierungsanforderung Folgendes sicher:
- Sie verfügen über die Anzahl der Funktionen auf dem Gerät. Diese Nummer kann aus den Deskriptoren abgeleitet werden, die durch die Anforderung „Get-Configuration“ abgerufen wurden.
- Sie haben ein USBD-Handle in einem vorherigen Aufruf von USBD_CreateHandle 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 So implementieren Sie eine Funktionssperre in einem Verbundtreiber.
Registrieren eines Verbundgeräts
Das folgende Verfahren beschreibt, wie Sie eine Registrierungsanforderung erstellen und senden, um einen Verbundtreiber mit dem USB-Treiberstapel zu verknüpfen.
Weise Sie eine COMPOSITE_DEVICE_CAPABILITIES-Struktur auf und initialisieren Sie diese, indem Sie das Makro COMPOSITE_DEVICE_CAPABILITIES_INIT aufrufen.
Legen Sie das CapabilityFunctionSuspend-Mitglied von COMPOSITE_DEVICE_CAPABILITIES auf 1 fest.
Weisen Sie eine REGISTER_COMPOSITE_DEVICE-Struktur zu und initialisieren Sie die Struktur, indem Sie die USBD_BuildRegisterCompositeDevice-Routine aufrufen. Geben Sie im Aufruf den USBD-Handle, die initialisierte COMPOSITE_DEVICE_CAPABILITIES-Struktur und die Anzahl der Funktionen an.
Weisen Sie ein E/A-Anforderungspaket (IRP) zu, indem Sie IoAllocateIrp aufrufen, und erhalten Sie einen Zeiger auf den ersten Stapelstandort von IRP (IO_STACK_LOCATION), indem Sie IoGetNextIrpStackLocation aufrufen.
Weisen Sie Speicher für einen Puffer zu, der groß genug ist, um ein Array von Funktionshandles (USBD_FUNCTION_HANDLE) zu speichern. Die Anzahl der Elemente im Array muss der Anzahl der PDOs entsprechen.
Erstellen Sie die Anforderung, indem Sie die folgenden Mitglieder von IO_STACK_LOCATION festlegen:
- Geben Sie den Anforderungstyp 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.
Rufen Sie IoCallDriver auf, um die Anforderung zu senden, indem Sie das IRP an den nächsten Stapelspeicherort übergeben.
Überprüfen Sie nach Abschluss das Array der vom USB-Treiberstapel zurückgegebenen Funktionshandles. Sie können das Array im Gerätekontext des Treibers für die zukünftige Verwendung speichern.
Das folgende Codebeispiel zeigt, wie eine Registrierungsanforderung erstellt und gesendet wird. Im Beispiel wird davon ausgegangen, dass der Verbundtreiber 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,
®isterInfo);
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 = ®isterInfo;
//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 eines Verbundgeräts
- Weisen Sie ein IRP zu, indem Sie IoAllocateIrp aufrufen, und erhalten Sie einen Zeiger auf den ersten Stapelstandort von IRP (IO_STACK_LOCATION), indem Sie IoGetNextIrpStackLocation aufrufen.
- Erstellen Sie die Anforderung, indem Sie das Parameters.DeviceIoControl.IoControlCode-Mitglied von IO_STACK_LOCATION auf IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DEVICE festlegen.
- Rufen Sie IoCallDriver auf, um die Anforderung zu senden, indem Sie das IRP an den nächsten Stapelspeicherort übergeben.
Die IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DEVICE-Anforderung wird einmal vom Verbundtreiber im Kontext der Geräteentfernungsroutine gesendet. Der Zweck der Anforderung besteht darin, die Zuordnung zwischen dem USB-Treiberstapel und dem Verbundtreiber und der enumerierten Funktion zu entfernen. Die Anforderung bereinigt auch alle Ressourcen, die erstellt wurden, um diese Zuordnung und alle Funktionshandles beizubehalten, die in der vorherigen Registrierungsanforderung zurückgegeben wurden.
Das folgende Codebeispiel zeigt, wie Sie eine Anforderung erstellen und senden, um die Registrierung des Verbundgeräts aufzuheben. Im Beispiel wird davon ausgegangen, dass der Verbundtreiber zuvor über eine Registrierungsanforderung registriert wurde, wie weiter oben in diesem Artikel 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;
}