Technologieaustausch

Entwicklung von Windows-USB-Gerätetreibern zur Verarbeitung statischer Streams von Stapelübertragungen

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Bei USB 2.0-Geräten und früheren Geräten kann ein Massenendpunkt einen einzelnen Datenstrom über den Endpunkt senden oder empfangen. Bei USB 3.0-Geräten ist der Massenendpunkt in der Lage, mehrere Datenströme über den Endpunkt zu senden und zu empfangen.

Der von Microsoft in Windows bereitgestellte USB-Treiberstapel unterstützt mehrere Streams. Dadurch können Client-Treiber unabhängige E/A-Anfragen an jeden Stream senden, der einem Massenendpunkt in einem USB 3.0-Gerät zugeordnet ist, ohne Anfragen an verschiedene Streams zu serialisieren.

Für einen Client-Treiber stellt ein Stream mehrere logische Endpunkte mit denselben Merkmalen dar. Um eine Anfrage an einen bestimmten Stream zu senden, benötigt der Client-Treiber ein Handle für diesen Stream (ähnlich dem Pipe-Handle eines Endpunkts). Der URB für Streaming-E/A-Anfragen ähnelt dem URB für E/A-Anfragen für Massenendpunkte. Der einzige Unterschied ist der Rohrgriff. Um eine E/A-Anfrage an einen Stream zu senden, gibt der Treiber ein Pipe-Handle im Stream an.

Während der Gerätekonfiguration sendet der Client-Treiber eine ausgewählte Konfigurationsanforderung und optional eine ausgewählte Schnittstellenanforderung. Diese Anforderungen rufen eine Reihe von Pipe-Handles für die Endpunkte ab, die in den Aktivitätseinstellungen der Schnittstelle definiert sind. Für Stream-fähige Endpunkte kann das Endpunkt-Pipe-Handle verwendet werden, um E/A-Anfragen an den Standard-Stream (den ersten Stream) zu senden, bis der Treiber den Stream öffnet.

Wenn ein Clienttreiber Anforderungen an andere Streams als den Standardstream senden möchte, muss der Treiber alle Streams öffnen und Handles für diese abrufen. Dazu sendet der Client-Treiber eine Anfrage zum Öffnen eines Streams, indem er die Anzahl der zu öffnenden Streams angibt. Nachdem ein Client-Treiber die Verwendung von Streams beendet hat, kann der Treiber diese schließen, indem er eine Anfrage zum Schließen des Streams sendet.

Das Kernel Mode Driver Framework (KMDF) unterstützt statische Streams nicht nativ. Clienttreiber müssen das Windows-Treibermodell (WDM) verwenden, um Streams zu öffnen und zu schließen. UMDF-Clienttreiber (User-Mode Driver Framework) können die statische Streaming-Funktion nicht verwenden.

Im Folgenden können einige Kommentare mit der Bezeichnung „WDM-Treiber“ enthalten sein. Diese Anleitung beschreibt Routinen für einen WDM-basierten USB-Client-Treiber, der Streaming-Anfragen senden möchte.

Voraussetzungen

Bevor ein Client-Treiber einen Stream öffnen oder schließen kann, muss der Treiber über Folgendes verfügen:

1. Rufen Sie die WdfUsbTargetDeviceCreateWithParameters-Methode auf. Die Methode erfordert die Client-Protokollversion USBD_CLIENT_CONTRACT_VERSION_602. Durch die Angabe dieser Version muss der Client-Treiber eine Reihe von Regeln einhalten.

Rufen Sie auf, um das WDFUSBDEVICE-Handle des USB-Zielgeräteobjekts des Frameworks abzurufen. Das Handle ist für nachfolgende Aufrufe des offenen Streams erforderlich. Normalerweise registriert sich ein Clienttreiber in der Ereignisrückrufroutine EVT_WDF_DEVICE_PREPARE_HARDWARE des Treibers.

WDM-Treiber: Rufen Sie die Routine USBD_CreateHandle auf und rufen Sie das vom Treiber im USB-Treiberstapel registrierte USBD-Handle ab.

2. Das Gerät konfiguriert und das WDFUSBPIPE-Pipe-Handle für den Massenendpunkt erhalten, der Streaming unterstützt. Um ein Pipe-Handle zu erhalten, rufen Sie die WdfUsbInterfaceGetConfiguredPipe-Methode für die aktuellen alternativen Einstellungen der Schnittstelle in der ausgewählten Konfiguration auf.

WDM-Treiber: Erhält das USBD-Pipe-Handle durch Senden einer Select-Configuration- oder Select-Interface-Anfrage.

So öffnen Sie einen statischen Stream

1. Bestimmen Sie, ob der zugrunde liegende USB-Treiberstapel und der Host-Controller die statische Streaming-Funktion unterstützen, indem Sie die Methode WdfUsbTargetDeviceQueryUsbCapability aufrufen. Normalerweise rufen Clienttreiber Routinen in der Ereignisrückrufroutine EVT_WDF_DEVICE_PREPARE_HARDWARE des Treibers auf.

WDM-Treiber: Ruft die Routine USBD_QueryUsbCapability auf. Normalerweise fragt ein Treiber die Funktion ab, die in der Startgeräteroutine des Treibers verwendet werden soll (IRP_MN_START_DEVICE).

stellen Sie folgende Informationen bereit:

  • Das Handle für das USB-Geräteobjekt, das in einem vorherigen Aufruf von WdfUsbTargetDeviceCreateWithParameters abgerufen wurde und zum Registrieren des Clienttreibers verwendet wird.

WDM-Treiber: Übergeben Sie das im vorherigen Aufruf abgerufene USBD-Handle an USBD_CreateHandle.

Wenn ein Client-Treiber eine bestimmte Funktion nutzen möchte, muss der Treiber zunächst den zugrunde liegenden USB-Treiberstapel abfragen, um festzustellen, ob der Treiberstapel und der Host-Controller die Funktion unterstützen. Wenn die Funktion unterstützt wird, sollte der Fahrer nur dann eine Anfrage zur Nutzung der Funktion senden. Bestimmte Anforderungen erfordern URBs, beispielsweise die in Schritt 5 besprochene Streaming-Funktionalität. Stellen Sie bei diesen Anforderungen sicher, dass Sie dasselbe Handle verwenden, um die Funktion abzufragen und den URB zuzuweisen. Dies liegt daran, dass der Treiberstapel Handles verwendet, um die unterstützten Funktionen zu verfolgen, die der Treiber verwenden kann.

Wenn beispielsweise USBD_HANDLE durch Aufrufen von USBD_CreateHandle abgerufen wurde, wird der Treiberstapel durch Aufrufen von USBD_QueryUsbCapability abgefragt und der URB durch Aufrufen von USBD_UrbAllocate zugewiesen. Übergeben Sie in beiden Aufrufen dasselbe USBD_HANDLE.

Wenn Sie die KMDF-Methoden WdfUsbTargetDeviceQueryUsbCapability und WdfUsbTargetDeviceCreateUrb aufrufen, geben Sie in diesen Methodenaufrufen dasselbe WDFUSBDEVICE-Handle für das Framework-Zielobjekt an.

  • GUID ist GUID_USB_CAPABILITY_STATIC_STREAMS zugewiesen;
  • Zeiger auf den Ausgabepuffer (zeigt auf USHORT). Sobald der Vorgang abgeschlossen ist, wird der Puffer mit (maximaler Anzahl an Streams für) jeden vom Host-Controller unterstützten Endpunkt gefüllt;
  • Die Länge des Ausgabepuffers in Bytes. Für Streams beträgt die Länge sizeof (USHORT);

2. Werten Sie den zurückgegebenen NTSTATUS-Wert aus. Wenn die Routine erfolgreich abgeschlossen wird, wird STATUS_SUCCESS zurückgegeben und die statische Streaming-Funktionalität wird unterstützt. Andernfalls gibt die Methode den entsprechenden Fehlercode zurück.

3. Bestimmen Sie die Anzahl der zu öffnenden Streams. Die maximale Anzahl der Streams, die geöffnet werden können, ist begrenzt durch:

  • Die maximale Anzahl von Streams, die vom Host-Controller unterstützt werden. WdfUsbTargetDeviceQueryUsbCapability (empfängt WdfUsbTargetDeviceQueryUsbCapabilityUSBD_QueryUsbCapability) im vom Aufrufer bereitgestellten Ausgabepuffer. Der von Microsoft bereitgestellte USB-Treiberstapel unterstützt bis zu 255 Streams. WdfUsbTargetDeviceQueryUsbCapability berücksichtigt diesen Grenzwert bei der Berechnung der Anzahl der Streams. Die Methode gibt niemals einen Wert größer als 255 zurück.
  • Die maximale Anzahl von Streams, die vom Endpunkt im Gerät unterstützt werden. Um diese Nummer zu erhalten, überprüfen Sie den Endpunkt-Begleitdeskriptor (in Usbspec.h) für USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR. Um den Endpunkt-Begleitdeskriptor zu erhalten, muss der Konfigurationsdeskriptor analysiert werden. Um den Konfigurationsdeskriptor zu erhalten, muss der Clienttreiber die Methode WdfUsbTargetDeviceRetrieveConfigDescriptor aufrufen. Es müssen die Hilfsroutinen USBD_ParseConfigurationDescriptorEx und USBD_ParseDescriptor verwendet werden.

Um die maximale Anzahl von Flüssen zu bestimmen, wählen Sie den kleineren der beiden vom Host-Controller und Endpunkt unterstützten Werte.

4. Ordnen Sie ein Array von USBD_STREAM_INFORMATION-Strukturen mit n Elementen zu, wobei n die Anzahl der zu öffnenden Streams ist. Der Client-Treiber ist dafür verantwortlich, dieses Array freizugeben, nachdem der Treiber den Stream nicht mehr verwendet.

5. Weisen Sie der Open-Stream-Anfrage einen URB zu, indem Sie die Methode WdfUsbTargetDeviceCreateUrb aufrufen. Wenn der Aufruf erfolgreich abgeschlossen wird, ruft die Methode das WDF-Speicherobjekt und die Adresse der URB-Struktur ab, die vom USB-Treiberstapel zugewiesen wurde.

WDM-Treiber: Ruft die USBD_UrbAllocate-Routine auf.

6. Legen Sie das URB-Format der Open-Stream-Anfrage fest. URB verwendet die Struktur _URB_OPEN_STATIC_STREAMS, um Anforderungen zu definieren. Um einen URB zu formatieren, benötigen Sie:

  • USBD-Pipe-Handle, das auf den Endpunkt zeigt. Wenn ein WDF-Pipe-Objekt vorhanden ist, können Sie das USBD-Pipe-Handle abrufen, indem Sie die Methode WdfUsbTargetPipeWdmGetPipeHandle aufrufen.
  • Erstellt in Schritt 4 (Stream-Array)
  • Zeiger auf die in (Schritt 5) erstellte URB-Struktur.

Um einen URB zu formatieren, rufen Sie UsbBuildOpenStaticStreamsRequest auf und übergeben Sie die erforderlichen Informationen als Parameterwerte. Stellen Sie sicher, dass die für UsbBuildOpenStaticStreamsRequest angegebene Anzahl von Streams die maximal unterstützte Anzahl von Streams nicht überschreitet.

7. Senden Sie den URB als WDF-Anforderungsobjekt, indem Sie die WdfRequestSend-Methode aufrufen. Um die Anforderung synchron zu senden, rufen Sie stattdessen die Methode WdfUsbTargetDeviceSendUrbSynchronously auf.

WDM-Treiber: Ordnet den URB dem IRP zu und übermittelt den IRP an den USB-Treiberstapel.

8. Überprüfen Sie nach Abschluss der Anfrage den Status der Anfrage.Wenn die Anforderung des USB-Treiberstapels fehlschlägt, enthält der URB-Status den entsprechenden Fehlercode.

Wenn der Status der Anfrage (IRP- oder WDF-Anfrageobjekt) USBD_STATUS_SUCCESS anzeigt, wurde die Anfrage erfolgreich abgeschlossen. Array von USBD_STREAM_INFORMATION-Strukturen, die nach Abschluss der Überprüfung empfangen werden. Das Array wird mit Informationen zum angeforderten Stream gefüllt. Der USB-Treiberstapel füllt jede Struktur im Array mit Stream-Informationen, z. B. dem empfangenen USBD_PIPE_HANDLE-Handle, der Stream-ID und der maximalen numerischen Übertragungsgröße. Der Stream kann nun Daten übertragen.

Für Open-Stream-Anfragen müssen URBs und Arrays zugewiesen werden. Nachdem die Anfrage zum Öffnen des Streams abgeschlossen ist, muss der Clienttreiber den URB freigeben, indem er die WdfObjectDelete-Methode für das zugeordnete WDF-Speicherobjekt aufruft. Wenn der Treiber die Anforderung synchron sendet, indem er WdfUsbTargetDeviceSendUrbSynchronously aufruft, muss das WDF-Speicherobjekt nach der Rückkehr der Methode freigegeben werden. Wenn ein Clienttreiber eine Anforderung asynchron sendet, indem er WdfRequestSend aufruft, muss der Treiber das WDF-Speicherobjekt in der vom Treiber implementierten Abschlussroutine freigeben, die der Anforderung zugeordnet ist.

Das Stream-Array kann freigegeben werden, nachdem der Client-Treiber die Verwendung des Streams beendet hat, oder es kann für E/A-Anforderungen gespeichert werden. Im unten aufgeführten Codebeispiel speichert der Treiber ein Stream-Array im Gerätekontext. Der Treiber gibt den Gerätekontext frei, bevor er das Geräteobjekt freigibt.

So übertragen Sie Daten an einen bestimmten Stream
Um eine Datenübertragungsanforderung an einen bestimmten Stream zu senden, ist ein WDF-Anforderungsobjekt erforderlich. Normalerweise müssen Clienttreiber keine WDF-Anforderungsobjekte zuweisen. Wenn der I/O-Manager eine Anfrage von einer Anwendung erhält, erstellt der I/O-Manager ein IRP für die Anfrage. Das IRP wurde vom Framework abgefangen. Das Framework weist dann ein WDF-Anforderungsobjekt zur Darstellung des IRP zu. Anschließend übergibt das Framework das WDF-Anforderungsobjekt an den Client-Treiber. Der Client-Treiber kann dann das Anforderungsobjekt dem Datenübertragungs-URB zuordnen und es an den USB-Treiberstapel senden.

Wenn ein Clienttreiber kein WDF-Anforderungsobjekt vom Framework empfängt und die Anforderung asynchron senden möchte, muss der Treiber ein WDF-Anforderungsobjekt zuweisen, indem er die WdfRequestCreate-Methode aufruft. Formatieren Sie das neue Objekt, indem Sie WdfUsbTargetPipeFormatRequestForUrb aufrufen, und senden Sie die Anforderung, indem Sie WdfRequestSend aufrufen.

Im synchronen Fall ist die Übergabe des WDF-Anfrageobjekts optional.

Um Daten an einen Stream zu übertragen, muss URB verwendet werden. Der URB muss durch Aufruf von WdfUsbTargetPipeFormatRequestForUrb formatiert werden.

Streams unterstützen die folgenden WDF-Methoden nicht:

  • WdfUsbTargetPipeFormatRequestForRead
  • WdfUsbTargetPipeFormatRequestForWrite
  • WdfUsbTargetPipeReadSynchronously
  • WdfUsbTargetPipeSynchronisiertes Schreiben

Bei der folgenden Vorgehensweise wird davon ausgegangen, dass der Client-Treiber das Anforderungsobjekt vom Framework erhält.

  1. Weisen Sie den URB zu, indem Sie WdfUsbTargetDeviceCreateUrb aufrufen. Diese Methode weist ein WDF-Speicherobjekt zu, das den neu zugewiesenen URB enthält. Client-Treiber können wählen, ob sie jeder E/A-Anfrage einen URB zuweisen oder ob sie einen URB zuweisen und ihn für Anfragen desselben Typs verwenden möchten.
  2. Formatieren Sie den URB für die Massenübertragung, indem Sie UsbBuildInterruptOrBulkTransferRequest aufrufen. Geben Sie im Parameter PipeHandle das Handle des Streams an. Das Stream-Handle wird in der vorherigen Anfrage abgerufen, wie im Abschnitt „So öffnen Sie einen statischen Stream“ beschrieben.
  3. Formatieren Sie das WDF-Anforderungsobjekt, indem Sie die Methode WdfUsbTargetPipeFormatRequestForUrb aufrufen. Geben Sie im Aufruf das WDF-Speicherobjekt an, das den Datenübertragungs-URB enthält. Das Speicherobjekt wurde in Schritt 1 zugewiesen.
  4. Senden Sie den URB als WDF-Anfrage, indem Sie WdfRequestSend oder WdfUsbTargetPipeSendUrbSynchronously aufrufen. Wenn Sie WdfRequestSend aufrufen, müssen Sie durch Aufrufen von WdfRequestSetCompletionRoutine eine Abschlussroutine angeben, damit der Clienttreiber benachrichtigt werden kann, wenn der asynchrone Vorgang abgeschlossen ist. Der Datentransfer-URB muss in der Abschlussroutine freigegeben werden.

WDM-Treiber: Weist einen URB zu, indem er USBD_UrbAllocate aufruft, und formatiert ihn für die Massenübertragung (siehe _URB_BULK_OR_INTERRUPT_TRANSFER). Um einen URB zu formatieren, können Sie UsbBuildInterruptOrBulkTransferRequest aufrufen oder die URB-Struktur manuell formatieren. Geben Sie das Handle des Streams im UrbBulkOrInterruptTransfer.PipeHandle-Mitglied des URB an.

So schließen Sie einen statischen Stream

Der Client-Treiber kann den Stream schließen, nachdem der Treiber seine Verwendung beendet hat. Das Schließen der Streaming-Anfrage ist jedoch optional. Der USB-Treiberstapel schließt alle Streams, wenn der mit dem Stream verknüpfte Endpunkt nicht konfiguriert ist. Endpunkte werden dekonfiguriert, wenn eine alternative Konfiguration oder Schnittstelle ausgewählt, ein Gerät gelöscht wird usw. Wenn der Client-Treiber eine andere Anzahl von Streams öffnen möchte, muss er die Streams schließen. Senden Sie eine Anfrage zum Schließen des Streams:

1. Ordnen Sie die URB-Struktur zu, indem Sie WdfUsbTargetDeviceCreateUrb aufrufen.

2. Legen Sie das URB-Format zum Schließen der Stream-Anfrage fest. Das UrbPipeRequest-Mitglied der URB-Struktur ist die _URB_PIPE_REQUEST-Struktur. Füllen Sie die Mitglieder wie folgt aus:

  • Erforderliches Hdr-Mitglied von URB_FUNCTION_CLOSE_STATIC_STREAMS_URB_PIPE_REQUEST
  • Das PipeHandle-Mitglied muss ein Handle sein, das den Endpunkt enthält, der zum Öffnen des Streams verwendet wird.

3. Senden Sie den URB als WDF-Anfrage, indem Sie WdfRequestSend oder WdfUsbTargetDeviceSendUrbSynchronously aufrufen.

Das Close-Handle fordert das Schließen aller Streams an, die zuvor vom Client-Treiber geöffnet wurden. Der Client-Treiber kann keine Anfragen verwenden, um einen bestimmten Stream im Endpunkt zu schließen.

Best Practices zum Senden statischer Streaming-Anfragen

Der USB-Treiberstapel führt eine Überprüfung des empfangenen URB durch. Um Validierungsfehler zu vermeiden, gehen Sie wie folgt vor:

  • Senden Sie keine Open-Stream- oder Close-Stream-Anfragen an Endpunkte, die keine Streams unterstützen. Rufen Sie WdfUsbTargetDeviceQueryUsbCapability (, USBD_QueryUsbCapability) des WDM-Treibers auf, um die statische Streaming-Unterstützung zu ermitteln und Streaming-Anfragen nur zu senden, wenn der Endpunkt dies unterstützt.
  • Fordern Sie nicht mehr Streams (offen) als die maximal unterstützte Anzahl an Streams an und senden Sie keine Anfrage, ohne die Anzahl der Streams anzugeben. Die Anzahl der Streams wird basierend auf der Anzahl der vom USB-Treiberstapel und dem Geräteendpunkt unterstützten Streams bestimmt.
  • Senden Sie keine Anfrage zum Öffnen eines Streams an einen Endpunkt, der bereits über einen offenen Stream verfügt.
  • Senden Sie keine Anfrage zum Schließen eines Streams an einen Endpunkt, der keinen offenen Stream hat.
  • Nachdem Sie einen statischen Stream für einen Endpunkt geöffnet haben, senden Sie keine E/A-Anfragen mit dem Endpunkt-Pipe-Handle, das Sie über eine ausgewählte Konfiguration oder eine ausgewählte Schnittstellenanforderung erhalten haben. Dies gilt auch dann, wenn der statische Stream geschlossen ist.
Pipeline-Vorgänge zurücksetzen und abbrechen

Gelegentlich können Übertragungen zu oder von einem Endpunkt fehlschlagen. Solche Fehler können durch Fehlerbedingungen am Endpunkt oder Host-Controller verursacht werden, beispielsweise durch eine Stopp- oder Stop-Bedingung. Um den Fehlerzustand zu beheben, bricht der Client-Treiber zunächst die ausstehende Übertragung ab und setzt dann die mit dem Endpunkt verknüpfte Pipe zurück. Um eine ausstehende Übertragung abzubrechen, kann der Client-Treiber eine Abbruch-Pipe-Anfrage senden. Um eine Pipe zurückzusetzen, muss der Client-Treiber eine Anforderung zum Zurücksetzen der Pipe senden.

Beim Streaming werden Abort-Pipe- und Reset-Pipe-Anfragen für einzelne Streams, die dem Massenendpunkt zugeordnet sind, nicht unterstützt. Wenn eine Übertragung auf einer bestimmten Stream-Pipe fehlschlägt, stoppt der Host-Controller die Übertragung auf allen anderen Pipes für andere Streams. Um den Fehlerzustand zu beheben, muss der Client-Treiber die Übertragung zu jedem Stream manuell abbrechen Ein Pipe-Handle zum Senden einer Reset-Pipe-Anfrage an den Massenendpunkt. Für diese Anfrage muss der Client-Treiber das Pipe-Handle des Endpunkts in der Struktur _URB_PIPE_REQUEST angeben und die URB-Funktion (Hdr.Function) auf URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL festlegen.

Vollständiges Beispiel

Das folgende Codebeispiel zeigt, wie ein Stream geöffnet wird.

  1. NTSTATUS
  2. OpenStreams (
  3. _In_ WDFDEVICE Device,
  4. _In_ WDFUSBPIPE Pipe)
  5. {
  6. NTSTATUS status;
  7. PDEVICE_CONTEXT deviceContext;
  8. PPIPE_CONTEXT pipeContext;
  9. USHORT cStreams = 0;
  10. USBD_PIPE_HANDLE usbdPipeHandle;
  11. WDFMEMORY urbMemory = NULL;
  12. PURB urb = NULL;
  13. PAGED_CODE();
  14. deviceContext =GetDeviceContext(Device);
  15. pipeContext = GetPipeContext (Pipe);
  16. if (deviceContext->MaxStreamsController == 0)
  17. {
  18. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  19. "%!FUNC! Static streams are not supported.");
  20. status = STATUS_NOT_SUPPORTED;
  21. goto Exit;
  22. }
  23. // If static streams are not supported, number of streams supported is zero.
  24. if (pipeContext->MaxStreamsSupported == 0)
  25. {
  26. status = STATUS_DEVICE_CONFIGURATION_ERROR;
  27. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  28. "%!FUNC! Static streams are not supported by the endpoint.");
  29. goto Exit;
  30. }
  31. // Determine the number of streams to open.
  32. // Compare the number of streams supported by the endpoint with the
  33. // number of streams supported by the host controller, and choose the
  34. // lesser of the two values. The deviceContext->MaxStreams value was
  35. // obtained in a previous call to WdfUsbTargetDeviceQueryUsbCapability
  36. // that determined whether or not static streams is supported and
  37. // retrieved the maximum number of streams supported by the
  38. // host controller. The device context stores the values for IN and OUT
  39. // endpoints.
  40. // Allocate an array of USBD_STREAM_INFORMATION structures to store handles to streams.
  41. // The number of elements in the array is the number of streams to open.
  42. // The code snippet stores the array in its device context.
  43. cStreams = min(deviceContext->MaxStreamsController, pipeContext->MaxStreamsSupported);
  44. // Allocate an array of streams associated with the IN bulk endpoint
  45. // This array is released in CloseStreams.
  46. pipeContext->StreamInfo = (PUSBD_STREAM_INFORMATION) ExAllocatePoolWithTag (
  47. NonPagedPool,
  48. sizeof (USBD_STREAM_INFORMATION) * cStreams,
  49. USBCLIENT_TAG);
  50. if (pipeContext->StreamInfo == NULL)
  51. {
  52. status = STATUS_INSUFFICIENT_RESOURCES;
  53. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  54. "%!FUNC! Could not allocate stream information array.");
  55. goto Exit;
  56. }
  57. RtlZeroMemory (pipeContext->StreamInfo,
  58. sizeof (USBD_STREAM_INFORMATION) * cStreams);
  59. // Get USBD pipe handle from the WDF target pipe object. The client driver received the
  60. // endpoint pipe handles during device configuration.
  61. usbdPipeHandle = WdfUsbTargetPipeWdmGetPipeHandle (Pipe);
  62. // Allocate an URB for the open streams request.
  63. // WdfUsbTargetDeviceCreateUrb returns the address of the
  64. // newly allocated URB and the WDFMemory object that
  65. // contains the URB.
  66. status = WdfUsbTargetDeviceCreateUrb (
  67. deviceContext->UsbDevice,
  68. NULL,
  69. &urbMemory,
  70. &urb);
  71. if (status != STATUS_SUCCESS)
  72. {
  73. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  74. "%!FUNC! Could not allocate URB for an open-streams request.");
  75. goto Exit;
  76. }
  77. // Format the URB for the open-streams request.
  78. // The UsbBuildOpenStaticStreamsRequest inline function formats the URB by specifying the
  79. // pipe handle to the entire bulk endpoint, number of streams to open, and the array of stream structures.
  80. UsbBuildOpenStaticStreamsRequest (
  81. urb,
  82. usbdPipeHandle,
  83. (USHORT)cStreams,
  84. pipeContext->StreamInfo);
  85. // Send the request synchronously.
  86. // Upon completion, the USB driver stack populates the array of with handles to streams.
  87. status = WdfUsbTargetPipeSendUrbSynchronously (
  88. Pipe,
  89. NULL,
  90. NULL,
  91. urb);
  92. if (status != STATUS_SUCCESS)
  93. {
  94. goto Exit;
  95. }
  96. Exit:
  97. if (urbMemory)
  98. {
  99. WdfObjectDelete (urbMemory);
  100. }
  101. return status;
  102. }