Delen via


Statische streams openen en sluiten in een USB-bulkeindpunt

In dit artikel wordt de mogelijkheid voor statische streams besproken en wordt uitgelegd hoe een USB-clientstuurprogramma streams kan openen en sluiten in een bulkeindpunt van een USB 3.0-apparaat.

Op USB 2.0- en eerdere apparaten kan een bulkeindpunt één gegevensstroom verzenden of ontvangen via het eindpunt. Op USB 3.0-apparaten hebben bulkeindpunten de mogelijkheid om meerdere gegevensstromen via het eindpunt te verzenden en te ontvangen.

De door Microsoft geleverde USB-stuurprogrammastack in Windows ondersteunt meerdere streams. Hierdoor kan een clientstuurprogramma onafhankelijke I/O-aanvragen verzenden naar elke stream die is gekoppeld aan een bulkeindpunt in een USB 3.0-apparaat. De aanvragen voor verschillende streams worden niet geserialiseerd.

Voor een clientstuurprogramma vertegenwoordigen streams meerdere logische eindpunten met dezelfde set kenmerken. Als u een aanvraag naar een bepaalde stream wilt verzenden, heeft het clientstuurprogramma een ingang naar die stroom nodig (vergelijkbaar met een pijpgreep voor een eindpunt). De URB voor een I/O-aanvraag naar een stream is vergelijkbaar met een URB voor een I/O-aanvraag naar een bulkeindpunt. Het enige verschil is de pijpgreep. Om een I/O-aanvraag naar een gegevensstroom te verzenden, specificeert het stuurprogramma de pijpgreep naar de stream.

Tijdens de apparaatconfiguratie verzendt het clientstuurprogramma een select-configuratieaanvraag en eventueel een select-interface-aanvraag. Deze aanvragen halen een set pijpgrepen op voor de eindpunten die zijn gedefinieerd in de actieve instelling van een interface. Voor een eindpunt dat streams ondersteunt, kan de greep van de eindpuntpijp worden gebruikt om I/O-aanvragen naar de standaardstroom (de eerste stream) te verzenden totdat het stuurprogramma streams heeft geopend (hierna besproken).

Als het clientstuurprogramma aanvragen wil verzenden naar andere streams dan de standaardstream, moet het stuurprogramma ingangen voor alle streams openen en verkrijgen. Hiervoor verzendt het clientstuurprogramma een aanvraag voor open streams door het aantal streams op te geven dat moet worden geopend. Nadat het clientstuurprogramma klaar is met het gebruik van streams, kan het stuurprogramma deze desgewenst sluiten door een aanvraag voor close-streams te verzenden.

KMDF (Kernel Mode Driver Framework) biedt geen ondersteuning voor statische streams die intrinsiek zijn. Het clientstuurprogramma moet WDM-stijl-URB's (Windows Driver Model) verzenden die gegevensstromen openen en sluiten. In dit artikel wordt beschreven hoe u deze URB's opmaakt en verzendt. Een gebruikersmodus-stuurprogramma (UMDF)-clientstuurprogramma kan de functie voor statische streams niet gebruiken.

Het artikel bevat enkele notities die zijn gelabeld als WDM-stuurprogramma's. Deze notities beschrijven routines voor een USB-clientstuurprogramma op basis van WDM dat streamaanvragen wil verzenden.

Vereiste voorwaarden

Voordat een clientstuurprogramma streams kan openen of sluiten, moet het stuurprogramma het volgende hebben:

  • De methode WdfUsbTargetDeviceCreateWithParameters aangeroepen.

    Voor de methode moet de clientcontractversie USBD_CLIENT_CONTRACT_VERSION_602 zijn. Door op te geven welke versie het clientstuurprogramma moet voldoen aan een set regels. Zie Beste praktijken: URB's gebruiken voor meer informatie.

    De aanroep haalt een WDFUSBDEVICE-handle op naar het USB-doelapparaat-object van het framework. Deze ingang is vereist om volgende aanroepen te doen om streams te openen. Normaal gesproken registreert het clientstuurprogramma zich in de EVT_WDF_DEVICE_PREPARE_HARDWARE-routine voor het terugbellen van gebeurtenissen.

    WDM-stuurprogramma's: Roep de USBD_CreateHandle routine aan en haal een USBD-ingang op voor de registratie van het stuurprogramma met de USB-stuurprogrammastack.

  • Het apparaat geconfigureerd en een pijpgreep van het type WDFUSBPIPE verkregen voor het bulkeindpunt dat streams ondersteunt. Als u de pijpgreep wilt verkrijgen, roept u de WdfUsbInterfaceGetConfiguredPipe-methode aan op de huidige alternatieve instelling van een interface in de geselecteerde configuratie.

    WDM-stuurprogramma's: Haal een USBD-pijpgreep op door een select-configuratie of select-interface-aanvraag te verzenden. Zie Een configuratie voor een USB-apparaat selecteren voor meer informatie.

Hoe statische streams openen

  1. Bepaal of de onderliggende USB-stuurprogrammastack en de hostcontroller de mogelijkheid voor statische streams ondersteunt door de methode WdfUsbTargetDeviceQueryUsbCapability aan te roepen. Normaal gesproken roept het clientstuurprogramma de routine aan in de EVT_WDF_DEVICE_PREPARE_HARDWARE gebeurtenisterugaanroep routine van het stuurprogramma.

    WDM-stuurprogramma's: Roep de USBD_QueryUsbCapability routine aan. Normaal gesproken voert het stuurprogramma query's uit voor de mogelijkheden die het wil gebruiken in de routine van het startapparaat van het stuurprogramma (IRP_MN_START_DEVICE). Zie USBD_QueryUsbCapability voor codevoorbeelden.

    Geef de volgende informatie op:

    • Een ingang naar het USB-apparaatobject dat is opgehaald, in een vorige aanroep naar WdfUsbTargetDeviceCreateWithParameters, voor registratie van clientstuurprogramma's.

      WDM-stuurprogramma's: Geef de USBD-handle door die in de vorige aanroep naar USBD_CreateHandle is opgehaald.

      Als het clientstuurprogramma een bepaalde mogelijkheid wil gebruiken, moet het stuurprogramma eerst een query uitvoeren op de onderliggende USB-stuurprogrammastack om te bepalen of de stuurprogrammastack en de hostcontroller de mogelijkheid ondersteunen. Als de mogelijkheid wordt ondersteund, moet het stuurprogramma pas dan een aanvraag verzenden om de mogelijkheid te gebruiken. Voor sommige aanvragen zijn URL's vereist, zoals de mogelijkheid voor streams (besproken in stap 5). Zorg ervoor dat u voor deze verzoeken dezelfde handle gebruikt om mogelijkheden op te vragen en URB's toe te wijzen. Dat komt doordat de stuurprogrammastack handles gebruikt om de ondersteunde mogelijkheden bij te houden die een stuurprogramma kan gebruiken.

      Als u bijvoorbeeld een USBD_HANDLE hebt verkregen (door USBD_CreateHandle aan te roepen), voert u een query uit op de stuurprogrammastack door USBD_QueryUsbCapability aan te roepen en de URB toe te wijzen door USBD_UrbAllocate aan te roepen. Geef dezelfde USBD_HANDLE door in beide aanroepen.

      Als u KMDF-methoden aanroept, zoals WdfUsbTargetDeviceQueryUsbCapability en WdfUsbTargetDeviceCreateUrb, geeft u dezelfde WDFUSBDEVICE-handle op aan het framework-doelobject in die methodeaanroepen.

    • De GUID die is toegewezen aan GUID_USB_CAPABILITY_STATIC_STREAMS.

    • Een uitvoerbuffer (pointer naar USHORT). Na voltooiing wordt de buffer gevuld met het maximum aantal streams (per eindpunt) dat wordt ondersteund door de hostcontroller.

    • De lengte, in bytes, van de uitvoerbuffer. Voor streams is de lengte sizeof (USHORT).

  2. Evalueer de geretourneerde NTSTATUS-waarde. Als de routine succesvol is voltooid, wordt STATUS_SUCCESS geretourneerd en wordt de mogelijkheid voor statische streams ondersteund. Anders retourneert de methode een geschikte foutcode.

  3. Bepaal het aantal streams dat moet worden geopend. Het maximum aantal streams dat kan worden geopend, wordt beperkt door:

    • Het maximum aantal streams dat wordt ondersteund door de hostcontroller. Dit nummer wordt ontvangen door WdfUsbTargetDeviceQueryUsbCapability (voor WDM-stuurprogramma's, USBD_QueryUsbCapability), in de door de aanroeper geleverde uitvoerbuffer. De door Microsoft geleverde USB-stuurprogrammastack ondersteunt maximaal 255 streams. WdfUsbTargetDeviceQueryUsbCapability houdt rekening met deze beperking bij het berekenen van het aantal streams. De methode retourneert nooit een waarde die groter is dan 255.
    • Het maximum aantal streams dat door het eindpunt in het apparaat wordt ondersteund. Als u dat getal wilt ophalen, inspecteert u de eindpuntassistentdescriptor (zie USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR in Usbspec.h). Als u de eindpuntassistentdescriptor wilt verkrijgen, moet u de configuratiedescriptor parseren. Om de configuratiedescriptor te verkrijgen, moet het clientstuurprogramma de methode WdfUsbTargetDeviceRetrieveConfigDescriptor aanroepen. U moet de helperroutines, USBD_ParseConfigurationDescriptorEx en USBD_ParseDescriptor gebruiken. Zie voor codevoorbeeld de voorbeeldfunctie RetrieveStreamInfoFromEndpointDesc in USB-pijpen opsommen.

    Als u het maximum aantal streams wilt bepalen, kiest u de mindere van twee waarden die worden ondersteund door de hostcontroller en het eindpunt.

  4. Wijs een matrix van USBD_STREAM_INFORMATION structuren toe met n elementen, waarbij n het aantal stromen is dat moet worden geopend. Het clientstuurprogramma is verantwoordelijk voor het vrijgeven van deze matrix nadat het stuurprogramma klaar is met het gebruik van streams.

  5. Wijs een URB toe voor de open-streams-aanvraag door de methode WdfUsbTargetDeviceCreateUrb aan te roepen. Als de aanroep is voltooid, haalt de methode een WDF-geheugenobject op en het adres van de URB-structuur die is toegewezen door de USB-stuurprogrammastack.

    WDM-stuurprogramma's: Roep de USBD_UrbAllocate routine aan.

  6. Maak de URB op voor de open-stream-aanvraag. De URB gebruikt de _URB_OPEN_STATIC_STREAMS structuur om de aanvraag te definiëren. Als u de URB wilt opmaken, hebt u het volgende nodig:

    Als u de URB wilt opmaken, roept u UsbBuildOpenStaticStreamsRequest aan en geeft u de vereiste informatie door als parameterwaarden. Zorg ervoor dat het aantal streams dat is opgegeven voor UsbBuildOpenStaticStreamsRequest niet groter is dan het maximum aantal ondersteunde streams.

  7. Verzend de URB als een WDF-aanvraagobject door de methode WdfRequestSend aan te roepen. Als u de aanvraag synchroon wilt verzenden, roept u in plaats daarvan de methode WdfUsbTargetDeviceSendUrbSynchronously aan .

    WDM-stuurprogramma's: Koppel de URB aan een IRP en verzend de IRP naar de USB-stuurprogrammastack. Zie Een URB verzenden voor meer informatie.

  8. Nadat de aanvraag is voltooid, controleert u de status van de aanvraag.

    Als de USB-stuurprogrammastack de aanvraag mislukt, bevat de URB-status de relevante foutcode. Enkele veelvoorkomende foutvoorwaarden worden beschreven in de sectie Opmerkingen.

Als de status van de aanvraag (IRP of het WDF-aanvraagobject) aangeeft USBD_STATUS_SUCCESS, is de aanvraag voltooid. Inspecteer de matrix van USBD_STREAM_INFORMATION structuren die zijn ontvangen na voltooiing. De matrix wordt gevuld met informatie over de aangevraagde streams. De USB-stuurprogrammastack vult elke structuur in de tabel met stroomgegevens, zoals handlers (ontvangen als USBD_PIPE_HANDLE), stream-id's en de maximale overdrachtsgrootte. Streams zijn nu geopend om gegevens over te dragen.

Voor een open streams-aanvraag moet u een URB en een matrix toewijzen. Het clientstuurprogramma moet de URB vrijgeven door de WdfObjectDelete-methode aan te roepen voor het bijbehorende WDF-geheugenobject, nadat de aanvraag voor open streams is voltooid. Als het stuurprogramma de aanvraag synchroon heeft verzonden door WdfUsbTargetDeviceSendUrbSynchronously aan te roepen, moet het het WDF-geheugenobject vrijgeven nadat de methode is geretourneerd. Als het clientstuurprogramma de aanvraag asynchroon heeft verzonden door WdfRequestSend aan te roepen, moet het stuurprogramma het WDF-geheugenobject vrijgeven in de door het stuurprogramma geïmplementeerde voltooiingsroutine die aan de aanvraag is gekoppeld.

De streamarray kan worden vrijgegeven nadat het clientstuurprogramma klaar is met het gebruik van streams of deze heeft opgeslagen voor I/O-verzoeken. In het codevoorbeeld dat in dit artikel is opgenomen, slaat het stuurprogramma de streamsmatrix op in de apparaatcontext. Het stuurprogramma geeft de apparaatcontext vrij net voordat het apparaatobject wordt verwijderd.

Gegevens overdragen naar een bepaalde stream

Als u een aanvraag voor gegevensoverdracht naar een bepaalde stream wilt verzenden, hebt u een WDF-aanvraagobject nodig. Normaal gesproken hoeft de client driver geen WDF-aanvraagobject toe te wijzen. Wanneer de I/O-manager een aanvraag van een toepassing ontvangt, maakt de I/O-manager een IRP voor de aanvraag. Dat IRP wordt onderschept door het framework. Het framework wijst vervolgens een WDF-aanvraagobject toe om de IRP weer te geven. Daarna geeft het framework het WDF-aanvraagobject door aan het clientstuurprogramma. Het clientstuurprogramma kan het aanvraagobject vervolgens koppelen aan de URB voor gegevensoverdracht en deze naar de USB-stuurprogrammastack verzenden.

Als het clientstuurprogramma geen WDF-aanvraagobject van het framework ontvangt en de aanvraag asynchroon wil verzenden, moet het stuurprogramma een WDF-aanvraagobject toewijzen door de methode WdfRequestCreate aan te roepen. Maak het nieuwe object op door WdfUsbTargetPipeFormatRequestForUrb aan te roepen en de aanvraag te verzenden door WdfRequestSend aan te roepen.

In de synchrone gevallen is het doorgeven van een WDF-aanvraagobject optioneel.

Als u gegevens wilt overdragen naar streams, moet u URL's gebruiken. De URB moet worden opgemaakt door WdfUsbTargetPipeFormatRequestForUrb aan te roepen.

De volgende WDF-methoden worden niet ondersteund voor streams:

In de volgende procedure wordt ervan uitgegaan dat het clientstuurprogramma het aanvraagobject van framework ontvangt.

  1. Wijs een URB toe door WdfUsbTargetDeviceCreateUrb aan te roepen. Met deze methode wordt een WDF-geheugenobject toegewezen dat de zojuist toegewezen URB bevat. Het clientstuurprogramma kan ervoor kiezen om een URB toe te wijzen voor elke I/O-aanvraag of om een URB toe te wijzen en opnieuw te gebruiken voor hetzelfde type aanvraag.

  2. Maak de URB op voor een bulkoverdracht door UsbBuildInterruptOrBulkTransferRequest aan te roepen. Geef in de parameter PipeHandle de ingang op voor de stroom. De streamhandles zijn verkregen in een eerder verzoek, beschreven in de sectie Hoe statische streams te openen.

  3. Maak het WDF-aanvraagobject op door de methode WdfUsbTargetPipeFormatRequestForUrb aan te roepen. Geef in de aanroep het WDF-geheugenobject op dat de URB voor gegevensoverdracht bevat. Het geheugenobject is toegewezen in stap 1.

  4. Verzend de URB als een WDF-aanvraag door WdfRequestSend of WdfUsbTargetPipeSendUrbSynchronously aan te roepen. Als u WdfRequestSend aanroept, moet u een voltooiingsroutine opgeven door WdfRequestSetCompletionRoutine aan te roepen, zodat het clientstuurprogramma een melding krijgt wanneer de asynchrone bewerking is voltooid. U moet de URB voor gegevensoverdracht ontkoppelen in de voltooiingsroutine.

WDM-stuurprogramma's: Wijs een URB toe door USBD_UrbAllocate aan te roepen en op te maken voor bulkoverdracht (zie _URB_BULK_OR_INTERRUPT_TRANSFER). Als u de URB wilt opmaken, kunt u UsbBuildInterruptOrBulkTransferRequest aanroepen of de URB-structuur handmatig opmaken. Geef de aanduiding van de stroom op in het lid UrbBulkOrInterruptTransfer.PipeHandle van de URB.

Hoe statische stromen te sluiten

Het clientstuurprogramma kan streams sluiten nadat het hiermee klaar is. De close-stream-aanvraag is echter optioneel. De USB-stuurprogramma stack sluit alle streams wanneer het eindpunt dat aan de streams is gekoppeld, wordt gedeconfigureerd. Een eindpunt is niet geconfigureerd wanneer een alternatieve configuratie of interface is geselecteerd, het apparaat wordt verwijderd, enzovoort. Een clientstuurprogramma moet streams sluiten als het stuurprogramma een ander aantal streams wil openen. Om een 'close-stream'-verzoek te verzenden:

  1. Wijs een URB-structuur toe door WdfUsbTargetDeviceCreateUrb aan te roepen.

  2. Maak de URB op voor het afsluiten van streams. Het UrbPipeRequest-lid van de URB-structuur is een _URB_PIPE_REQUEST structuur. Vul de leden als volgt in:

    • Het Hdr-lid-element van _URB_PIPE_REQUEST moet URB_FUNCTION_CLOSE_STATIC_STREAMS zijn.
    • Het lid PipeHandle moet de ingang zijn voor het eindpunt dat de geopende streams bevat die in gebruik zijn.
  3. Verzend de URB als een WDF-aanvraag door WdfRequestSend of WdfUsbTargetDeviceSendUrbSynchronously aan te roepen.

De close-handle-aanvraag sluit alle streams die eerder door het clientstuurprogramma zijn geopend. Het clientstuurprogramma kan de aanvraag niet gebruiken om specifieke streams in het eindpunt te sluiten.

Aanbevolen procedures voor het verzenden van een statische streams-aanvraag

De USB-stuurprogrammastack voert validaties uit op de ontvangen URB. Validatiefouten voorkomen:

  • Verzend geen open-stream- of close-stream-aanvraag naar een eindpunt dat geen ondersteuning biedt voor streams. Roep WdfUsbTargetDeviceQueryUsbCapability (voor WDM-stuurprogramma's USBD_QueryUsbCapability) aan om ondersteuning voor statische streams te bepalen en alleen streams-aanvragen te verzenden als het eindpunt deze ondersteunt.
  • Vraag geen aantal (te openen streams) aan dat het maximum aantal ondersteunde streams overschrijdt of een aanvraag verzendt zonder het aantal streams op te geven. Bepaal het aantal streams op basis van het aantal streams dat wordt ondersteund door de USB-stuurprogrammastack en het eindpunt van het apparaat.
  • Verzend geen open stream-aanvraag naar een eindpunt dat al geopende streams heeft.
  • Verzend geen close-stream-aanvraag naar een eindpunt dat geen geopende streams heeft.
  • Nadat statische streams zijn geopend voor een eindpunt moet u geen I/O-aanvragen verzenden met behulp van het pijphandvat van het eindpunt dat is verkregen via een select-configuratie- of select-interface-aanvraag. Dit geldt zelfs als de statische stromen zijn gesloten.

Pijpbewerkingen opnieuw instellen en onderbreken

Soms kunnen overdrachten naar of van een eindpunt mislukken. Dergelijke fouten kunnen het gevolg zijn van een foutvoorwaarde op het eindpunt of de hostcontroller, zoals een staaf- of stopvoorwaarde. Als het clientstuurprogramma de fouttoestand wil wissen, annuleert het eerst wachtende overdrachten en reset daarna de pijp waarmee het eindpunt is gekoppeld. Als u overdrachten in behandeling wilt annuleren, kan het clientstuurprogramma een abort-pipe verzoek verzenden. Om een pijp opnieuw in te stellen, moet het clientstuurprogramma een reset-pijpverzoek verzenden.

Voor streamoverdrachten worden aanvragen voor het afbreken van pijplijnen en reset-aanvragen niet ondersteund voor individuele streams gekoppeld aan het bulkeindpunt. Als een overdracht op een bepaalde stroompijp mislukt, stopt de hostcontroller overdrachten op alle andere pijpen (voor andere streams). Als u wilt herstellen van de foutvoorwaarde, moet het clientstuurprogramma handmatig overdrachten naar elke stream annuleren. Vervolgens moet het clientstuurprogramma een reset-pipe-aanvraag naar het bulkeindpunt verzenden met behulp van het pijphandvat. Voor deze aanvraag moet de client driver de pijpgreep voor het eindpunt opgeven in een _URB_PIPE_REQUEST-structuur en de URB-functie (Hdr.Function) instellen op URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL.

Volledig voorbeeld

In het volgende codevoorbeeld ziet u hoe u streams opent.

NTSTATUS
    OpenStreams (
    _In_ WDFDEVICE Device,
    _In_ WDFUSBPIPE Pipe)
{
    NTSTATUS status;
    PDEVICE_CONTEXT deviceContext;
    PPIPE_CONTEXT pipeContext;
    USHORT cStreams = 0;
    USBD_PIPE_HANDLE usbdPipeHandle;
    WDFMEMORY urbMemory = NULL;
    PURB      urb = NULL;

    PAGED_CODE();

    deviceContext =GetDeviceContext(Device);
    pipeContext = GetPipeContext (Pipe);

    if (deviceContext->MaxStreamsController == 0)
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
            "%!FUNC! Static streams are not supported.");

        status = STATUS_NOT_SUPPORTED;
        goto Exit;
    }

    // If static streams are not supported, number of streams supported is zero.

    if (pipeContext->MaxStreamsSupported == 0)
    {
        status = STATUS_DEVICE_CONFIGURATION_ERROR;

        TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
            "%!FUNC! Static streams are not supported by the endpoint.");

        goto Exit;
    }

    // Determine the number of streams to open.
    // Compare the number of streams supported by the endpoint with the
    // number of streams supported by the host controller, and choose the
    // lesser of the two values. The deviceContext->MaxStreams value was
    // obtained in a previous call to WdfUsbTargetDeviceQueryUsbCapability
    // that determined whether or not static streams is supported and
    // retrieved the maximum number of streams supported by the
    // host controller. The device context stores the values for IN and OUT
    // endpoints.

    // Allocate an array of USBD_STREAM_INFORMATION structures to store handles to streams.
    // The number of elements in the array is the number of streams to open.
    // The code snippet stores the array in its device context.

    cStreams = min(deviceContext->MaxStreamsController, pipeContext->MaxStreamsSupported);

    // Allocate an array of streams associated with the IN bulk endpoint
    // This array is released in CloseStreams.

    pipeContext->StreamInfo = (PUSBD_STREAM_INFORMATION) ExAllocatePoolWithTag (
        NonPagedPool,
        sizeof (USBD_STREAM_INFORMATION) * cStreams,
        USBCLIENT_TAG);

    if (pipeContext->StreamInfo == NULL)
    {
        status = STATUS_INSUFFICIENT_RESOURCES;

        TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
            "%!FUNC! Could not allocate stream information array.");

        goto Exit;
    }

    RtlZeroMemory (pipeContext->StreamInfo,
        sizeof (USBD_STREAM_INFORMATION) * cStreams);

    // Get USBD pipe handle from the WDF target pipe object. The client driver received the
    // endpoint pipe handles during device configuration.

    usbdPipeHandle = WdfUsbTargetPipeWdmGetPipeHandle (Pipe);

    // Allocate an URB for the open streams request.
    // WdfUsbTargetDeviceCreateUrb returns the address of the
    // newly allocated URB and the WDFMemory object that
    // contains the URB.

    status = WdfUsbTargetDeviceCreateUrb (
        deviceContext->UsbDevice,
        NULL,
        &urbMemory,
        &urb);

    if (status != STATUS_SUCCESS)
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
            "%!FUNC! Could not allocate URB for an open-streams request.");

        goto Exit;
    }

    // Format the URB for the open-streams request.
    // The UsbBuildOpenStaticStreamsRequest inline function formats the URB by specifying the
    // pipe handle to the entire bulk endpoint, number of streams to open, and the array of stream structures.

    UsbBuildOpenStaticStreamsRequest (
        urb,
        usbdPipeHandle,
        (USHORT)cStreams,
        pipeContext->StreamInfo);

    // Send the request synchronously.
    // Upon completion, the USB driver stack populates the array of with handles to streams.

    status = WdfUsbTargetPipeSendUrbSynchronously (
        Pipe,
        NULL,
        NULL,
        urb);

    if (status != STATUS_SUCCESS)
    {
        goto Exit;
    }

Exit:
    if (urbMemory)
    {
        WdfObjectDelete (urbMemory);
    }

    return status;
}